飛不算本事:系統(tǒng)穩(wěn)定依賴清晰規(guī)則與邊界設(shè)計(jì))
開(kāi)頭先不繞彎子?!?斯坦李吐槽dc 所以超人是無(wú)緣無(wú)故會(huì)飛的嘛哈哈哈哈哈哈哈錘哥真是技術(shù)人才啊#雷神 #復(fù)聯(lián)”這類調(diào)侃式短標(biāo)題第一波沖擊力在于它把兩個(gè)宇宙的角色塞進(jìn)同一個(gè)吐槽箱里但細(xì)想一下就能發(fā)現(xiàn)它真正碰到的根本不是“哪個(gè)超級(jí)英雄更強(qiáng)”而是另一個(gè)更值得技術(shù)人注意的問(wèn)題在沒(méi)有明確力場(chǎng)說(shuō)明、沒(méi)有能量來(lái)源標(biāo)注、沒(méi)有守恒邊界的前提下一個(gè)能力設(shè)定憑什么能讓觀眾接受這個(gè)問(wèn)題的內(nèi)核和我們?cè)谧鰯?shù)據(jù)處理、自動(dòng)化和工具鏈建設(shè)時(shí)遇到的問(wèn)題是同一類一個(gè)系統(tǒng)能不能被信任不在于它聲稱自己有多強(qiáng)而在于它的輸入、規(guī)則、邊界和異常處理是否清晰。超人是“無(wú)緣無(wú)故會(huì)飛”還是劇本通過(guò)世界觀默認(rèn)值把“會(huì)飛”這個(gè)能力合法化了雷神揮錘子為什么看起來(lái)很有說(shuō)服力因?yàn)槁辽俳o了“阿斯加德科技/魔法混合”這個(gè)模糊但一致的默認(rèn)框架。放到工程場(chǎng)景里就是你可以接受一個(gè)組件閉源、接受它有隱含規(guī)則、接受它不做完整解釋但前提是它的行為必須穩(wěn)定、邊界必須可預(yù)期。這篇文章不討論電影宇宙戰(zhàn)力排名。我想借這個(gè)梗把隱藏在“角色能力憑什么成立”背后的那套工程思維拆出來(lái)用來(lái)聊一個(gè)更實(shí)際的話題為什么單次跑通不算完規(guī)則一致、邊界清晰、異常可查、結(jié)果可復(fù)用才是軟件系統(tǒng)能長(zhǎng)期運(yùn)行的關(guān)鍵。1. 先搞清楚“為什么設(shè)定成立”和“為什么代碼能跑”是同一個(gè)問(wèn)題看電影時(shí)觀眾很少會(huì)問(wèn)“超人的飛行原理是什么”。因?yàn)橛捌ㄟ^(guò)早期鏡頭、旁白、角色行為不斷重復(fù)一個(gè)默認(rèn)設(shè)定氪星人在地球黃色太陽(yáng)下就有多種超能力飛行是其中一種。這個(gè)設(shè)定不需要解釋成因只需要保持穩(wěn)定。一旦超人某天突然飛不起來(lái)且影片沒(méi)有給出氪石或能量衰減等前提觀眾就會(huì)覺(jué)得“人設(shè)崩了”。軟件系統(tǒng)也一樣。一段數(shù)據(jù)處理流程能跑通很多時(shí)候不是因?yàn)椤八协h(huán)節(jié)都完全可解釋”而是因?yàn)槊總€(gè)環(huán)節(jié)的隱含前提都恰好被滿足。比如某個(gè)腳本能正常解析文件可能依賴文件名編碼、目錄權(quán)限、依賴庫(kù)版本、輸入字段順序這些默認(rèn)值。這就是第一個(gè)關(guān)鍵判斷系統(tǒng)是否可信取決于默認(rèn)規(guī)則是否一致而不取決于每個(gè)細(xì)節(jié)是否都被解釋清楚。1.1 從虛構(gòu)世界觀到工程系統(tǒng)的三個(gè)共性要求規(guī)則一致超人今天能飛明天在同樣條件下也應(yīng)該能飛代碼今天能解析這個(gè)格式明天遇到同構(gòu)數(shù)據(jù)也應(yīng)當(dāng)能解析。邊界可預(yù)期觀眾知道超人怕氪石開(kāi)發(fā)者知道某個(gè)函數(shù)在遇到空值時(shí)可能報(bào)錯(cuò)這就是邊界。異常要能被歸因角色行為反常時(shí)觀眾能通過(guò)劇情線索找到原因程序報(bào)錯(cuò)時(shí)工程師可以通過(guò)日志和堆棧找到是哪一層出了問(wèn)題。這三條不是漫威和 DC 的編劇專利而是任何想進(jìn)入生產(chǎn)環(huán)境的算法、腳本和批處理任務(wù)都必須滿足的基本條件。如果只追求“單次結(jié)果看起來(lái)沒(méi)問(wèn)題”那就像只看到一個(gè)電影片段里超人飛過(guò)了大樓卻沒(méi)看到他在同一部電影后段遇到氪石后的表現(xiàn)。1.2 單點(diǎn)能力成立不等于整體系統(tǒng)成立很多初學(xué)者拿到一份數(shù)據(jù)轉(zhuǎn)換腳本跑通了就開(kāi)始批量處理幾百個(gè)文件。這種勇氣和漫威決定讓雷神在《復(fù)仇者聯(lián)盟》里直接接入地球科技線差不多——單角色能力看起來(lái)成立不等于角色一進(jìn)入更復(fù)雜的協(xié)作環(huán)境仍然成立。批量場(chǎng)景里會(huì)發(fā)生什么第 3 個(gè)文件編碼不同腳本中斷。第 17 個(gè)文件結(jié)構(gòu)里多了一個(gè)字段轉(zhuǎn)換邏輯錯(cuò)位。某個(gè)目錄沒(méi)有寫(xiě)權(quán)限腳本在凌晨跑批時(shí)靜默失敗。依賴庫(kù)被升級(jí)原本好用的解析函數(shù)換了默認(rèn)參數(shù)。你發(fā)現(xiàn)沒(méi)有這些問(wèn)題和“超人為啥會(huì)飛”本質(zhì)一樣在一個(gè)沒(méi)有說(shuō)明、沒(méi)有檢查、沒(méi)有兜底的默認(rèn)規(guī)則下任何能力都可能突然失效。區(qū)別只是電影里的能力失效可以寫(xiě)成劇情沖突系統(tǒng)里的能力失效直接變成線上事故。2. 為什么說(shuō)雷神的“錘子規(guī)則”其實(shí)很像一套技術(shù)規(guī)范雷神的錘子是一個(gè)特別有意思的設(shè)定。它的能力邏輯不是“無(wú)條件強(qiáng)大”而是帶有一組明確的判定規(guī)則夠不夠格決定了能不能拿起它。雖然這套規(guī)則來(lái)自魔法/奧丁咒語(yǔ)之類的不透明機(jī)制但它的表現(xiàn)是可預(yù)測(cè)的。觀眾看到美隊(duì)、黑寡婦等人嘗試時(shí)都會(huì)有明確預(yù)期。這種“強(qiáng)規(guī)則、可觀測(cè)、有邊界”的設(shè)定方式正好對(duì)應(yīng)工程上的接口約定和配置規(guī)范。2.1 明確規(guī)則比能力大小更重要在設(shè)計(jì)一個(gè)數(shù)據(jù)同步任務(wù)時(shí)有兩個(gè)方向方向 A寫(xiě)一個(gè)看起來(lái)“非常智能”的同步函數(shù)能自動(dòng)猜文件格式、自動(dòng)匹配字段、自動(dòng)重試但失敗原因不對(duì)外暴露規(guī)則內(nèi)嵌在復(fù)雜邏輯里。方向 B寫(xiě)一個(gè)看起來(lái)“很笨”但規(guī)則清晰的同步任務(wù)明確定義輸入格式、編碼、必填字段、可選字段、沖突策略不符合輸入直接報(bào)錯(cuò)并輸出可讀原因。短期看A 的使用體驗(yàn)好像更好因?yàn)樗∈隆iL(zhǎng)期看B 才能真正進(jìn)入生產(chǎn)環(huán)境。為什么因?yàn)?A 相當(dāng)于一個(gè)沒(méi)有規(guī)則的超級(jí)英雄。它今天的“智能表現(xiàn)”依賴內(nèi)部一堆不可見(jiàn)條件明天換一個(gè)環(huán)境就可能產(chǎn)生不同行為而你根本沒(méi)有辦法判斷該信任它還是防備它。B 則相反它像雷神的錘子規(guī)則一樣把限制寫(xiě)在明面上規(guī)則之內(nèi)我穩(wěn)定執(zhí)行規(guī)則之外我會(huì)拒絕執(zhí)行并且告訴你哪里不符合。2.2 從“無(wú)理由會(huì)飛”到“必須給出空值策略”回到數(shù)據(jù)清洗場(chǎng)景最常見(jiàn)的問(wèn)題不是“能不能清洗”而是“遇到空值時(shí)怎么處理”。很多新手腳本默認(rèn)跳過(guò)空值結(jié)果輸出行數(shù)變少或者用 0 填充結(jié)果統(tǒng)計(jì)口徑全偏。這就像超人無(wú)緣無(wú)故會(huì)飛一樣代碼“無(wú)緣無(wú)故”替用戶做了決定。真正的工程做法是把空值策略變成顯式參數(shù)要么丟棄并記錄要么填充并標(biāo)記要么中斷并等待人工確認(rèn)。# 偽代碼顯式空值策略 if value is None: if null_policy skip: continue elif null_policy fill: value default_value elif null_policy raise: raise ValueError(f字段 {field} 為空且策略設(shè)置為中斷)這個(gè)例子看起來(lái)非常簡(jiǎn)單但它是從“會(huì)飛就行”到“飛行受控”的分水嶺。一個(gè)系統(tǒng)最危險(xiǎn)的部分從來(lái)不是它不會(huì)做的事而是它會(huì)在你沒(méi)預(yù)期到的條件下替你做了決定。2.3 邊界條件才是判斷技術(shù)方案的分水嶺如果一個(gè)方案的演示樣本全是 A 級(jí)內(nèi)容干凈的中文文本、規(guī)范的 JSON 結(jié)構(gòu)、完整的字段、合理的長(zhǎng)度。你很難判斷它到底行不行。只有當(dāng)你把亂碼、缺失字段、超長(zhǎng)文本、重復(fù)請(qǐng)求、并發(fā)任務(wù)丟進(jìn)去才能看出方案的真實(shí)水平。這個(gè)道理和評(píng)價(jià)一個(gè)角色設(shè)定是否成功是相通的。你看《雷神》時(shí)錘子能不能被拿起來(lái)這件事會(huì)反復(fù)在各種場(chǎng)景里被測(cè)試這正是因?yàn)樗幸粭l可觀測(cè)的邊界規(guī)則。技術(shù)方案也需要通過(guò)測(cè)試來(lái)探明邊界。至少要測(cè)這五類輸入異常文件為空、字段缺失、字段類型錯(cuò)位。數(shù)據(jù)規(guī)模變化單條能過(guò)十萬(wàn)條、百萬(wàn)條是否還能穩(wěn)定執(zhí)行。編碼與格式差異UTF-8、GBK、UTF-8-BOM換行符差異。運(yùn)行環(huán)境變化本地能跑服務(wù)器上能否跑Windows 能跑Linux 上能否跑。冪等性同一個(gè)任務(wù)重復(fù)執(zhí)行多次結(jié)果是否一致。前兩類是功能測(cè)試后三類是邊界和穩(wěn)定性測(cè)試。很多方案死在第三類以后。比如一個(gè)腳本在本地處理文件名時(shí)靠中文路徑?jīng)]問(wèn)題到了 Linux 服務(wù)器上因?yàn)榫幋a不一致直接無(wú)法導(dǎo)入這類問(wèn)題最隱蔽。3. 從“單次跑通”到“穩(wěn)定運(yùn)行”還差哪幾塊拼圖如果要給出一份從單次工具使用到長(zhǎng)期穩(wěn)定運(yùn)行的成熟度清單我會(huì)把它分成四個(gè)階段對(duì)應(yīng)不同工程師水平。3.1 階段一先跑通最小可用路徑這個(gè)階段不要貪心。目標(biāo)只有一個(gè)讓一條數(shù)據(jù)樣本從輸入到輸出完整走通。具體操作順序準(zhǔn)備 1 到 3 條有代表性的小樣本而不是一上來(lái)就用全量數(shù)據(jù)。先不做格式轉(zhuǎn)換不寫(xiě)復(fù)雜參數(shù)只確認(rèn)最核心流程能通。明確輸入輸出路徑把數(shù)據(jù)目錄和結(jié)果目錄分開(kāi)。記錄當(dāng)前環(huán)境的依賴版本和關(guān)鍵參數(shù)方便回溯。這個(gè)階段最容易被忽略的是環(huán)境記錄。很多人跑通了就開(kāi)心卻沒(méi)有記錄當(dāng)前用的是什么 Python 版本、什么依賴庫(kù)、什么參數(shù)組合。等到第二天換臺(tái)電腦或換個(gè)人接手重新復(fù)現(xiàn)就變成一場(chǎng)噩夢(mèng)。建議從一開(kāi)始就用 requirements.txt 或等價(jià)方式鎖定依賴至少把運(yùn)行環(huán)境、依賴版本、輸入樣例三條信息記錄下來(lái)。3.2 階段二給流程建立顯式邊界跑通之后不要馬上批量。先回答幾個(gè)問(wèn)題這個(gè)任務(wù)的合法輸入是什么哪些字段必填哪些字段可選遇到非法輸入時(shí)應(yīng)該中斷還是跳過(guò)中斷信息是否可讀輸出目錄的目錄沖突怎么處理覆蓋、新建時(shí)間戳目錄還是報(bào)錯(cuò)單條任務(wù)失敗后會(huì)不會(huì)影響后續(xù)任務(wù)整個(gè)任務(wù)是否支持重復(fù)執(zhí)行而不產(chǎn)生重復(fù)輸出這些問(wèn)題看上去瑣碎但每一個(gè)都直接決定流程能不能從“手工可用”變成“腳本可復(fù)用”。用一句話總結(jié)這一階段的目標(biāo)把隱式默認(rèn)值變成顯式參數(shù)把靜默處理變成可觀測(cè)處理。3.3 階段三批量化與狀態(tài)追蹤批量任務(wù)最大的問(wèn)題不是單個(gè)任務(wù)失敗而是失敗后你無(wú)法快速定位到底哪一批數(shù)據(jù)出了問(wèn)題。成熟做法任務(wù)編號(hào)給每條數(shù)據(jù)或每個(gè)子任務(wù)分配唯一標(biāo)識(shí)日志里可以按標(biāo)識(shí)檢索。三步式日志開(kāi)始處理前記錄“將處理什么”處理中記錄“當(dāng)前進(jìn)度”處理結(jié)束記錄“處理結(jié)果”。失敗不中斷批量時(shí)默認(rèn)不要讓單個(gè)失敗中斷整個(gè)任務(wù)把失敗信息收集起來(lái)最后統(tǒng)一輸出失敗清單。# 偽代碼批量任務(wù)失敗收集 failed [] for record in batch: try: process(record) except Exception as e: failed.append({record_id: record.id, error: str(e)}) # 全部完成后統(tǒng)一輸出失敗報(bào)告很多新手會(huì)寫(xiě)成一個(gè)失敗就 break 的結(jié)構(gòu)然后整個(gè)任務(wù)白跑。批量任務(wù)必須默認(rèn)“同類繼續(xù)失敗匯總”。3.4 階段四可觀測(cè)性與長(zhǎng)期維護(hù)進(jìn)入長(zhǎng)期使用階段后最重要的不是流程本身而是你能多快定位一次失敗。需要考慮日志里是否有足夠的上下文比如輸入文件、處理時(shí)間、參數(shù)版本、輸出數(shù)量。是否有結(jié)果校驗(yàn)比如“輸入 10000 條輸出 9500 條丟棄 500 條”這種數(shù)字報(bào)告。是否有失敗重試機(jī)制重試時(shí)會(huì)不會(huì)產(chǎn)生重復(fù)數(shù)據(jù)。依賴升級(jí)時(shí)是否能在測(cè)試環(huán)境跑通后再更新到生產(chǎn)。這已經(jīng)不是在寫(xiě)腳本而是在做一個(gè)小型的數(shù)據(jù)工程系統(tǒng)。到這一步你需要的技術(shù)能力不再只是“會(huì)調(diào)用某個(gè)函數(shù)”而是會(huì)設(shè)計(jì)輸入校驗(yàn)、狀態(tài)管理、日志規(guī)范、異常隔離和結(jié)果校驗(yàn)。4. 很多人誤解了“自動(dòng)化”它不替代判斷它固化判斷回到開(kāi)頭那個(gè)調(diào)侃。如果只看梗本身你可能會(huì)覺(jué)得超人會(huì)飛這件事是編劇偷懶是無(wú)理由設(shè)定。但如果我們把漫威宇宙中雷神的能力展現(xiàn)過(guò)程展開(kāi)會(huì)發(fā)現(xiàn)編劇做了大量“判斷前置”工作什么情況下雷神有力量、什么情況下沒(méi)有力量、武器認(rèn)主的規(guī)則是什么。這些判斷一旦在故事早期被定義好后面所有情節(jié)就不需要重復(fù)解釋。自動(dòng)化方案也是同樣道理。4.1 自動(dòng)化的價(jià)值不是省掉人的思考而是把人的經(jīng)驗(yàn)變成規(guī)則我見(jiàn)過(guò)很多人在宣傳某個(gè)自動(dòng)化方案時(shí)說(shuō)用了它你就不需要人工干預(yù)了。這是錯(cuò)誤的理解。成熟自動(dòng)化方案真正省掉的不是“決策”而是“重復(fù)執(zhí)行同一決策”的時(shí)間。舉例來(lái)說(shuō)一個(gè)文本處理任務(wù)需要決定“遇到超長(zhǎng)文本是截?cái)噙€是跳過(guò)還是分段處理”。這個(gè)決策本身需要人來(lái)做??梢坏┒ㄏ聛?lái)后續(xù)每個(gè)文件都不需要再思考這個(gè)問(wèn)題因?yàn)榱鞒桃呀?jīng)把它固化成規(guī)則。這就像編劇前期確定了“雷神之錘有認(rèn)主規(guī)則”后面所有角色拿起錘子的鏡頭都不用向觀眾重新解釋一遍設(shè)定。自動(dòng)化的本質(zhì)一直是把明確判斷固化成默認(rèn)規(guī)則把規(guī)則外的異常留給人工。4.2 規(guī)則固化越多規(guī)則外部要留的逃生門(mén)也越多但這會(huì)帶來(lái)一個(gè)反直覺(jué)問(wèn)題規(guī)則確定得越多系統(tǒng)越穩(wěn)定但一旦出現(xiàn)規(guī)則沒(méi)覆蓋到的情況系統(tǒng)出錯(cuò)的代價(jià)也越大。所以我在設(shè)計(jì)任何自動(dòng)化流程時(shí)都會(huì)做一個(gè)“逃生門(mén)檢查”有沒(méi)有一個(gè)開(kāi)關(guān)可以讓人介入有沒(méi)有一個(gè)通道可以在規(guī)則外手動(dòng)跑單條有沒(méi)有清晰的二次確認(rèn)流程來(lái)處理低置信度結(jié)果有沒(méi)有辦法在某個(gè)環(huán)節(jié)掛掉時(shí)回滾到上一步如果一套自動(dòng)化流程沒(méi)有任何逃生門(mén)它就像一列停不下來(lái)的火車。前期決策再正確遇到軌道前方異常時(shí)仍然可能翻車。4.3 好的工具鏈?zhǔn)悄茏層脩衾斫狻斑吔缭谀睦铩钡倪@個(gè)標(biāo)準(zhǔn)可以拿來(lái)檢驗(yàn)市面上的很多“智能工具”它是否能讓你知道什么時(shí)候該信任它、什么時(shí)候該懷疑它、什么時(shí)候應(yīng)該停下來(lái)人工檢查如果一個(gè)工具包給你一堆參數(shù)卻不告訴你哪些參數(shù)會(huì)在什么條件下影響輸出那它更像一個(gè)“無(wú)緣無(wú)故會(huì)飛”的工具。今天飛得起來(lái)你很高興明天同樣的輸入飛不起來(lái)了你根本不知道問(wèn)題出在哪。而好的工具通常會(huì)在一開(kāi)始就告訴你這個(gè)函數(shù)只接受什么格式的輸入。超出輸入范圍時(shí)會(huì)發(fā)生什么。哪些字段會(huì)顯著影響結(jié)果哪些字段只是輔助。結(jié)果質(zhì)量如何評(píng)估。失敗時(shí)怎么獲取更多錯(cuò)誤上下文。這種工具并不一定是最高級(jí)的但它是唯一讓人敢在真實(shí)業(yè)務(wù)中長(zhǎng)期依賴的工具。5. 一個(gè)能直接照搬的排查鏈路前面講了很多設(shè)計(jì)和思維層面的問(wèn)題。這塊給一份可以直接照用的排查鏈路當(dāng)你遇到“腳本或工具在自己電腦上能用換個(gè)環(huán)境或換個(gè)數(shù)據(jù)就出問(wèn)題”時(shí)按順序逐層排查。5.1 第一層先看現(xiàn)象和輸入不要一上來(lái)就翻源碼、改參數(shù)。先回答幾個(gè)事實(shí)類問(wèn)題是報(bào)錯(cuò)中斷還是靜默輸出錯(cuò)誤結(jié)果報(bào)錯(cuò)出現(xiàn)在整個(gè)流程的第幾步輸入文件的編碼、格式、字段結(jié)構(gòu)是否和上次一樣輸入文件路徑是否包含中文、空格或特殊字符數(shù)據(jù)量級(jí)是不是和上次完全不在同一水平很多問(wèn)題在查完這一層后就解決了。最常見(jiàn)的是編碼問(wèn)題文件本身是 GBK 編碼但腳本默認(rèn)用 UTF-8 解析導(dǎo)致讀取階段就出錯(cuò)。5.2 第二層復(fù)現(xiàn)并檢查環(huán)境差異把同樣的代碼放在報(bào)錯(cuò)環(huán)境里跑一次確認(rèn)是穩(wěn)定復(fù)現(xiàn)還是偶發(fā)問(wèn)題。檢查項(xiàng)包括依賴庫(kù)版本和第一次跑通時(shí)是否一致。Python 或其他運(yùn)行時(shí)的版本。操作系統(tǒng)差異尤其是路徑分隔符和編碼差異。系統(tǒng)權(quán)限目標(biāo)目錄是否可寫(xiě)臨時(shí)目錄是否可訪問(wèn)。環(huán)境變量比如語(yǔ)言設(shè)置、默認(rèn)編碼、臨時(shí)目錄位置。如果問(wèn)題是偶發(fā)的更多要考慮資源競(jìng)爭(zhēng)、并發(fā)沖突或網(wǎng)絡(luò)超時(shí)。比如某個(gè)文件被其他進(jìn)程占用或者并發(fā)任務(wù)太多導(dǎo)致內(nèi)存不足。5.3 第三層檢查參數(shù)和配置環(huán)境沒(méi)問(wèn)題就要開(kāi)始檢查參數(shù)。重點(diǎn)看默認(rèn)參數(shù)是否被隱式改變。輸出目錄是否被軟鏈或權(quán)限設(shè)置影響。模型或算法相關(guān)參數(shù)是否因?yàn)榘姹静煌a(chǎn)生不同默認(rèn)值。超時(shí)設(shè)置是否對(duì)當(dāng)前數(shù)據(jù)量過(guò)小。這里建議把關(guān)鍵參數(shù)通過(guò)配置文件顯式傳參而不是依賴代碼內(nèi)的默認(rèn)值。因?yàn)槟愀居洸蛔∩弦淮斡玫哪J(rèn)值是哪個(gè)版本的默認(rèn)值。5.4 第四層檢查工具本身的能力邊界如果前三層都沒(méi)問(wèn)題就要接受一個(gè)現(xiàn)實(shí)工具不保證處理所有輸入。查找工具文檔里是否聲明了輸入限制或已知問(wèn)題。用最簡(jiǎn)樣例測(cè)試該工具在當(dāng)前版本下是否正常。把失敗輸入切到最小單元看問(wèn)題是否仍然存在??紤]替換方案不用死磕一個(gè)不合適當(dāng)前場(chǎng)景的功能。注意不要在一個(gè)邊界之外的功能上試圖通過(guò)反復(fù)改寫(xiě)來(lái)獲得穩(wěn)定結(jié)果。工具能力不夠和參數(shù)沒(méi)調(diào)好是兩碼事。前者用參數(shù)繞不過(guò)去后者才值得繼續(xù)調(diào)。5.5 第五層沉淀為一條可復(fù)用經(jīng)驗(yàn)找到根因后別急著歡呼。把這次排查過(guò)程沉淀成一份簡(jiǎn)短記錄至少包括問(wèn)題現(xiàn)象。根因。解決動(dòng)作。以后如何能更早發(fā)現(xiàn)。是否需要更新檢查清單。排查一次不算完能防止同類錯(cuò)誤再次發(fā)生才叫閉環(huán)。6. 判斷一個(gè)方案靠不靠譜別只看演示做工程的人經(jīng)常會(huì)收到各種推薦某個(gè)工具很好用、某個(gè)腳本能一鍵處理所有格式、某個(gè)模型能自動(dòng)識(shí)別幾十種文檔。這時(shí)候最需要保持冷靜。我的判斷方法很簡(jiǎn)單用一套五問(wèn)清單它的輸入格式是否明確如果演示時(shí)什么都吃但沒(méi)說(shuō)明哪些格式只是“碰巧能解析”風(fēng)險(xiǎn)就會(huì)后移。它的輸出是否存在校驗(yàn)它檢查的不只是“有輸出”而是“輸出是否正確、是否與預(yù)期一致”。它對(duì)異常的處理是靜默還是顯式靜默跳過(guò)風(fēng)險(xiǎn)最大因?yàn)樗赡茏屇沐e(cuò)過(guò)關(guān)鍵異常。它是否支持重復(fù)執(zhí)行重復(fù)跑會(huì)不會(huì)生成重復(fù)結(jié)果會(huì)不會(huì)覆蓋原文件可不可以冪等重試它的失敗是否能定位失敗了能不能告訴你具體是哪條、哪個(gè)字段、哪個(gè)環(huán)節(jié)、為什么失敗。用這五問(wèn)去套大部分自動(dòng)化工具基本能判斷這個(gè)東西是適合嘗鮮還是適合進(jìn)入你的生產(chǎn)流程。如果五問(wèn)全過(guò)哪怕它功能保守一些也可以放心用。如果五問(wèn)里過(guò)了不到兩問(wèn)即便演示效果驚艷也不要直接拿去做核心業(yè)務(wù)。6.1 從角色能力到工程能力本質(zhì)都是“規(guī)則質(zhì)量”聊回最開(kāi)始的問(wèn)題。超人會(huì)飛不是“無(wú)緣無(wú)故”而是編劇選擇省略解釋但這個(gè)省略要想成立世界里其他部分的規(guī)則必須保持一致。雷神的能力體系看起來(lái)更可信不是因?yàn)椤袄咨瘛边@個(gè)名字自帶邏輯而是漫威在電影里反復(fù)展示了同一套規(guī)則在不同條件下的表現(xiàn)。工程系統(tǒng)也一樣。你不會(huì)要求一個(gè)函數(shù)把所有邏輯都注明原因但你一定希望它的行為穩(wěn)定可預(yù)期。一個(gè)工具真正讓人放心的時(shí)刻不是它演示出多強(qiáng)的能力時(shí)而是它清楚告訴你邊界在哪里時(shí)。哪類工具更適合入門(mén)小規(guī)模驗(yàn)證、一次性數(shù)據(jù)整理、原型探索優(yōu)先追求快速跑通不用太在意代碼工程化。哪類工具適合長(zhǎng)期批量明確輸入輸出、有日志、有異常處理、結(jié)果可校驗(yàn)、可重復(fù)執(zhí)行的工具哪怕?tīng)奚恍爸悄芑币仓档迷谏a(chǎn)環(huán)境里用。哪類場(chǎng)景不適合用自動(dòng)工具涉及大量人工判斷、規(guī)則尚未明確、結(jié)果無(wú)法低成本驗(yàn)證的場(chǎng)景強(qiáng)行自動(dòng)化只會(huì)把錯(cuò)誤放大。7. 收尾把“有規(guī)則地飛”作為工程底線如果你從這個(gè)梗里只記住一句話我希望是這句“會(huì)飛”不是本事“有規(guī)則地飛”才是。這里的規(guī)則不是指死板的流程而是指你知道它為什么飛、什么時(shí)候飛不了、飛不了時(shí)如何發(fā)現(xiàn)、如何回到穩(wěn)定狀態(tài)。軟件工程里大量的麻煩不是來(lái)自“方案不夠聰明”而是來(lái)自“聰明得沒(méi)有規(guī)則”。下次再看到一個(gè)工具說(shuō)可以自動(dòng)處理復(fù)雜任務(wù)先別急著把全量數(shù)據(jù)丟進(jìn)去。先問(wèn)自己它的規(guī)則是什么邊界是什么異常時(shí)會(huì)不會(huì)告訴我原因我能不能信任它重復(fù)執(zhí)行一萬(wàn)次的結(jié)果先跑通再優(yōu)化最后工程化。這是幾乎任何數(shù)據(jù)處理流程都要走的路。它不快但它能保證你在第一次出現(xiàn)意外情況時(shí)知道該去哪一層排查而不是對(duì)著一個(gè)黑盒干著急。超人和雷神的設(shè)定差異恰好映射了兩種系統(tǒng)設(shè)計(jì)哲學(xué)一種把規(guī)則藏在默認(rèn)值里另一種把規(guī)則寫(xiě)在明處?,F(xiàn)實(shí)中前者適合做爽片后者適合做工程。如果你正在維護(hù)一個(gè)長(zhǎng)期任務(wù)希望你的系統(tǒng)更像后者。