![]()
一個(gè)后端服務(wù),從Rust切到TypeScript,再切回Rust——三次重寫,47%的性能波動(dòng),團(tuán)隊(duì)加班周期從2周變成6周。這不是技術(shù)選型失誤,是決策鏈條上每個(gè)環(huán)節(jié)都在埋雷。
第一次:Rust的"安全幻覺"讓我們多寫了4萬行代碼
2022年Q3,團(tuán)隊(duì)決定用Rust重寫Go寫的支付網(wǎng)關(guān)。動(dòng)機(jī)很直接:上一季度出了兩次內(nèi)存泄漏,Rust的所有權(quán)模型(ownership model)能徹底根治這類問題。
三個(gè)月后,代碼量從1.2萬行膨脹到5.8萬行。不是業(yè)務(wù)變復(fù)雜了,是Rust的編譯器在逼你顯式處理每一個(gè)邊界情況。一個(gè)HTTP請(qǐng)求的參數(shù)解析,Go里三行,Rust里要定義結(jié)構(gòu)體、實(shí)現(xiàn)反序列化 trait、處理Option嵌套、寫單元測(cè)試覆蓋所有Err分支。
技術(shù)負(fù)責(zé)人李默在復(fù)盤會(huì)上說:「我們以為買的是安全,結(jié)果付的是開發(fā)效率。線上bug確實(shí)歸零了,但新功能上線周期從兩周拉到六周。」
更隱蔽的成本在人員端。團(tuán)隊(duì)6個(gè)人里只有2個(gè)有Rust生產(chǎn)經(jīng)驗(yàn),另外4個(gè)邊寫邊學(xué)。一個(gè)中級(jí)工程師寫出的代碼, senior要review兩輪才能過——不是邏輯問題,是生命周期標(biāo)注(lifetime annotation)的寫法不符合慣用模式。
編譯通過只是開始,代碼評(píng)審才是Rust項(xiàng)目的真實(shí)耗時(shí)黑洞。
2023年1月,CTO拍板:切到TypeScript,用Node.js跑。理由是「團(tuán)隊(duì)人效優(yōu)先,安全可以用測(cè)試補(bǔ)」。
第二次:TypeScript的"類型舒適區(qū)"埋了運(yùn)行時(shí)的雷
遷移比預(yù)期快。2個(gè)月完成核心模塊,代碼量壓回1.8萬行——TypeScript的any類型(any type)在遷移期幫了大忙,先把東西跑起來,類型慢慢補(bǔ)。
問題在半年后暴露。一個(gè)支付回調(diào)接口,類型定義里聲明了amount是number,但上游渠道突然傳了字符串"100.00"。TypeScript編譯器沒報(bào)錯(cuò),運(yùn)行時(shí)直接做了隱式類型轉(zhuǎn)換,結(jié)果0.1+0.2的浮點(diǎn)精度問題被放大,對(duì)賬差了三毛錢。
三毛錢找了一周。日志里看不出問題,因?yàn)閏onsole.log打印出來是100,進(jìn)數(shù)據(jù)庫也是100,只有和渠道方原始數(shù)據(jù)逐筆比對(duì)才發(fā)現(xiàn)類型漂移。
李默的團(tuán)隊(duì)開始瘋狂加防御代碼:每個(gè)外部輸入點(diǎn)手動(dòng)校驗(yàn),joi/schema-validation 庫全量接入。代碼量又漲回4.2萬行,但這次的冗余不是Rust式的顯式處理,是補(bǔ)丁摞補(bǔ)丁的債務(wù)累積。
TypeScript的類型系統(tǒng)在編譯時(shí)給你安全感,運(yùn)行時(shí)該崩還是崩——這層窗戶紙,很多團(tuán)隊(duì)要踩過坑才捅得破。
性能數(shù)據(jù)也難看。同樣的壓測(cè)場(chǎng)景,Rust版P99延遲12ms,TypeScript版飆到67ms。不是V8引擎(V8 engine)慢,是單線程事件循環(huán)(event loop)里混進(jìn)了CPU密集型任務(wù),一個(gè)JSON大對(duì)象的序列化就把后續(xù)請(qǐng)求全堵住。
團(tuán)隊(duì)試過worker_threads,試過把重計(jì)算拆微服務(wù),架構(gòu)復(fù)雜度指數(shù)級(jí)上升。李默的原話:「Node.js適合IO密集型,我們這是計(jì)算密集型場(chǎng)景,硬套就是削足適履。」
第三次:折中方案背后的真實(shí)決策邏輯
2024年初,團(tuán)隊(duì)做了第三次遷移——不是回Rust,是拆服務(wù)。
核心支付鏈路用Rust寫,保證延遲和穩(wěn)定性;運(yùn)營(yíng)后臺(tái)、報(bào)表導(dǎo)出用TypeScript,追求迭代速度。中間用gRPC(gRPC遠(yuǎn)程過程調(diào)用協(xié)議)通信, schema用Protobuf(Protocol Buffers接口描述語言)鎖定,兩邊各自生成代碼。
這個(gè)架構(gòu)的維護(hù)成本不低。一個(gè)字段變更要改三處:Protobuf定義、Rust端、TypeScript端。但李默算過賬:核心鏈路變更頻率是每月1-2次,運(yùn)營(yíng)需求是每周3-5次,拆開后各自的人效都回來了。
更意外的收獲在團(tuán)隊(duì)端。Rust小組縮到2人,專注底層;TypeScript小組4人,快速響應(yīng)業(yè)務(wù)。招聘難度直線下降——市場(chǎng)上Rust工程師難找,但愿意寫Rust的往往對(duì)底層有興趣,自我驅(qū)動(dòng)力強(qiáng);TypeScript崗則簡(jiǎn)歷充足。
技術(shù)選型不是選最好的,是選能匹配團(tuán)隊(duì)結(jié)構(gòu)、業(yè)務(wù)節(jié)奏和招聘市場(chǎng)的。
三次重寫的直接成本:14人月,約合當(dāng)時(shí)團(tuán)隊(duì)4個(gè)月的全部產(chǎn)能。間接成本更難量化——支付網(wǎng)關(guān)的穩(wěn)定性口碑在第二次遷移期間下滑,有兩個(gè)大客戶把結(jié)算周期從T+1改成T+3,現(xiàn)金流壓力傳導(dǎo)到整個(gè)公司。
李默現(xiàn)在有個(gè)習(xí)慣:每次技術(shù)評(píng)審會(huì),先問「這個(gè)決策6個(gè)月后能無損回滾嗎?」
他的理由是:Rust和TypeScript都是好工具,但工具的價(jià)值取決于使用場(chǎng)景和團(tuán)隊(duì)狀態(tài)。第一次選Rust,錯(cuò)在高估了團(tuán)隊(duì)的學(xué)習(xí)帶寬;第二次選TypeScript,錯(cuò)在低估了運(yùn)行時(shí)的防御成本;第三次才算把決策框架搭對(duì)——先畫業(yè)務(wù)邊界,再匹配技術(shù)棧,最后看組織能不能跟上。
這個(gè)案例在GitHub上有完整的技術(shù)文檔和壓測(cè)數(shù)據(jù),作者把三次迭代的代碼結(jié)構(gòu)、性能曲線、故障記錄全開源了。評(píng)論區(qū)最高贊的反饋是:「看完最大的收獲不是Rust或TypeScript該選誰,是意識(shí)到我們之前的技術(shù)選型會(huì)根本沒人寫決策依據(jù)。」
你的團(tuán)隊(duì)上一次技術(shù)遷移,留下的文檔里,有寫清楚「當(dāng)時(shí)為什么選這個(gè)」嗎?
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.