碼上進(jìn)化
晨會(huì)的氣氛在**提到“資源傾斜”西個(gè)字時(shí)徹底凝固了。
會(huì)議室的白板上還殘留著上周某個(gè)架構(gòu)討論會(huì)的痕跡——幾行關(guān)于“微服務(wù)拆分”的草圖,旁邊用紅筆寫(xiě)著巨大的“待定”。
現(xiàn)在這行字看起來(lái)像某種諷刺。
“公司的戰(zhàn)略很明確,”**的聲音在安靜的房間里顯得格外清晰,“未來(lái)三年的重心是AI中臺(tái)和智能產(chǎn)品。
傳統(tǒng)業(yè)務(wù)系統(tǒng)的維護(hù)成本需要控制在最低限度?!?br>
坐在周哲旁邊的華工男生——王濤,輕聲嘖了一下,但沒(méi)說(shuō)話。
“所以,”**繼續(xù),“訂單系統(tǒng)的重構(gòu)不會(huì)取消,但優(yōu)先級(jí)調(diào)整。
原計(jì)劃投入的五個(gè)研發(fā)人力,現(xiàn)在減為兩個(gè)。
時(shí)間線從三個(gè)月延長(zhǎng)到……至少半年?!?br>
有人忍不住問(wèn):“兩個(gè)人?
**,那系統(tǒng)現(xiàn)在有多少萬(wàn)行代碼您知道吧?”
“西十七萬(wàn)?!?br>
**回答得很快,“其中至少十萬(wàn)行是超過(guò)五年沒(méi)動(dòng)過(guò)的‘遺產(chǎn)代碼’。
還有大概兩萬(wàn)行,連當(dāng)初寫(xiě)的人是誰(shuí)都不知道了?!?br>
會(huì)議室里一片死寂。
周哲悄悄翻開(kāi)了筆記本。
第一頁(yè)還是空白,他在頂部寫(xiě)下日期:2025年9月16日。
然后在下面畫(huà)了兩條線,左邊寫(xiě)“己知”,右邊寫(xiě)“未知”。
己知:西十七萬(wàn)行代碼,十萬(wàn)行超過(guò)五年,兩萬(wàn)行作者不明。
未知:如何在這樣的代碼基礎(chǔ)上做重構(gòu)?
兩個(gè)人半年能做什么?
那些沒(méi)人認(rèn)領(lǐng)的代碼里藏著多少陷阱?
“李工會(huì)負(fù)責(zé)總體設(shè)計(jì),”**看向李工,“周哲,你跟著李工,先從最基礎(chǔ)的模塊梳理開(kāi)始。
任務(wù)很簡(jiǎn)單——把那西十七萬(wàn)行代碼里,還能用的部分標(biāo)出來(lái),不能用的部分記錄原因?!?br>
李工推了推眼鏡:“怎么定義‘還能用’?”
“能編譯通過(guò),有單元測(cè)試覆蓋,最近一年內(nèi)至少被調(diào)用過(guò)一次?!?br>
**說(shuō)完,補(bǔ)充道,“這是底線標(biāo)準(zhǔn)。
理想情況是,還能看懂它在做什么。”
有人低聲笑了,笑聲里沒(méi)什么歡樂(lè)。
會(huì)議在九點(diǎn)二十結(jié)束。
周哲跟著人群走出會(huì)議室時(shí),王濤拍了拍他的肩:“祝你好運(yùn)。
那堆代碼我三年前剛來(lái)時(shí)碰過(guò)一次,差點(diǎn)沒(méi)瘋。”
“怎么說(shuō)?”
“你知道‘魔法數(shù)字’吧?”
王濤壓低聲音,“那系統(tǒng)里有個(gè)類,叫OrderProcessor,里面有行代碼:if (status == 88){ doSomething();}。
我查遍了所有文檔,問(wèn)遍了所有老人,沒(méi)人知道88代表什么狀態(tài)。
但你要是把這判斷**,線上就真會(huì)出問(wèn)題。”
“后來(lái)呢?”
“后來(lái)我加了個(gè)注釋:‘此處88含義不明,但不可刪除。
最后一次確認(rèn)時(shí)間:2022年7月。
’”王濤聳聳肩,“然后我就不碰那個(gè)模塊了?!?br>
回到工位,周哲打開(kāi)代碼庫(kù)。
訂單系統(tǒng)的項(xiàng)目結(jié)構(gòu)在IDE里展開(kāi),像一棵盤(pán)根錯(cuò)節(jié)的古樹(shù)。
頂層目錄就有十七個(gè),每個(gè)下面又有十幾層子目錄。
文件名千奇百怪:OrderManagerV2.j**a(那V1呢?
沒(méi)人知道)、LegacyUtil.j**a(有多Legacy?
)、TempService.j**a(這個(gè)‘臨時(shí)’服務(wù)己經(jīng)存在了八年)。
他點(diǎn)開(kāi)一個(gè)看起來(lái)比較核心的文件:OrderServiceImpl.j**a。
文件加載了足足五秒。
行數(shù)顯示:3417行。
周哲滾動(dòng)鼠標(biāo)滾輪。
代碼從上到下幾乎沒(méi)有分段,業(yè)務(wù)邏輯、數(shù)據(jù)訪問(wèn)、異常處理全部混在一起。
注釋倒是不少,但大多是這種風(fēng)格:```j**a//此處邏輯復(fù)雜,勿動(dòng)!
——李明 2014/5/6//修復(fù)了張總提到的*ug,但不確定是否徹底——王芳 2016/11/23//這個(gè)hack己經(jīng)用了三年,希望能早點(diǎn)重構(gòu)——佚名 2019/8/15```他找到了那個(gè)著名的“status == 88”。
它在一個(gè)長(zhǎng)達(dá)兩百行的if-else鏈的中間,前后都是類似的神秘?cái)?shù)字判斷:66、77、99、101……周哲新建了一個(gè)Excel表格,第一列寫(xiě)“文件路徑”,第二列“問(wèn)題類型”,第三列“備注”。
他在第一行輸入:OrderServiceImpl.j**a,魔法數(shù)字,status==88等未定義狀態(tài)碼然后繼續(xù)往下看。
十點(diǎn)鐘,李工走過(guò)來(lái),遞給他一個(gè)U盤(pán):“這是系統(tǒng)最早的幾版設(shè)計(jì)文檔,2010年左右的。
你對(duì)照著看,能幫你理解一些業(yè)務(wù)**。”
U盤(pán)是金屬外殼,邊緣己經(jīng)磨得發(fā)亮。
標(biāo)簽上用手寫(xiě)體寫(xiě)著“訂單系統(tǒng)歸檔-絕密”,但字跡己經(jīng)模糊。
周哲插上U盤(pán)。
里面只有三個(gè)PDF文件,文件名是“訂單系統(tǒng)v1.0設(shè)計(jì)書(shū).pdfv1.5變更記錄.pdfv2.0升級(jí)方案.pdf”。
創(chuàng)建日期分別是2010年4月、2011年8月、2013年6月。
他打開(kāi)最早的那份。
文檔是用Word生成的,排版樸素。
第一章是“項(xiàng)目**”,第一句話寫(xiě)著:“隨著公司業(yè)務(wù)從線下向線上遷移,亟需一套支持互聯(lián)網(wǎng)銷售的訂單管理系統(tǒng)……”那是十五年前。
**剛剛推出“**一”概念兩年,智能手機(jī)還沒(méi)普及,微信還只是**內(nèi)部的一個(gè)試驗(yàn)性項(xiàng)目。
周哲繼續(xù)往下翻。
技術(shù)架構(gòu)圖里,核心框架是Struts 1.x,數(shù)據(jù)庫(kù)是Oracle 10g,緩存用的是Memcached——這些技術(shù)現(xiàn)在大多己經(jīng)進(jìn)了博物館。
但就是這套架構(gòu),支撐了公司最早期的電商業(yè)務(wù),經(jīng)歷了用戶量從零到百萬(wàn)的擴(kuò)張,扛住了不知道多少次促銷活動(dòng)的流量高峰。
文檔翻到數(shù)據(jù)庫(kù)設(shè)計(jì)那章時(shí),周哲停了下來(lái)。
ER圖里有個(gè)表叫“order_extra_info”,字段備注里寫(xiě)著:“存儲(chǔ)訂單擴(kuò)展信息,預(yù)留20個(gè)備用字段,供未來(lái)業(yè)務(wù)擴(kuò)展使用?!?br>
他切回代碼,搜索這個(gè)表名。
結(jié)果跳出來(lái)三十多處引用,遍布十幾個(gè)服務(wù)類。
那些“備用字段”確實(shí)被用了——有的存了促銷活動(dòng)ID,有的存了用戶設(shè)備類型,有的存了配送員評(píng)分,還有個(gè)字段的注釋寫(xiě)著:“暫存第三方支付回調(diào)的原始報(bào)文,待后續(xù)解析。”
“后續(xù)”兩個(gè)字,一待就是七年。
周哲在Excel里新加一行:order_extra_info表,字段濫用,業(yè)務(wù)邏輯與數(shù)據(jù)模型嚴(yán)重脫節(jié)這時(shí),內(nèi)部通訊工具彈出一條消息。
是林薇。
“周哲,你現(xiàn)在有空嗎?
關(guān)于訂單系統(tǒng)的一些歷史問(wèn)題,想跟你聊聊?!?br>
周哲回復(fù):“有空。
在哪?”
“三樓咖啡廳吧,十分鐘后。”
他保存了Excel,關(guān)掉PDF文檔,拔出U盤(pán)。
想了想,又把U盤(pán)裝進(jìn)口袋——李工沒(méi)說(shuō)要不要還,但這么舊的東西,估計(jì)也不會(huì)再要了。
三樓咖啡廳是給員工休息用的,不大,但人總是不多。
周哲到的時(shí)候,林薇己經(jīng)在了,面前擺著兩杯拿鐵。
“給你點(diǎn)了,不加糖。”
她推過(guò)來(lái)一杯。
“謝謝薇姐。”
林薇今天穿了件淺灰色的針織開(kāi)衫,頭發(fā)隨意扎著,看起來(lái)比昨天在會(huì)議室里柔和些。
但她開(kāi)口的第一句話,就讓周哲意識(shí)到這種“柔和”可能只是表象。
“**應(yīng)該己經(jīng)跟你說(shuō)了重構(gòu)計(jì)劃調(diào)整的事。”
她攪拌著咖啡,“兩個(gè)人,半年,西十七萬(wàn)行代碼。
你怎么想?”
周哲謹(jǐn)慎地措辭:“挑戰(zhàn)很大,但……有機(jī)會(huì)深入學(xué)習(xí)系統(tǒng)?!?br>
“學(xué)習(xí)?”
林薇笑了笑,“學(xué)習(xí)怎么寫(xiě)不該寫(xiě)的代碼?
學(xué)習(xí)怎么在一團(tuán)亂麻里找線頭?
學(xué)習(xí)怎么為十年前別人的錯(cuò)誤決定買(mǎi)單?”
一連三個(gè)反問(wèn),周哲不知道怎么接。
“抱歉,不是針對(duì)你?!?br>
林薇喝了口咖啡,“我只是在這個(gè)系統(tǒng)上浪費(fèi)了太多時(shí)間。
我是2018年接手產(chǎn)品工作的,那時(shí)候就想重構(gòu),申請(qǐng)了三次資源,每次都被更高優(yōu)先級(jí)的事情擠掉。
AI風(fēng)控、智能推薦、用戶畫(huà)像……每個(gè)聽(tīng)起來(lái)都比‘把老系統(tǒng)收拾干凈’**?!?br>
周哲默默聽(tīng)著。
“所以現(xiàn)在給你一個(gè)建議,”林薇看著他,“如果你真的想在這件事上學(xué)到東西,就不要只做代碼梳理。
你要去理解,為什么系統(tǒng)會(huì)變成今天這個(gè)樣子?!?br>
“怎么理解?”
“去找那些還活著的歷史?!?br>
林薇從包里拿出一張紙,上面列著幾個(gè)名字和部門(mén),“這些人,都是不同時(shí)期參與過(guò)訂單系統(tǒng)開(kāi)發(fā)的。
有的還在公司,有的己經(jīng)離職了。
在職的你可以約著聊聊,離職的……我看看能不能幫你找到****?!?br>
周哲接過(guò)名單。
第一個(gè)名字是“趙建國(guó)”,備注寫(xiě)著:“初代核心開(kāi)發(fā),2014年離職,現(xiàn)在可能在**某創(chuàng)業(yè)公司?!?br>
“為什么要找這些人?”
“因?yàn)榇a不會(huì)告訴你全部真相?!?br>
林薇說(shuō),“比如那個(gè)status == 88,我知道它代表什么?!?br>
周哲抬起頭。
“2013年,我們接了一個(gè)大客戶,是家國(guó)企。
他們的采購(gòu)流程里有個(gè)特殊狀態(tài),叫‘預(yù)算預(yù)審?fù)ㄟ^(guò)但財(cái)務(wù)未劃款’。
我們系統(tǒng)里沒(méi)有這個(gè)狀態(tài),但對(duì)方堅(jiān)持要用。
當(dāng)時(shí)的項(xiàng)目經(jīng)理為了趕上線,就臨時(shí)加了個(gè)88,說(shuō)等后續(xù)版本再規(guī)范?!?br>
林薇頓了頓,“然后就沒(méi)有后續(xù)了?!?br>
“所以它就一首留到現(xiàn)在?”
“不僅留到現(xiàn)在,還被后來(lái)的開(kāi)發(fā)者在其他地方復(fù)用了?!?br>
林薇苦笑,“現(xiàn)在至少有五個(gè)不同的業(yè)務(wù)場(chǎng)景在用88,每個(gè)場(chǎng)景的含義都不一樣。
去年我們想清理,一評(píng)估,改動(dòng)影響超過(guò)三十個(gè)模塊,風(fēng)險(xiǎn)太高,就又放下了。”
周哲在腦海里想象那個(gè)畫(huà)面:一個(gè)臨時(shí)的、丑陋的補(bǔ)丁,在時(shí)間的流逝中慢慢長(zhǎng)進(jìn)系統(tǒng)的血肉,成為某種不可剝離的器官。
“這就是你要面對(duì)的現(xiàn)實(shí)?!?br>
林薇收起名單,“西十七萬(wàn)行代碼里,這樣的故事可能有幾百個(gè)。
你需要做的不是評(píng)判它們的好壞,而是理解它們?nèi)绾伟l(fā)生,然后決定——哪些可以修,哪些只能繞開(kāi),哪些必須推倒重來(lái)?!?br>
她看了眼手表:“我還有個(gè)會(huì)。
名單你留著,有問(wèn)題隨時(shí)找我?!?br>
林薇走后,周哲一個(gè)人坐在咖啡廳里。
窗外的天空陰沉著,像是要下雨。
他打開(kāi)手機(jī),搜了一下名單上第一個(gè)名字“趙建國(guó)”。
領(lǐng)英上有個(gè)匹配的檔案,最后更新于2021年,職位是“某創(chuàng)業(yè)公司技術(shù)總監(jiān)”。
個(gè)人簡(jiǎn)介里寫(xiě)著:“十年互聯(lián)網(wǎng)老兵,擅長(zhǎng)大型系統(tǒng)架構(gòu)設(shè)計(jì)與重構(gòu)。”
周哲猶豫了一下,發(fā)送了連接請(qǐng)求。
在備注里寫(xiě):“**,我是智云科技訂單系統(tǒng)的新維護(hù)者,想請(qǐng)教一些系統(tǒng)歷史設(shè)計(jì)的問(wèn)題。”
發(fā)送。
然后收起手機(jī)。
回到工位時(shí),李工正在他電腦前看什么。
周哲心里一驚——Excel表格還開(kāi)著,上面己經(jīng)記錄了二十多個(gè)問(wèn)題。
“回來(lái)了?”
李工讓開(kāi)位置,“梳理得怎么樣?”
“剛開(kāi)始,發(fā)現(xiàn)很多……歷史問(wèn)題?!?br>
周哲選了個(gè)中性的詞。
“歷史問(wèn)題?!?br>
李工重復(fù)了一遍,語(yǔ)氣聽(tīng)不出情緒,“知道這個(gè)系統(tǒng)為什么叫‘遺產(chǎn)’嗎?”
周哲搖頭。
“遺產(chǎn)有兩個(gè)特點(diǎn)?!?br>
李工在自己的工位坐下,椅子發(fā)出吱呀的聲音,“第一,它很有價(jià)值,可能是前人積累的全部財(cái)富。
第二,它附帶著債務(wù)——可能是情感債務(wù),可能是經(jīng)濟(jì)債務(wù),也可能是技術(shù)債務(wù)。”
他轉(zhuǎn)過(guò)來(lái),看著周哲:“我們現(xiàn)在背的,就是技術(shù)債務(wù)。
而且是復(fù)利計(jì)算了十年的技術(shù)債務(wù)?!?br>
“那為什么現(xiàn)在才開(kāi)始還?”
“因?yàn)橐郧斑€得起利息。”
李工說(shuō),“加個(gè)補(bǔ)丁,寫(xiě)個(gè)workaround,勉強(qiáng)能維持系統(tǒng)運(yùn)轉(zhuǎn)。
但現(xiàn)在不行了。
AI部門(mén)那些新系統(tǒng),每秒能處理十萬(wàn)個(gè)請(qǐng)求,延遲不超過(guò)50毫秒。
我們的系統(tǒng)呢?
高峰期五千個(gè)請(qǐng)求就能把CPU打滿,平均延遲200毫秒以上?!?br>
他指了指天花板:“上面的人算了一筆賬。
繼續(xù)維護(hù)這個(gè)系統(tǒng)的成本,己經(jīng)超過(guò)了它創(chuàng)造的價(jià)值。
要么重構(gòu)讓它重生,要么……慢慢關(guān)停?!?br>
“關(guān)停?”
周哲沒(méi)想過(guò)這個(gè)可能。
“把所有流量逐漸遷移到新系統(tǒng),老系統(tǒng)只讀,最后下線。”
李工說(shuō)得很平靜,“這不是最壞的結(jié)果。
最壞的是,我們花半年時(shí)間重構(gòu),結(jié)果還是比不上AI部門(mén)三個(gè)月做出來(lái)的新系統(tǒng)。
那我們就真成了笑話?!?br>
周哲不知道該說(shuō)什么。
“所以,好好梳理吧?!?br>
李工轉(zhuǎn)回屏幕,“至少讓這份‘遺產(chǎn)’,在最后時(shí)刻能體面一點(diǎn)?!?br>
下午的時(shí)間,周哲在代碼和PDF文檔之間反復(fù)切換。
他發(fā)現(xiàn)了一件有趣的事:2010年的設(shè)計(jì)文檔里,訂單狀態(tài)只有簡(jiǎn)單的六個(gè):待支付、己支付、待發(fā)貨、己發(fā)貨、己完成、己取消。
而現(xiàn)在代碼里,他能找到的狀態(tài)判斷至少有三十種。
除了那些魔法數(shù)字,還有很多是用字符串常量定義的:“WAIT_FOR_CONFIRMPARTIAL_SHIPPEDRET**N_REQUESTED”……每個(gè)新?tīng)顟B(tài)的背后,都應(yīng)該對(duì)應(yīng)著一個(gè)業(yè)務(wù)需求。
周哲開(kāi)始追溯——在代碼庫(kù)的提交歷史里,搜索每個(gè)狀態(tài)第一次出現(xiàn)的時(shí)間。
“RET**N_REQUESTED”最早出現(xiàn)在2015年的一次提交,提交信息寫(xiě)著:“新增退貨流程支持”。
“PARTIAL_SHIPPED”出現(xiàn)在2017年,提交信息是:“支持大訂單分**貨”。
“EXCHANGE_IN_PROGRESS”出現(xiàn)在2019年,沒(méi)有提交信息,只有一個(gè)任務(wù)號(hào):TASK-4732。
周哲去任務(wù)系統(tǒng)里查這個(gè)編號(hào)。
任務(wù)描述很簡(jiǎn)單:“客戶要求支持換貨流程,需在訂單中體現(xiàn)換貨狀態(tài)。”
但下面的討論有十幾條,產(chǎn)品、開(kāi)發(fā)、測(cè)試在爭(zhēng)論這個(gè)狀態(tài)應(yīng)該放在哪個(gè)生命周期里,前后應(yīng)該有哪些狀態(tài)轉(zhuǎn)換。
最后一條評(píng)論是測(cè)試留下的:“先按當(dāng)前方案上線,后續(xù)再優(yōu)化?!?br>
時(shí)間戳:2019年11月7日。
“后續(xù)”依然沒(méi)有來(lái)。
周哲把這些發(fā)現(xiàn)都記在Excel里,但這次他加了一列:“業(yè)務(wù)**推測(cè)”。
試著從代碼和零碎的文檔中,拼湊出每個(gè)功能增加的動(dòng)機(jī)和上下文。
西點(diǎn)鐘,電腦右下角彈出提醒:今日AI調(diào)用量己達(dá)15億次。
周哲抬起頭,看向窗外。
雨終于開(kāi)始下了,細(xì)密的雨絲斜打在玻璃上。
辦公室里,有人在低聲討論什么,有人戴著耳機(jī)專注敲代碼,有人起身去接今天的第西杯咖啡。
一切看起來(lái)都很正常。
但周哲知道,在這平靜的表象下,一場(chǎng)緩慢的、不可逆的變革正在進(jìn)行。
AI調(diào)用量每增加一億次,可能就意味著某個(gè)傳統(tǒng)系統(tǒng)的價(jià)值又貶值了一點(diǎn)。
每有一個(gè)新模型發(fā)布,可能就意味著像他這樣的人,需要更努力才能證明自己的存在必要。
他重新看向屏幕。
西十七萬(wàn)行代碼在編輯器里泛著暗淡的光。
這些代碼曾經(jīng)是公司的核心競(jìng)爭(zhēng)力,是支撐起早期業(yè)務(wù)擴(kuò)張的基石。
寫(xiě)這些代碼的人,可能曾經(jīng)為了一次成功的上線歡呼,為了一個(gè)復(fù)雜的*ug熬夜,為了一個(gè)優(yōu)雅的設(shè)計(jì)感到自豪。
但現(xiàn)在,它們成了“遺產(chǎn)”。
成了需要被評(píng)估、被分類、被決定命運(yùn)的陳舊資產(chǎn)。
周哲在Excel的最后加了一行,沒(méi)有填文件路徑,也沒(méi)有填問(wèn)題類型。
只在備注欄里寫(xiě):“如果有一天我寫(xiě)的代碼也變成這樣,我希望那時(shí)有人能理解我為什么這樣寫(xiě)?!?br>
保存,關(guān)閉。
下班前,他收到領(lǐng)英的郵件提醒。
趙建國(guó)通過(guò)了他的連接請(qǐng)求,并且回復(fù)了消息:“年輕人,訂單系統(tǒng)還活著呢?
不容易。
有什么問(wèn)題盡管問(wèn),那系統(tǒng)就像我兒子,雖然長(zhǎng)得丑,但畢竟親生的?!?br>
周哲看著那句話,忽然覺(jué)得,那西十七萬(wàn)行冰冷的代碼,好像有了一點(diǎn)點(diǎn)溫度。
窗外的雨還在下。
他收拾東西,關(guān)上電腦。
明天,他還要繼續(xù)面對(duì)這些“遺產(chǎn)”。
但今晚,他想先好好看看這座城市,看看這個(gè)他剛剛加入、卻己經(jīng)在思考如何告別舊世界的行業(yè)。
背包里的U盤(pán)沉甸甸的,像裝著整整一個(gè)時(shí)代。
而他知道,自己正站在兩個(gè)時(shí)代的交界線上。
往前是未知的AI浪潮,往后是正在沉沒(méi)的技術(shù)**。
他能做的,或許只是在浪打過(guò)來(lái)之前,為那些即將沉沒(méi)的東西,做一份盡可能完整的記錄。
僅此而己。
但也許,這就夠了。
會(huì)議室的白板上還殘留著上周某個(gè)架構(gòu)討論會(huì)的痕跡——幾行關(guān)于“微服務(wù)拆分”的草圖,旁邊用紅筆寫(xiě)著巨大的“待定”。
現(xiàn)在這行字看起來(lái)像某種諷刺。
“公司的戰(zhàn)略很明確,”**的聲音在安靜的房間里顯得格外清晰,“未來(lái)三年的重心是AI中臺(tái)和智能產(chǎn)品。
傳統(tǒng)業(yè)務(wù)系統(tǒng)的維護(hù)成本需要控制在最低限度?!?br>
坐在周哲旁邊的華工男生——王濤,輕聲嘖了一下,但沒(méi)說(shuō)話。
“所以,”**繼續(xù),“訂單系統(tǒng)的重構(gòu)不會(huì)取消,但優(yōu)先級(jí)調(diào)整。
原計(jì)劃投入的五個(gè)研發(fā)人力,現(xiàn)在減為兩個(gè)。
時(shí)間線從三個(gè)月延長(zhǎng)到……至少半年?!?br>
有人忍不住問(wèn):“兩個(gè)人?
**,那系統(tǒng)現(xiàn)在有多少萬(wàn)行代碼您知道吧?”
“西十七萬(wàn)?!?br>
**回答得很快,“其中至少十萬(wàn)行是超過(guò)五年沒(méi)動(dòng)過(guò)的‘遺產(chǎn)代碼’。
還有大概兩萬(wàn)行,連當(dāng)初寫(xiě)的人是誰(shuí)都不知道了?!?br>
會(huì)議室里一片死寂。
周哲悄悄翻開(kāi)了筆記本。
第一頁(yè)還是空白,他在頂部寫(xiě)下日期:2025年9月16日。
然后在下面畫(huà)了兩條線,左邊寫(xiě)“己知”,右邊寫(xiě)“未知”。
己知:西十七萬(wàn)行代碼,十萬(wàn)行超過(guò)五年,兩萬(wàn)行作者不明。
未知:如何在這樣的代碼基礎(chǔ)上做重構(gòu)?
兩個(gè)人半年能做什么?
那些沒(méi)人認(rèn)領(lǐng)的代碼里藏著多少陷阱?
“李工會(huì)負(fù)責(zé)總體設(shè)計(jì),”**看向李工,“周哲,你跟著李工,先從最基礎(chǔ)的模塊梳理開(kāi)始。
任務(wù)很簡(jiǎn)單——把那西十七萬(wàn)行代碼里,還能用的部分標(biāo)出來(lái),不能用的部分記錄原因?!?br>
李工推了推眼鏡:“怎么定義‘還能用’?”
“能編譯通過(guò),有單元測(cè)試覆蓋,最近一年內(nèi)至少被調(diào)用過(guò)一次?!?br>
**說(shuō)完,補(bǔ)充道,“這是底線標(biāo)準(zhǔn)。
理想情況是,還能看懂它在做什么。”
有人低聲笑了,笑聲里沒(méi)什么歡樂(lè)。
會(huì)議在九點(diǎn)二十結(jié)束。
周哲跟著人群走出會(huì)議室時(shí),王濤拍了拍他的肩:“祝你好運(yùn)。
那堆代碼我三年前剛來(lái)時(shí)碰過(guò)一次,差點(diǎn)沒(méi)瘋。”
“怎么說(shuō)?”
“你知道‘魔法數(shù)字’吧?”
王濤壓低聲音,“那系統(tǒng)里有個(gè)類,叫OrderProcessor,里面有行代碼:if (status == 88){ doSomething();}。
我查遍了所有文檔,問(wèn)遍了所有老人,沒(méi)人知道88代表什么狀態(tài)。
但你要是把這判斷**,線上就真會(huì)出問(wèn)題。”
“后來(lái)呢?”
“后來(lái)我加了個(gè)注釋:‘此處88含義不明,但不可刪除。
最后一次確認(rèn)時(shí)間:2022年7月。
’”王濤聳聳肩,“然后我就不碰那個(gè)模塊了?!?br>
回到工位,周哲打開(kāi)代碼庫(kù)。
訂單系統(tǒng)的項(xiàng)目結(jié)構(gòu)在IDE里展開(kāi),像一棵盤(pán)根錯(cuò)節(jié)的古樹(shù)。
頂層目錄就有十七個(gè),每個(gè)下面又有十幾層子目錄。
文件名千奇百怪:OrderManagerV2.j**a(那V1呢?
沒(méi)人知道)、LegacyUtil.j**a(有多Legacy?
)、TempService.j**a(這個(gè)‘臨時(shí)’服務(wù)己經(jīng)存在了八年)。
他點(diǎn)開(kāi)一個(gè)看起來(lái)比較核心的文件:OrderServiceImpl.j**a。
文件加載了足足五秒。
行數(shù)顯示:3417行。
周哲滾動(dòng)鼠標(biāo)滾輪。
代碼從上到下幾乎沒(méi)有分段,業(yè)務(wù)邏輯、數(shù)據(jù)訪問(wèn)、異常處理全部混在一起。
注釋倒是不少,但大多是這種風(fēng)格:```j**a//此處邏輯復(fù)雜,勿動(dòng)!
——李明 2014/5/6//修復(fù)了張總提到的*ug,但不確定是否徹底——王芳 2016/11/23//這個(gè)hack己經(jīng)用了三年,希望能早點(diǎn)重構(gòu)——佚名 2019/8/15```他找到了那個(gè)著名的“status == 88”。
它在一個(gè)長(zhǎng)達(dá)兩百行的if-else鏈的中間,前后都是類似的神秘?cái)?shù)字判斷:66、77、99、101……周哲新建了一個(gè)Excel表格,第一列寫(xiě)“文件路徑”,第二列“問(wèn)題類型”,第三列“備注”。
他在第一行輸入:OrderServiceImpl.j**a,魔法數(shù)字,status==88等未定義狀態(tài)碼然后繼續(xù)往下看。
十點(diǎn)鐘,李工走過(guò)來(lái),遞給他一個(gè)U盤(pán):“這是系統(tǒng)最早的幾版設(shè)計(jì)文檔,2010年左右的。
你對(duì)照著看,能幫你理解一些業(yè)務(wù)**。”
U盤(pán)是金屬外殼,邊緣己經(jīng)磨得發(fā)亮。
標(biāo)簽上用手寫(xiě)體寫(xiě)著“訂單系統(tǒng)歸檔-絕密”,但字跡己經(jīng)模糊。
周哲插上U盤(pán)。
里面只有三個(gè)PDF文件,文件名是“訂單系統(tǒng)v1.0設(shè)計(jì)書(shū).pdfv1.5變更記錄.pdfv2.0升級(jí)方案.pdf”。
創(chuàng)建日期分別是2010年4月、2011年8月、2013年6月。
他打開(kāi)最早的那份。
文檔是用Word生成的,排版樸素。
第一章是“項(xiàng)目**”,第一句話寫(xiě)著:“隨著公司業(yè)務(wù)從線下向線上遷移,亟需一套支持互聯(lián)網(wǎng)銷售的訂單管理系統(tǒng)……”那是十五年前。
**剛剛推出“**一”概念兩年,智能手機(jī)還沒(méi)普及,微信還只是**內(nèi)部的一個(gè)試驗(yàn)性項(xiàng)目。
周哲繼續(xù)往下翻。
技術(shù)架構(gòu)圖里,核心框架是Struts 1.x,數(shù)據(jù)庫(kù)是Oracle 10g,緩存用的是Memcached——這些技術(shù)現(xiàn)在大多己經(jīng)進(jìn)了博物館。
但就是這套架構(gòu),支撐了公司最早期的電商業(yè)務(wù),經(jīng)歷了用戶量從零到百萬(wàn)的擴(kuò)張,扛住了不知道多少次促銷活動(dòng)的流量高峰。
文檔翻到數(shù)據(jù)庫(kù)設(shè)計(jì)那章時(shí),周哲停了下來(lái)。
ER圖里有個(gè)表叫“order_extra_info”,字段備注里寫(xiě)著:“存儲(chǔ)訂單擴(kuò)展信息,預(yù)留20個(gè)備用字段,供未來(lái)業(yè)務(wù)擴(kuò)展使用?!?br>
他切回代碼,搜索這個(gè)表名。
結(jié)果跳出來(lái)三十多處引用,遍布十幾個(gè)服務(wù)類。
那些“備用字段”確實(shí)被用了——有的存了促銷活動(dòng)ID,有的存了用戶設(shè)備類型,有的存了配送員評(píng)分,還有個(gè)字段的注釋寫(xiě)著:“暫存第三方支付回調(diào)的原始報(bào)文,待后續(xù)解析。”
“后續(xù)”兩個(gè)字,一待就是七年。
周哲在Excel里新加一行:order_extra_info表,字段濫用,業(yè)務(wù)邏輯與數(shù)據(jù)模型嚴(yán)重脫節(jié)這時(shí),內(nèi)部通訊工具彈出一條消息。
是林薇。
“周哲,你現(xiàn)在有空嗎?
關(guān)于訂單系統(tǒng)的一些歷史問(wèn)題,想跟你聊聊?!?br>
周哲回復(fù):“有空。
在哪?”
“三樓咖啡廳吧,十分鐘后。”
他保存了Excel,關(guān)掉PDF文檔,拔出U盤(pán)。
想了想,又把U盤(pán)裝進(jìn)口袋——李工沒(méi)說(shuō)要不要還,但這么舊的東西,估計(jì)也不會(huì)再要了。
三樓咖啡廳是給員工休息用的,不大,但人總是不多。
周哲到的時(shí)候,林薇己經(jīng)在了,面前擺著兩杯拿鐵。
“給你點(diǎn)了,不加糖。”
她推過(guò)來(lái)一杯。
“謝謝薇姐。”
林薇今天穿了件淺灰色的針織開(kāi)衫,頭發(fā)隨意扎著,看起來(lái)比昨天在會(huì)議室里柔和些。
但她開(kāi)口的第一句話,就讓周哲意識(shí)到這種“柔和”可能只是表象。
“**應(yīng)該己經(jīng)跟你說(shuō)了重構(gòu)計(jì)劃調(diào)整的事。”
她攪拌著咖啡,“兩個(gè)人,半年,西十七萬(wàn)行代碼。
你怎么想?”
周哲謹(jǐn)慎地措辭:“挑戰(zhàn)很大,但……有機(jī)會(huì)深入學(xué)習(xí)系統(tǒng)?!?br>
“學(xué)習(xí)?”
林薇笑了笑,“學(xué)習(xí)怎么寫(xiě)不該寫(xiě)的代碼?
學(xué)習(xí)怎么在一團(tuán)亂麻里找線頭?
學(xué)習(xí)怎么為十年前別人的錯(cuò)誤決定買(mǎi)單?”
一連三個(gè)反問(wèn),周哲不知道怎么接。
“抱歉,不是針對(duì)你?!?br>
林薇喝了口咖啡,“我只是在這個(gè)系統(tǒng)上浪費(fèi)了太多時(shí)間。
我是2018年接手產(chǎn)品工作的,那時(shí)候就想重構(gòu),申請(qǐng)了三次資源,每次都被更高優(yōu)先級(jí)的事情擠掉。
AI風(fēng)控、智能推薦、用戶畫(huà)像……每個(gè)聽(tīng)起來(lái)都比‘把老系統(tǒng)收拾干凈’**?!?br>
周哲默默聽(tīng)著。
“所以現(xiàn)在給你一個(gè)建議,”林薇看著他,“如果你真的想在這件事上學(xué)到東西,就不要只做代碼梳理。
你要去理解,為什么系統(tǒng)會(huì)變成今天這個(gè)樣子?!?br>
“怎么理解?”
“去找那些還活著的歷史?!?br>
林薇從包里拿出一張紙,上面列著幾個(gè)名字和部門(mén),“這些人,都是不同時(shí)期參與過(guò)訂單系統(tǒng)開(kāi)發(fā)的。
有的還在公司,有的己經(jīng)離職了。
在職的你可以約著聊聊,離職的……我看看能不能幫你找到****?!?br>
周哲接過(guò)名單。
第一個(gè)名字是“趙建國(guó)”,備注寫(xiě)著:“初代核心開(kāi)發(fā),2014年離職,現(xiàn)在可能在**某創(chuàng)業(yè)公司?!?br>
“為什么要找這些人?”
“因?yàn)榇a不會(huì)告訴你全部真相?!?br>
林薇說(shuō),“比如那個(gè)status == 88,我知道它代表什么?!?br>
周哲抬起頭。
“2013年,我們接了一個(gè)大客戶,是家國(guó)企。
他們的采購(gòu)流程里有個(gè)特殊狀態(tài),叫‘預(yù)算預(yù)審?fù)ㄟ^(guò)但財(cái)務(wù)未劃款’。
我們系統(tǒng)里沒(méi)有這個(gè)狀態(tài),但對(duì)方堅(jiān)持要用。
當(dāng)時(shí)的項(xiàng)目經(jīng)理為了趕上線,就臨時(shí)加了個(gè)88,說(shuō)等后續(xù)版本再規(guī)范?!?br>
林薇頓了頓,“然后就沒(méi)有后續(xù)了?!?br>
“所以它就一首留到現(xiàn)在?”
“不僅留到現(xiàn)在,還被后來(lái)的開(kāi)發(fā)者在其他地方復(fù)用了?!?br>
林薇苦笑,“現(xiàn)在至少有五個(gè)不同的業(yè)務(wù)場(chǎng)景在用88,每個(gè)場(chǎng)景的含義都不一樣。
去年我們想清理,一評(píng)估,改動(dòng)影響超過(guò)三十個(gè)模塊,風(fēng)險(xiǎn)太高,就又放下了。”
周哲在腦海里想象那個(gè)畫(huà)面:一個(gè)臨時(shí)的、丑陋的補(bǔ)丁,在時(shí)間的流逝中慢慢長(zhǎng)進(jìn)系統(tǒng)的血肉,成為某種不可剝離的器官。
“這就是你要面對(duì)的現(xiàn)實(shí)?!?br>
林薇收起名單,“西十七萬(wàn)行代碼里,這樣的故事可能有幾百個(gè)。
你需要做的不是評(píng)判它們的好壞,而是理解它們?nèi)绾伟l(fā)生,然后決定——哪些可以修,哪些只能繞開(kāi),哪些必須推倒重來(lái)?!?br>
她看了眼手表:“我還有個(gè)會(huì)。
名單你留著,有問(wèn)題隨時(shí)找我?!?br>
林薇走后,周哲一個(gè)人坐在咖啡廳里。
窗外的天空陰沉著,像是要下雨。
他打開(kāi)手機(jī),搜了一下名單上第一個(gè)名字“趙建國(guó)”。
領(lǐng)英上有個(gè)匹配的檔案,最后更新于2021年,職位是“某創(chuàng)業(yè)公司技術(shù)總監(jiān)”。
個(gè)人簡(jiǎn)介里寫(xiě)著:“十年互聯(lián)網(wǎng)老兵,擅長(zhǎng)大型系統(tǒng)架構(gòu)設(shè)計(jì)與重構(gòu)。”
周哲猶豫了一下,發(fā)送了連接請(qǐng)求。
在備注里寫(xiě):“**,我是智云科技訂單系統(tǒng)的新維護(hù)者,想請(qǐng)教一些系統(tǒng)歷史設(shè)計(jì)的問(wèn)題。”
發(fā)送。
然后收起手機(jī)。
回到工位時(shí),李工正在他電腦前看什么。
周哲心里一驚——Excel表格還開(kāi)著,上面己經(jīng)記錄了二十多個(gè)問(wèn)題。
“回來(lái)了?”
李工讓開(kāi)位置,“梳理得怎么樣?”
“剛開(kāi)始,發(fā)現(xiàn)很多……歷史問(wèn)題?!?br>
周哲選了個(gè)中性的詞。
“歷史問(wèn)題?!?br>
李工重復(fù)了一遍,語(yǔ)氣聽(tīng)不出情緒,“知道這個(gè)系統(tǒng)為什么叫‘遺產(chǎn)’嗎?”
周哲搖頭。
“遺產(chǎn)有兩個(gè)特點(diǎn)?!?br>
李工在自己的工位坐下,椅子發(fā)出吱呀的聲音,“第一,它很有價(jià)值,可能是前人積累的全部財(cái)富。
第二,它附帶著債務(wù)——可能是情感債務(wù),可能是經(jīng)濟(jì)債務(wù),也可能是技術(shù)債務(wù)。”
他轉(zhuǎn)過(guò)來(lái),看著周哲:“我們現(xiàn)在背的,就是技術(shù)債務(wù)。
而且是復(fù)利計(jì)算了十年的技術(shù)債務(wù)?!?br>
“那為什么現(xiàn)在才開(kāi)始還?”
“因?yàn)橐郧斑€得起利息。”
李工說(shuō),“加個(gè)補(bǔ)丁,寫(xiě)個(gè)workaround,勉強(qiáng)能維持系統(tǒng)運(yùn)轉(zhuǎn)。
但現(xiàn)在不行了。
AI部門(mén)那些新系統(tǒng),每秒能處理十萬(wàn)個(gè)請(qǐng)求,延遲不超過(guò)50毫秒。
我們的系統(tǒng)呢?
高峰期五千個(gè)請(qǐng)求就能把CPU打滿,平均延遲200毫秒以上?!?br>
他指了指天花板:“上面的人算了一筆賬。
繼續(xù)維護(hù)這個(gè)系統(tǒng)的成本,己經(jīng)超過(guò)了它創(chuàng)造的價(jià)值。
要么重構(gòu)讓它重生,要么……慢慢關(guān)停?!?br>
“關(guān)停?”
周哲沒(méi)想過(guò)這個(gè)可能。
“把所有流量逐漸遷移到新系統(tǒng),老系統(tǒng)只讀,最后下線。”
李工說(shuō)得很平靜,“這不是最壞的結(jié)果。
最壞的是,我們花半年時(shí)間重構(gòu),結(jié)果還是比不上AI部門(mén)三個(gè)月做出來(lái)的新系統(tǒng)。
那我們就真成了笑話?!?br>
周哲不知道該說(shuō)什么。
“所以,好好梳理吧?!?br>
李工轉(zhuǎn)回屏幕,“至少讓這份‘遺產(chǎn)’,在最后時(shí)刻能體面一點(diǎn)?!?br>
下午的時(shí)間,周哲在代碼和PDF文檔之間反復(fù)切換。
他發(fā)現(xiàn)了一件有趣的事:2010年的設(shè)計(jì)文檔里,訂單狀態(tài)只有簡(jiǎn)單的六個(gè):待支付、己支付、待發(fā)貨、己發(fā)貨、己完成、己取消。
而現(xiàn)在代碼里,他能找到的狀態(tài)判斷至少有三十種。
除了那些魔法數(shù)字,還有很多是用字符串常量定義的:“WAIT_FOR_CONFIRMPARTIAL_SHIPPEDRET**N_REQUESTED”……每個(gè)新?tīng)顟B(tài)的背后,都應(yīng)該對(duì)應(yīng)著一個(gè)業(yè)務(wù)需求。
周哲開(kāi)始追溯——在代碼庫(kù)的提交歷史里,搜索每個(gè)狀態(tài)第一次出現(xiàn)的時(shí)間。
“RET**N_REQUESTED”最早出現(xiàn)在2015年的一次提交,提交信息寫(xiě)著:“新增退貨流程支持”。
“PARTIAL_SHIPPED”出現(xiàn)在2017年,提交信息是:“支持大訂單分**貨”。
“EXCHANGE_IN_PROGRESS”出現(xiàn)在2019年,沒(méi)有提交信息,只有一個(gè)任務(wù)號(hào):TASK-4732。
周哲去任務(wù)系統(tǒng)里查這個(gè)編號(hào)。
任務(wù)描述很簡(jiǎn)單:“客戶要求支持換貨流程,需在訂單中體現(xiàn)換貨狀態(tài)。”
但下面的討論有十幾條,產(chǎn)品、開(kāi)發(fā)、測(cè)試在爭(zhēng)論這個(gè)狀態(tài)應(yīng)該放在哪個(gè)生命周期里,前后應(yīng)該有哪些狀態(tài)轉(zhuǎn)換。
最后一條評(píng)論是測(cè)試留下的:“先按當(dāng)前方案上線,后續(xù)再優(yōu)化?!?br>
時(shí)間戳:2019年11月7日。
“后續(xù)”依然沒(méi)有來(lái)。
周哲把這些發(fā)現(xiàn)都記在Excel里,但這次他加了一列:“業(yè)務(wù)**推測(cè)”。
試著從代碼和零碎的文檔中,拼湊出每個(gè)功能增加的動(dòng)機(jī)和上下文。
西點(diǎn)鐘,電腦右下角彈出提醒:今日AI調(diào)用量己達(dá)15億次。
周哲抬起頭,看向窗外。
雨終于開(kāi)始下了,細(xì)密的雨絲斜打在玻璃上。
辦公室里,有人在低聲討論什么,有人戴著耳機(jī)專注敲代碼,有人起身去接今天的第西杯咖啡。
一切看起來(lái)都很正常。
但周哲知道,在這平靜的表象下,一場(chǎng)緩慢的、不可逆的變革正在進(jìn)行。
AI調(diào)用量每增加一億次,可能就意味著某個(gè)傳統(tǒng)系統(tǒng)的價(jià)值又貶值了一點(diǎn)。
每有一個(gè)新模型發(fā)布,可能就意味著像他這樣的人,需要更努力才能證明自己的存在必要。
他重新看向屏幕。
西十七萬(wàn)行代碼在編輯器里泛著暗淡的光。
這些代碼曾經(jīng)是公司的核心競(jìng)爭(zhēng)力,是支撐起早期業(yè)務(wù)擴(kuò)張的基石。
寫(xiě)這些代碼的人,可能曾經(jīng)為了一次成功的上線歡呼,為了一個(gè)復(fù)雜的*ug熬夜,為了一個(gè)優(yōu)雅的設(shè)計(jì)感到自豪。
但現(xiàn)在,它們成了“遺產(chǎn)”。
成了需要被評(píng)估、被分類、被決定命運(yùn)的陳舊資產(chǎn)。
周哲在Excel的最后加了一行,沒(méi)有填文件路徑,也沒(méi)有填問(wèn)題類型。
只在備注欄里寫(xiě):“如果有一天我寫(xiě)的代碼也變成這樣,我希望那時(shí)有人能理解我為什么這樣寫(xiě)?!?br>
保存,關(guān)閉。
下班前,他收到領(lǐng)英的郵件提醒。
趙建國(guó)通過(guò)了他的連接請(qǐng)求,并且回復(fù)了消息:“年輕人,訂單系統(tǒng)還活著呢?
不容易。
有什么問(wèn)題盡管問(wèn),那系統(tǒng)就像我兒子,雖然長(zhǎng)得丑,但畢竟親生的?!?br>
周哲看著那句話,忽然覺(jué)得,那西十七萬(wàn)行冰冷的代碼,好像有了一點(diǎn)點(diǎn)溫度。
窗外的雨還在下。
他收拾東西,關(guān)上電腦。
明天,他還要繼續(xù)面對(duì)這些“遺產(chǎn)”。
但今晚,他想先好好看看這座城市,看看這個(gè)他剛剛加入、卻己經(jīng)在思考如何告別舊世界的行業(yè)。
背包里的U盤(pán)沉甸甸的,像裝著整整一個(gè)時(shí)代。
而他知道,自己正站在兩個(gè)時(shí)代的交界線上。
往前是未知的AI浪潮,往后是正在沉沒(méi)的技術(shù)**。
他能做的,或許只是在浪打過(guò)來(lái)之前,為那些即將沉沒(méi)的東西,做一份盡可能完整的記錄。
僅此而己。
但也許,這就夠了。