創(chuàng)作聲明:本文為虛構(gòu)創(chuàng)作,請(qǐng)勿與現(xiàn)實(shí)關(guān)聯(lián)
紀(jì)懷舟偷走核心代碼那天,蘇默白在出租屋里坐了三個(gè)小時(shí)。
然后他打開電腦,登錄了一個(gè)很久沒用的GitHub倉庫——十年前,他和紀(jì)懷舟還在大學(xué)機(jī)房通宵趕項(xiàng)目的時(shí)候用的那個(gè)倉庫。
他翻到最底層一行注釋:「//這行代碼我知道有問題,但產(chǎn)品明天要上線,先這樣。等用戶量超過百萬,我再重寫?!K默白,2015年11月?!?/p>
他盯著這行字看了一整夜。
第二天,他租下了紀(jì)懷舟公司正對(duì)面的辦公室,架起四臺(tái)投影儀,把那行BUG代碼打在了六十平米的玻璃幕墻上。
第三天,不是罵人,不是舉報(bào),是一行代碼,二十四小時(shí)滾動(dòng)播放。紀(jì)懷舟透過百葉窗看到那行代碼時(shí),手里的咖啡杯掉在了地上。
01
紀(jì)懷舟把我踢出公司那天,是周四下午。
他約我在樓下的咖啡廳見面,點(diǎn)了兩杯美式??Х冗€沒上來,他就從公文包里拿出了一份文件——股東會(huì)決議。我的名字不在上面。他把文件放在桌上,推到我面前,動(dòng)作很輕,像是怕碰灑什么。他說公司的核心代碼是團(tuán)隊(duì)共同開發(fā)的,知識(shí)產(chǎn)權(quán)屬于公司,不屬于任何個(gè)人。他的聲音不高不低,語速均勻,每個(gè)字都像提前排練過。我看著他,問你說的是哪個(gè)團(tuán)隊(duì)。他沒回答。
他旁邊坐著公司法務(wù),一個(gè)我沒見過的人,穿深灰色西裝,領(lǐng)帶松了一扣。法務(wù)的手放在桌上,手邊攤著一份技術(shù)服務(wù)合同——我簽過的那份。他指了一下合同第七頁的某個(gè)條款,沒有念出來。我知道那條款寫的是什么。當(dāng)初紀(jì)懷舟跟我說,簽技術(shù)顧問合同是稅務(wù)上的權(quán)宜之計(jì),方便走賬。我信了。簽了?,F(xiàn)在這份合同在法律上的定義是:我是一個(gè)按月拿服務(wù)費(fèi)的外包技術(shù)顧問。合同到期的那個(gè)月,他不再續(xù)約。我和這個(gè)公司沒有任何股權(quán)關(guān)系。我寫了所有代碼,但法律上,代碼是公司的。
我站起來。椅子刮在地磚上發(fā)出一聲尖響。我端起自己那杯美式,放在他面前??Х纫好婊瘟藘上?,灑出來幾滴,洇在他那份股東會(huì)決議的紙邊上。我說你喝吧,你需要醒醒。然后我走出了咖啡廳。
外面在下雨。不大。手機(jī)震了,是公司OA系統(tǒng)發(fā)來的離職確認(rèn)提醒。紀(jì)懷舟是個(gè)細(xì)致的人,連離職流程都提前幫我走完了。系統(tǒng)提示:您有新的待辦——確認(rèn)離職交接清單。清單編號(hào)后面,我點(diǎn)進(jìn)去看,核心倉庫的提交記錄里,我所有的簽入名都被替換成了三個(gè)字母——J.H.Z.。紀(jì)懷舟。那些代碼的注釋還是我的筆跡,但名字被擦掉了。像一個(gè)陌生人穿著我的衣服走在街上。
我回到出租屋。在沙發(fā)上坐了三小時(shí)。窗外的雨停了,又下,又停。天黑了。我沒有開燈。然后我打開電腦,登錄了一個(gè)很久沒用的GitHub倉庫——十年前我們?cè)诖髮W(xué)機(jī)房趕第一個(gè)項(xiàng)目時(shí)用的那個(gè)。那時(shí)候還沒有云帆企服,沒有融資,沒有客戶。只有我和他,兩臺(tái)舊電腦,一包泡面掰兩半分著吃。倉庫還在,沒被清理。
我點(diǎn)進(jìn)最底層的那個(gè)文件,拉到代碼最后。一行注釋還在。
「//這行代碼我知道有問題,但產(chǎn)品明天要上線,先這樣。等用戶量超過百萬,我再重寫?!K默白,2015年11月」
屏幕的光照著我的臉。窗外又開始下雨。我沒有修改這個(gè)文件,只是往下翻。文檔區(qū)有一份《技術(shù)債務(wù)清單》,文件名很正式——是當(dāng)年創(chuàng)業(yè)比賽要求提交的“已知問題與改進(jìn)計(jì)劃”。清單第三條寫著底層調(diào)度算法在大規(guī)模并發(fā)下的時(shí)間戳錯(cuò)位風(fēng)險(xiǎn),預(yù)估重構(gòu)周期三周。末尾有一行回復(fù),紀(jì)懷舟寫的:「好的,先記著。上完線回頭一起改?!狫.H.Z.」
兩份文件,同一年的同一個(gè)月。我當(dāng)時(shí)沒有多想。關(guān)掉文檔區(qū),回到那行注釋上。手指懸在鍵盤上方,沒有按下任何一個(gè)鍵。只是看著那行注釋,看了一整夜。十年前的我用兩行斜杠給自己留了一句話,十年后我才看清楚那句話還留著。
![]()
02
我沒有打官司。紀(jì)懷舟在這件事上的準(zhǔn)備比我想的更充分,當(dāng)初的合同條款每一個(gè)字都是攔路虎。我需要時(shí)間,需要一種他無法用律師函擋回去的方式。
我打了幾個(gè)電話,找了寫字樓租賃中介。中介叫小丁,二十五歲,頭發(fā)打了太多發(fā)蠟,說話語速很快。他帶我看了幾個(gè)辦公空間,我都不滿意。直到他領(lǐng)我到望江路那棟寫字樓——二十一層,正對(duì)面是紀(jì)懷舟的新公司「云帆企服」。兩棟樓間距不到三十米,近到我能看清他辦公室百葉窗上的灰塵。他的辦公室在對(duì)面樓的十八層,一整面玻璃幕墻,陽光照在上面反著光,像一塊巨大的廣告牌。
我租下了最靠窗的那間。六十平米,月租押一付三。然后我去二手市場(chǎng)買了四臺(tái)工程投影儀,型號(hào)很老,燈泡用了很久但亮度足夠。我把它們并排架在窗邊,鏡頭對(duì)準(zhǔn)對(duì)面寫字樓的玻璃幕墻。畫面要投滿整面墻——不是我這邊這面,是他的那扇墻。我在玻璃上貼了一張黑色的遮光膜,把自己這面遮住,只留投影儀的鏡頭從縫隙里伸出去。像一個(gè)槍管。
我在窗邊的墻上貼了一張紙條,寫了一句話:從這面窗戶看出去,只有一行代碼。
03
投影開始的那天,是紀(jì)懷舟公司新辦公室啟用的第一天。
他那天請(qǐng)了客戶來參觀,門口擺了花籃,前臺(tái)在分蛋糕。云帆企服四個(gè)大字掛在背景墻上,燙金字體,燈光打在上面亮得晃眼。我從二十六樓往下望,能看到穿西裝的客人們?cè)陂T口握手、交換名片。透過玻璃幕墻,能隱約看到紀(jì)懷舟的背影——他在人群中間,手在空中比劃著什么,大概在說云帆的未來。
下午三點(diǎn),訪客最多的時(shí)候,我按下了投影儀的開關(guān)。
一行代碼,白色字體,打在紀(jì)懷舟公司正面的玻璃幕墻上。二十四號(hào)字,CourierNew,等寬字體。每一個(gè)字符清晰到能看清分號(hào)上的弧度:那行十年前的注釋,和注釋下面那十二行縮進(jìn)對(duì)齊的BUG代碼本身,像一個(gè)袖珍的墓碑。四臺(tái)投影儀,每一臺(tái)打一句版本迭代記錄——從2015年的初版,到2018年被刪掉的提交備注,到今年倉庫里被替換的簽名。信息流在玻璃上慢慢滾動(dòng)。沒有罵人,沒有舉報(bào),沒有泄露一行商業(yè)機(jī)密。只是一行十年前的代碼,和一段被刪掉的提交歷史。
路人開始停下來拍照。先是一個(gè)外賣騎手,他把電動(dòng)車停在路邊,仰頭看了半分鐘,掏出了手機(jī)。然后是兩個(gè)穿格子衫的年輕人,大概是隔壁寫字樓的程序員——其中一個(gè)舉起了手機(jī),嘴巴張著,沒有合上。接著越來越多。有人在打電話,有人在大聲念出那行注釋里的日期和署名。
樓下保安仰頭看了半天。物業(yè)的人來了,敲門。我沒有開。他們報(bào)警。
民警來了兩個(gè),一老一少。老的走進(jìn)我辦公室,看了看窗邊那四臺(tái)投影儀,又看了看對(duì)面玻璃幕墻上滾動(dòng)的代碼。他轉(zhuǎn)過頭看我:「這玩意是你弄出來的?」
「是?!?/p>
「這是什么東西。」
「代碼。我寫的。上面有我的名字和日期。」
他沉默了一會(huì)兒,走到窗邊,盯著對(duì)面墻上的字幕看了大概五分鐘。投影儀嗡嗡地響,一行一行代碼從玻璃上滑過去。他轉(zhuǎn)過來問我:「這違法嗎。」
我說不違法。不是誹謗,不是侵權(quán),代碼是我寫的,注釋是我寫的,我只是在展示一份公開倉庫里的歷史記錄。他斜靠在我那臺(tái)舊投影儀旁邊,又看了半小時(shí)滾動(dòng)字幕,然后站起來說了一句話:「要是人家公司因?yàn)槟氵@行字倒了,算誰的。」
「代碼本身不殺人。觸發(fā)的條件是他自己寫進(jìn)去的,他也未必還記得?!?/p>
老民警看了我一眼。那個(gè)眼神沒有惡意,也沒有同情,只是一種見過太多事情之后的平淡審視。他沒再說話,招呼年輕的那個(gè)走了。走到門口,他回頭看了一眼我貼在墻上的那張紙條,沒讀出聲。他的嘴唇動(dòng)了動(dòng),然后關(guān)上了門。
第二天,寫字樓物業(yè)接到一個(gè)電話。不是投訴。是有人想租我對(duì)面那間更大的辦公室,要求窗戶正對(duì)著那面幕墻。他說他想每天看到那行字。
04
投影開始后的第三天,紀(jì)懷舟沒來找我。
他讓法務(wù)發(fā)了一封律師函,說我「干擾正常商業(yè)活動(dòng)」,要求立即停止投影。我把律師函掃描成PDF,第二天把它投在了他的玻璃幕墻上。函里有一行字被我不小心放得特別大——「貴方行為已構(gòu)成不正當(dāng)競爭」。我把「不正當(dāng)競爭」五個(gè)字排在那行BUG代碼的旁邊,字體放大到和代碼注釋一樣的大小。路過的人越來越多。有人在拍照,有人錄視頻發(fā)到網(wǎng)上。一個(gè)穿格子衫的年輕人站在對(duì)面樓下,仰頭看了五分鐘,嘴里念叨著什么。然后他轉(zhuǎn)身走了——走的方向是云帆企服的大門。他大概是被叫來改BUG的。
第五天,出事了。不是紀(jì)懷舟的事——是他客戶的事。
云帆企服的核心產(chǎn)品是一套物流調(diào)度SaaS系統(tǒng),賣給中型物流公司。系統(tǒng)底層有一個(gè)自動(dòng)分配運(yùn)力的算法模塊,是核心中的核心。而這個(gè)模塊的底層,就藏著十年前那行BUG。觸發(fā)條件很簡單——單日訂單量超過一百萬。過去十年,沒有客戶超過這個(gè)量。
直到上個(gè)月,紀(jì)懷舟簽了「通遠(yuǎn)物流」——全國前二十的物流公司。通遠(yuǎn)物流的單日訂單量,峰值一百三十萬。
系統(tǒng)開始崩潰。不是一次性崩潰,是間歇性。運(yùn)力分配出錯(cuò)——明明是往華東的貨,系統(tǒng)把車派到了西北。時(shí)間戳錯(cuò)位——已經(jīng)簽收的訂單重新出現(xiàn)在待發(fā)貨列表里。調(diào)度結(jié)果延遲,整個(gè)鏈路出現(xiàn)間歇性斷裂。通遠(yuǎn)物流的研發(fā)團(tuán)隊(duì)緊急排查,兩天兩夜沒合眼,從底層模塊一層一層往上翻,最后找到了那行BUG。他們?cè)噲D修改。改了三版,每一版都在測(cè)試環(huán)境里把別的模塊帶崩。那行代碼背后的算法是一個(gè)自定義變種,用一種非常冷門的遞歸結(jié)構(gòu)寫的——不是標(biāo)準(zhǔn)模式,是蘇默白大四實(shí)習(xí)時(shí)自己寫的一個(gè)算法變種。改一行會(huì)引發(fā)整個(gè)模塊的邏輯鏈條斷裂,像拆一個(gè)自己會(huì)動(dòng)的炸彈。
通遠(yuǎn)物流的電話打到了云帆企服的客服部。語氣從客氣變成冷硬。他們說如果在兩周內(nèi)修不好,合同終止,追償違約金。違約金是多少不重要。重要的是通遠(yuǎn)一旦解約,其他客戶會(huì)跟著走。
紀(jì)懷舟知道這一點(diǎn)。他開始打電話。不是打給我。打給以前公司的技術(shù)骨干,一個(gè)一個(gè)問能不能改這行BUG。我在前同事群里的消息記錄里看到有人發(fā)了一句:「懷舟昨晚打了四個(gè)電話,語氣一個(gè)比一個(gè)急。」沒人能改。那行BUG是蘇默白寫的,只有蘇默白能改。
他最后打給了我。
05
他約我在樓下的咖啡廳見面。
同一家咖啡廳,同一種美式。這次他先到了,坐在靠窗的位置,桌上沒有文件,只有兩杯冒著熱氣的咖啡。他比上次瘦了,襯衫領(lǐng)口松了一指,眼睛里血絲密布,下巴上有沒刮干凈的胡茬——紀(jì)懷舟從來不在見人之前不刮胡子。他叫我默白,不是蘇默白。默白是我們大學(xué)時(shí)互相叫的稱呼。他在試圖把一個(gè)被背叛的合伙人拉回室友距離。
他說他可以給我公司百分之十五的股份,簽正式協(xié)議,這次簽真的,有律師公證。我說不要。他又說,或者一筆現(xiàn)金,具體數(shù)額讓我自己填。他掏出支票簿,放在咖啡杯旁邊,鋼筆擱在支票簿上。我說不要。他沉默了一會(huì)兒。窗外能看到對(duì)面寫字樓的玻璃幕墻,那行代碼還在滾動(dòng)。從咖啡廳的窗戶往外看,剛好能看到那行代碼投在云帆企服的logo上方。
「那你到底要什么。」
我說:「你站在你的客戶面前,親口告訴他們。這行代碼是誰寫的,誰刪了誰的提交記錄,誰把創(chuàng)業(yè)的合伙人踢出了他一手創(chuàng)建的公司?!?/p>
他不說話了??Х葲隽?。奶精在液面上凝成一層薄膜。
法務(wù)在旁邊,還是那個(gè)灰西裝的。他在桌下用手機(jī)發(fā)了一條消息。過了一會(huì)兒,紀(jì)懷舟的手機(jī)亮了。他低頭看了一眼,臉色變了。他手機(jī)屏幕上是我剛發(fā)布的一個(gè)公開技術(shù)文檔——里面有那行BUG的完整剖析,包括觸發(fā)條件和歷史提交記錄。那些被刪掉的Commit記錄全部掛在文檔里,時(shí)間戳、修改內(nèi)容、原始作者署名。我把它們從舊倉庫里一條一條找出來,一個(gè)一個(gè)放在公開頁面上,標(biāo)簽就打在頁頭:「已知技術(shù)債務(wù),原作者記錄。」
他盯著手機(jī)屏幕足足一分鐘。然后把手機(jī)翻過來放在桌上,抬起頭看著我。他眼睛里第一次出現(xiàn)了一種東西——不是憤怒,是恐懼。他怕的不是這行BUG。是不知道我還有多少行這樣的代碼、多少條存著的記錄、多少個(gè)十年沒刪干凈的注釋。
法務(wù)在旁邊敲電腦,敲了幾下停了。大概是在查我公開倉庫有沒有侵犯知識(shí)產(chǎn)權(quán)——沒有。代碼是我寫的,十年前寫在私人倉庫里,不是公司的。我展示的也是公開倉庫,時(shí)間戳不可篡改。
我起身走了。走到門口的時(shí)候,紀(jì)懷舟在我身后說了一句:「你十年前就防著我?!?/p>
我沒回頭。推開咖啡廳的門,玻璃上映著對(duì)面寫字樓幕墻上的代碼。它還在滾。
06
他查了所有備份。找到了那個(gè)GitHub倉庫的時(shí)間戳。
那天晚上他獨(dú)自來了我的辦公室。沒有法務(wù),沒有咖啡,沒有開場(chǎng)白。門沒鎖,他直接推門進(jìn)來。雨傘上的水一滴一滴滴在地板上。外面又下雨了。
他站在門口。我坐在窗邊,身后四臺(tái)投影儀還在工作,嗡嗡的聲音填滿了整個(gè)房間。他的臉被投影儀的藍(lán)光照亮,一半明一半暗。他說這行BUG不可能是意外留下的。十年前我們的產(chǎn)品還在校園測(cè)試階段,用戶量不到一千,根本不可能想到有一天會(huì)有百萬訂單。想得到的不是產(chǎn)品——是我寫的那個(gè)遞歸變種算法,它比我上過的任何編程課都更難讀,但唯獨(dú)這行BUG,夾在用不上它的年代里,卡在一個(gè)永不可能被觸發(fā)的條件上。他不明白我為什么在根本沒有用戶的時(shí)候就埋了這個(gè)東西。
「那個(gè)算法,」我說,「你不是一直說它整段都長得像另一個(gè)時(shí)代的遺產(chǎn)嗎?!?/p>
他愣了一下。然后他的臉白了——白到投影儀的藍(lán)光打在他臉上都沒有血色。他從我的語氣里聽出來了。聽出了這行BUG是什么。
這行BUG不是意外。是我十年前故意留的。
不是我預(yù)知了背叛。是我不信任何人,包括我自己。我在每一個(gè)重要項(xiàng)目里都會(huì)留一行只有我自己能解的鎖——不是什么高級(jí)后門,是如果我被拿走作品的署名權(quán),它就是活的證據(jù)。我把命名規(guī)則嵌進(jìn)了一段沒人能讀懂的遞歸,外人不碰它,相安無事。一旦有人試圖刪掉我的名字,它就等著某一天被人翻到,把時(shí)間戳和觸發(fā)條件一起擺出來。十年前我寫了這把鎖,十年后他自己撞了上去。我沒有上他的鎖。是他用他刪掉的那些簽入記錄,撬開了我十年前埋的保險(xiǎn)柜。
他扶著門框。雨聲很大,但他的呼吸聲比雨聲還重。投影儀把代碼一幀一幀投到他背后的墻上,白色的字符從他肩頭滑過去。幾年前我最后一次和他一起寫代碼的時(shí)候,他坐在我旁邊,說你這人寫注釋總是太長。我說注釋不是寫給機(jī)器的,是寫給以后看代碼的人的。他問以后是誰。我說以后是任何人。
現(xiàn)在這個(gè)任何人是他自己。
我站起來,投影儀的光從我背后投過來,把他的影子拉得很長,投射在對(duì)面的百葉窗上——就是那扇窗背后他明天就要跟客戶解釋為什么連自己的核心代碼都改不了的地方?!改氵€想讓你的客戶聽見什么。我可以幫你投上去。」
他沒有回答。他的手抓著門框,骨節(jié)泛白,像抓住一塊浮木。然后他松開了手,退后半步,走進(jìn)了走廊的黑暗里。門在他背后緩緩合上。投影儀的嗡嗡聲填補(bǔ)了整個(gè)房間。