為什麼估算這麼難——也這麼重要#
Standish Group 在 2009 年研究 5 萬多個軟體專案後發現: 44% 的專案延遲、超預算或缺少需求;24% 完全失敗; 平均延遲的專案,超出原始時程 79%。
Ooyala 在 2008 年要花 4 個月重寫 Flash 播放器,最後花了 9 個月才上線。 Netscape 5.0 延遲兩年,市占率從 80% 跌到 20%;Windows Vista 延遲超過 3 年;遊戲 Daikatana 延遲到讓公司倒閉。 這些故事都共有一個教訓:估算不準,會直接影響業務存續。
估算 vs. 目標#
Steve McConnell 在《軟體估算》中區分兩個常被混淆的概念:
- 估算(Estimate):工程師對「實際需要多久」的最佳猜測
- 目標(Target):管理者對「希望什麼時候完成」的商業期望
專案延遲常因為「讓目標扭曲估算」:上頭說 3 個月內必須做完,工程師就硬把 4 個月的工作擠進去。 正確做法應該反過來——讓估算去引導商業決策。
建立準確估算的十項策略#
- 拆解到細粒度任務:超過兩天的估算就再拆。長估算是壞驚喜的藏身處
- 以「實際需要」估算,而非「希望」:管理者常以 Parkinson’s Law 壓縮時程,但細粒度估算讓你更容易守住底線
- 把估算視為機率分布:與其說「6 週完成」,不如說「50% 機率 4 週內、90% 機率 8 週內完成」
- 讓真正要做的人來估:在 Ooyala 是少數資深工程師估全隊的工作量;讓執行者自己估更貼近真實
- 小心錨定效應:Dan Ariely 的實驗顯示,社安號末兩碼會影響受試者對紅酒價格的估算。先估再討論,別讓主管的隨口一說定錨
- 用多種方法交叉驗證:由下而上 + 歷史資料 + 子系統平均工時,三者比對
- 警惕人月神話:增加人手會以平方速度增加溝通成本,新成員還需上手時間。增援不會等比例縮短時程
- 用歷史資料校準:如果你過去總低估 20%,下次估算後乘 1.25
- 用 timebox 限制可膨脹任務:開放性研究若無上限,會無止盡擴張
- 讓他人挑戰你的估算:團隊一起評審能補上你看不到的洞
在某個 Python 轉 Scala 的專案中,作者請每位成員用試算表記錄「估計時數 vs. 實際時數」。多數人初期會低估約 2 倍,但兩週內所有人都能更準確估出每週能搬幾行程式碼——這份洞察讓後續里程碑變得可靠許多。
為「未知數」預留緩衝#
Frederick Brooks 在《人月神話》中寫道:「重大專案延遲,通常不是被龍捲風摧毀的,而是被白蟻啃掉的。」
Ooyala 的 4 個月變 9 個月,不是因為單一大事故,而是無數小決定的複利效應:
- 為了採用 Thrift 協議,自己寫 ActionScript 編譯器外掛
- 第三方廣告模組有 Bug、影片播放底層 API 不穩
- 高優先客戶業務臨時插單、早期工程師離職
- Subversion 遷移到 Git
- 改版 UI 元件、撰寫測試框架……
區分「工時」與「日曆時間」#
「這專案需要一個月的工程量」不等於「這專案會在一個月內完工」。 工程師日常還要處理 Bug、會議、面試、On-call、回信、帶新人…… 真正能投入的工時遠少於 8 小時。
Asana 工程經理 Jack Heart:團隊規則是「1 個理想工程日 ≈ 2 個實際日曆日」。 Dropbox 平台基礎建設主管 Alex Allain 則會用試算表逐週列出時程、誰做什麼、把假日請假都標出來,做輕量級的合理性檢查。
定義具體目標與可衡量的里程碑#
Box 工程師 Tamar Bercovici 在 2012 年帶領團隊把單一 MySQL 資料庫 sharding,處理每天 17 億查詢。 她的成功心法:先把目標講得極度具體——「儘快遷移到 sharded 架構,且不能停機」。
具體目標的兩大價值#
- 過濾必要 vs. 加分項:避免功能蔓延(Feature Creep)。Bercovici 一開始想順便重寫資料存取層以禁用任意 SQL 過濾,但對照目標後判定「不在範圍內」,留到後續處理
- 建立利害關係人對齊:避免快上線時被主管問「那 X、Y、Z 怎麼沒做?」
具體目標範例:
- 把首頁 P95 延遲降到 500 ms 以下
- 推出能依內容類型過濾的搜尋功能
- 把服務從 Ruby 移到 C++ 以提升效能
可衡量的里程碑#
Bercovici 的 sharding 專案分為四個里程碑:
- 重構讓 file/folder 查詢能 sharding(如把單庫 JOIN 改為應用層 JOIN)
- 邏輯上 shard 應用(走查 shard 但仍存取單一資料庫)
- 把單一 shard 移到另一個資料庫
- 完整 shard 所有帳戶的 file/folder 資料
訂目標像「鋪賽道」,訂里程碑像「沿路立里程碑」——你才知道是否還在原訂節奏上。 落後時,里程碑就是「重新調整計畫」的時機。
儘早降低風險(Reduce Risk Early)#
工程師喜歡建東西、看見進度。這種偏好讓我們傾向先做容易的部分,假裝專案按時推進——但風險其實還沒兌現。
Square 工程經理 Zach Brock 的口頭禪:「這專案最可怕的部分是什麼?先做那個。」
兩種風險來源#
- 特定風險:用新技術、用新基礎設施、新演算法的不確定性
- 解法:先做小規模端對端 prototype、效能 benchmark,把估算誤差縮小
- 整合風險:所有大型專案最常爆炸的階段
- 程式碼複雜度隨「互動次數」增長(不只是行數)
- 早期就先搭好 end-to-end scaffold,stub 出未完成的模組,讓你能在開發過程中持續整合測試,把整合成本分攤而不是壓在最後
重寫專案要極度謹慎#
Google Apps 工程主管 Sam Schillace 認為工程師最大的代價錯誤是:「從零開始重寫,這是首罪。」
為什麼重寫總是失敗#
- 與其他專案一樣存在估算困難,但因為「我們熟悉舊版」,往往低估得更嚴重
- 重寫過程中誘惑滿地:「順便重構、優化效能、改 API……」
- 重寫期間,新功能要不延後上線,要不在舊版和新版各做一份——時程越拉長代價越高
Frederick Brooks 稱這現象為 第二系統效應(Second-System Effect):第一次做時謹慎、簡單;第二次設計就會塞進當時故意捨棄的所有想法。
替代做法:增量重寫#
成功的重寫者,都把大重寫拆成一連串小重寫:
- Ooyala 後續案例:把 Flash 內容管理系統遷到 HTML5 時,先建混合架構支援 HTML5 元件嵌入 Flash 應用,逐元件遷移
- Lob API 案例:建一個 Proxy 伺服器選擇性把流量路由到舊或新 API,能逐端點遷移與切換
- Writely → Google Docs:四人團隊先把 C# 翻譯到 Java(用 regex 批次轉換 + 修補幾十萬個 compile error),跑起來再開始重構。12 週內完成,創下 Google 收購團隊最快遷移紀錄
增量重寫雖然總工時可能多一些,但因為每階段都可停下來改路、應變突發需求,綜合風險低很多。
不要在馬拉松中段衝刺#
作者親歷兩次:團隊以為「再衝最後一哩」,把工時從 60 拉到 70 小時,幾個月後發現原來才跑到馬拉松中段。
為什麼加班解決不了問題:
- 單位時間產出下降:百年研究都指向相同結論。1922 年 Henry Ford 改為 40 小時工作週反而提升總產出
- 你比想像中還落後:時程已落後代表前段被低估;剩下的也很可能被低估
- 燒掉團隊:加班後通常會有「補休期」相互抵銷
- 加深溝通成本:截止日近,會議和狀態回報激增,剩下能寫程式的時間更少
- 誘發技術債:為了趕里程碑,幾乎不可避免地走捷徑
真有必要加班時的紀律#
- 釐清「之前為什麼會落後」——是放鬆還是真的被低估?預期接下來會不會也低估?
- 重新訂出符合現實的修正版計畫與里程碑,能在中途偵測再落後
- 一旦再次落後就果斷停止衝刺,承認你跑錯方向,而不是再加班
重點摘要(Key Takeaways)#
- 把估算當成專案計畫的輸入:別讓「想要的目標」反過來操控估算
- 為未知預留緩衝:競爭工作、假期、生病、突發事故都會發生
- 定義可衡量的里程碑:用里程碑判斷是否落後,並重新校準估算
- 先做最危險的任務:減少估算變異與專案風險,別被「容易的進度」假象矇蔽
- 了解加班的極限:只在有可信修正計畫時才加班,否則只是把馬拉松當衝刺