第 2 章:第二章:五天前——星期一的會議
會議室的中央空調在頭頂嗡嗡作響,冷氣出風口正對著陳志明的後颈。他无意識地轉了一下椅子角度,避開那股涼意,視線落在投影幕上。
「電商結账系統重構,時程六周。」
林副總用雷射筆點著投影片上的甘特圖,語氣像在念一份已經定案的公文。會議室裡坐著工程部第二小組的全部八個人,加上產品經理小琪,還有兩個志明不太熟的 QA。
「這個案子很重要,」林副總放下雷射筆,環視一圈,目光最後停在志明身上,「志明,你來做技術負责人。」
志明點了一下頭。他早就猜到了——這種跨組整合的案子,副總是不會交給別人的。
「技術選型、架構、時程控管,你全權處理。」林副總補了一句,嘴角掛著那種熟悉的笑容,「這件事你看著辦,我相信你的判斷。」
這句話落在會議室裡,像一顆石子丟進平靜的水面。志明感覺到左側有一道目光,不用看也知道是老張。
「好。」志明說。
會議結束後,人群三三兩兩地散去。志明收拾筆記本電腦的時候,發現大衛還坐在位子上,盯著自己的萤幕,手指在桌面上輕輕敲著。
「怎麼了?」志明走過去。
大衛抬起頭,犹豫了一下:「Zhi哥,你決定用哪個金流套件了嗎?」
「還在評估。」志明拉了把椅子坐下,「目前看起來有兩個選項。一個是老牌的,穩定但文件舊。另一個是新的,社群活躍,Laravel 10 原生支持。」
「新的那個是 pay-bridge/payment-kit?」
「對。」
大衛皺了皺眉:「我昨天有看一下它的 GitHub……有幾個 Issue 在討論 Race Condition 的問題。」
志明注意到,當大衛說出「pay-bridge/payment-kit」這個名字的時候,坐在會議室角落裡的老張,手指在桌面下微微一頓——不是敲桌子的那種頓,是一種下意識的、像是被什麼擊中後的停頓。老張的目光從自己的筆電萤幕抬起來,掃過大衛,掃過志明,然後又低下去了。
什麼都沒說。
「哪個 Issue?」
「#247。標題是『Duplicate charge under concurrent requests』。」大衛把瀏覽器打開,拉到一個頁面,「這裡。有人測到在同時收到兩筆 webhook callback 的時候,订單狀態更新會有競態問題,導致重復扣款。」
志明凑過去看。Issue 的描述很詳細,還附了 log 截圖。發 Issue 的人說他在 production 環境遇到了這個問題,已經影響了幾十個客户。
「這個 Issue 多久了?」
「開了一年半。」大衛滚動頁面,「狀態是 Open,maintainer 最後一次回是八個月前,說『我們會看』,然後就沒了。」
志明沉默了幾秒。會議室的空調聲突然變得很大。
「另一個選項呢?」大衛問。
「另一個要自己刻,至少多兩周。」
大衛沒說話。他不需要說——兩周對六周的時程來說意味著什麼,兩個人都清楚。
「先用了,」志明把筆電蓋上,「有問題再說。」
大衛張了張嘴,又閉上了。他點點頭,站起來走了。
志明走到茶水間倒咖啡的時候,老張正靠在窗邊抽煙。公司的禁煙區在茶水間外面,但老張從來不遵守。
「副總的『我相信你』,」老張吐出一口煙,「聽起來真舒服。」
志明沒接話,把咖啡機的杯子拿出來。
「六周,」老張弹了弹煙灰,「用那個有 Race Condition 的套件。你算過風險嗎?」
「我還沒決定——」
「你刚才在會議上說『先用了有問題再說』,」老張打斷他,「我在場。」
咖啡太烫,志明喝了一口,舌尖發麻。
「老張,你如果有更好的方案,現在說。」
老張把煙按滅在窗臺上的寶特瓶裡,那個瓶子裡已經有十幾根煙蒂,液面發黃。
「沒有。」老張說。
他走了。
下午兩點,志明在 Slack 上發了一條消息到團隊群組:
> 技術選型確認:金流套件用 pay-bridge/payment-kit。時程不變,六周後上線。大家開始準備。
小琪回了一個 OK 的手勢。
大衛沒有回復。
老張的頭像亮著,但沒有反應。
志明關掉 Slack,打開編輯器,開始寫 project skeleton。Laravel 的 Service Provider、PaymentService、WebhookController、Queue Job——架構在他腦子裡已經跑過很多遍了。他寫得很流暢,手指在鍵盤上幾乎沒有停頓。
寫到一半的時候,他停下來,打開瀏覽器,又看了一次那個 GitHub Issue。
Issue #247 的頁面還開著。最下面有一則留言,三天前留的:
> Any update on this? We're seeing it in production more frequently now.
沒有回應。
志明把瀏覽器關掉。
晚上七點半,辦公室裡只剩下志明和兩三個加班的同事。他站在落地窗前,看著樓下的車流。手機震動了一下,是雅婷傳的訊息:
> 今天幾點回來?
志明打字:
> 大概九點。
他看著這行字,又加了一句:
> 對不起,最近比較忙。
雅婷秒回:
> 沒關系。女兒睡了,我留飯給你。
志明把手機收起來,坐回座位。他打開終端機,跑了一下 composer require pay-bridge/payment-kit,看著依賴項一個個被下載、安裝。終端機跳出绿色的 Package installed successfully。
他盯著那行字看了很久。
Package installed successfully。
綠色的字,看起來一切正常。但志明不知道為什麼,胃裡有一種說不上來的感覺。不是後悔——還沒有到後悔的程度。更像是一種預感。就是你站在月臺上,火車還沒進站,但你已經聽到鐵軌傳來的那種遠遠的震動。
他把終端機關掉。
## 如果志明說了不
會議室裡的空調還是在響。
如果——如果志明在那個當下,看著大衛打開的那個 GitHub Issue,做了不一樣的選擇。
「這個 Package 有風險,」如果志明說了,「我需要多兩周做壓力測試。」
林副總的表情會是什麼?
志明閉上眼睛,幾乎可以看見。
副總會先沉默。不是那種「我在思考」的沉默,而是那種「你怎麼不配合」的沉默。然後他會說:「好吧,但你要對這個決定負责。」
這句話和「我相信你的判斷」一樣輕,但重量完全不同。
「你要對這個決定負责」——意思是:如果後來出了事,是你選的,不是我逼的。
而「我相信你的判斷」——意思是:如果後來出了事,是你決定的,我只是信任你。
兩種說法,责任歸属一模一樣。但前者會讓志明在會議上被貼上「不配合」的標簽,後者不會。
如果志明說了「不」,六周變成八周。八周的時程,副總不會說什麼,但工程部的周會上,別的 Team Lead 會怎麼看?「志明的案子延期了」「他是不是估時程的能力有問題?」
這些話不會當面對他說,但會傳到副總耳朵裡。然後副總在績效評估的時候,會在「領導力」那一栏打一個 B+ 而不是 A。
然後年底調薪的時候,志明的幅度會少 2,000 塊。
2,000 塊。雅婷的孕婦營養品、女兒的幼稚園學費、車貸。
如果志明說了不,他會準時回家。女兒不會在九點半還等爸爸。雅婷不會挺著七個月的肚子一個人幫女兒洗澡然後哄睡。
如果志明說了不,六周變成八周,但系統不會有 Race Condition。不會有重復扣款。不會有記者會。不會有凌晨三點的崩潰。
如果志明說了不。
但志明沒有說。
會議室裡,他看著副總的笑容,點了頭。
「這件事你看著辦,我相信你的判斷。」
「好。」
這個「好」字很輕。輕到志明後來回想的時候,幾乎聽不見。
但它落下來的地方,比他自己以為的要深得多。