工程師忽略的隱形成本
原文地址(source):http://www.theeffectiveengineer.com/blog/hidden-costs-that-engineers-ignore
有時候我們說,“實現(xiàn)這個功能,我只花了幾個小時”。但是完成之后,我們發(fā)現(xiàn)每隔幾周,我們要么在修復(fù)該功能的bug、向另一個工程師解釋,要么做客服回答問題、以解釋其工作原理。維護該功能總的投入時間要遠遠超過最初開發(fā)的幾個小時。
軟件開發(fā)中內(nèi)化的最艱難教訓(xùn)之一就是額外復(fù)雜度所帶來的隱形成本。有時候,復(fù)雜度在問題領(lǐng)域只是固有的。為了匹配乘客和司機,通過調(diào)整價格來平衡供求是一個復(fù)雜和痛苦的問題。因此,在擴大一個社區(qū)和維護社區(qū)質(zhì)量的時候,把問題和答案疏通到喜歡回答和看問題的人們那里,也是如此。或者像是開發(fā)一個兼容所有設(shè)備的富文檔編輯器以支持實時協(xié)作。這是固有的復(fù)雜度,我們需要根據(jù)產(chǎn)品做出調(diào)整以取得成功。
但是其它時候,和我們較勁的復(fù)雜度恰恰是我們自己產(chǎn)生的復(fù)雜度。我們用新編程語言寫代碼,很少人了解它,現(xiàn)在我們不得不維護它。或者我們增加了額外的基礎(chǔ)架構(gòu),因為我們嘗試從Hacker News看到的、熱門新技術(shù),但是它失敗了,這是我們當(dāng)初沒有想到的。或者我們引入了一個很少人使用的功能,但是修復(fù)和bug報告就花掉了極不對稱的大把時間。
額外的復(fù)雜度暴露了很多隱形成本。在開發(fā)軟件時,我們所做的決定不只是決定了我們當(dāng)前的開發(fā)速度。它們還要反映出我們花在維護上的時間和努力程度。
復(fù)雜度的隱形成本
太多復(fù)雜度增加了認知負荷,并產(chǎn)生了做完事情的額外阻力。它以很多不同的方式滲入到團隊里——大部分是直接滲入到代碼、系統(tǒng)和產(chǎn)品復(fù)雜度里,但是間接地滲入到了組織的復(fù)雜度里。我們逐個看看這幾種不同類型復(fù)雜度的隱形成本。
代碼復(fù)雜度
代碼復(fù)雜度不只是隨著代碼行數(shù)的線性函數(shù)而增長——它組合式地增長。在復(fù)雜的代碼庫里,每行代碼可能與其它很多行代碼交互和影響。我們對于組合式增長難以有足夠的認識,這就是為什么我們傾向于嚴重低估完成大型軟件項目所需要的時間。這也是重寫項目有時候會大幅延期的主要原因。
當(dāng)代碼過于復(fù)雜的時候,它將變得難以擴展、難以理清其中緣由、難以修復(fù)bug,很難理清追蹤錯誤來源的依賴和數(shù)據(jù)流向。工程師或許會積極地避免代碼庫最復(fù)雜的部分,即使它是可以做某種修改的、最有邏輯的地方,也要選擇繞彎來解決。他們或許避免把那些地方都組合起來,即使這項工作有著很大的影響。
系統(tǒng)復(fù)雜度
工程師喜歡擺弄新玩具,要么因為好奇,要么因為他們認為新技術(shù)可能為解決他們的緊迫問題提供了銀彈。當(dāng)Pinterest在2011年剛開始擴容網(wǎng)站以應(yīng)對快速增長時,他們只有3個工程師的后端小組卻使用了6種不同的存儲技術(shù)(MySQL、Cassandra、Membase、Memcache、Redis和MongoDB)。他們實驗每項新技術(shù)的諾言都是解決現(xiàn)有系統(tǒng)的某些限制。但是,每種新解決方案都以其自身特定方式失敗了,為了管理和維護而投入了更多時間和努力。最終,團隊明白了,增加更多機器而不是更多技術(shù),更能簡化擴容,因此他們消除了Cassandra和MongoDB之類的系統(tǒng),強化了架構(gòu)的已有組件。
把基礎(chǔ)架構(gòu)切分為太多系統(tǒng),會帶來很多隱形成本。注意力被分散到了多個系統(tǒng)。對于每個系統(tǒng)來說,更難以整合資源以開發(fā)可復(fù)用的資源庫,更難以為日常工作招聘新人,更難以理解具體的失敗模式和每個系統(tǒng)的性能特點。每個系統(tǒng)的抽象最終變得更弱,因為沒有可投入的太多時間。當(dāng)工具和抽象太復(fù)雜、或太多的時候,讓團隊去理解和探索將變得困難。
產(chǎn)品復(fù)雜度
產(chǎn)品復(fù)雜度可以導(dǎo)致一個不明確的版本、或引發(fā)缺乏產(chǎn)品聚焦的無節(jié)制野心。它希望在很多地方是優(yōu)秀的,而不只是在一個核心領(lǐng)域,這種欲望使其不能向新用戶明確地解釋產(chǎn)品的意圖。產(chǎn)品復(fù)雜度引發(fā)了更多的代碼和系統(tǒng)復(fù)雜度——團隊增加更多代碼、更多基礎(chǔ)架構(gòu)以支持新功能。當(dāng)產(chǎn)品外圍寬泛時,增加一個新功能或修改現(xiàn)有功能,將需要放大很多的努力來理解和適應(yīng)舊的功能。
過于復(fù)雜的產(chǎn)品意味著有更多的代碼分支,更多要考慮的問題、更多的需要團隊解決的bug反饋。工程師和數(shù)據(jù)科學(xué)家需要分析更多的變量、做更多的一次性的報表,而不是集中于核心用戶行為的理解上。工程師需要投入更多時間來提供功能空間和提高效率。每個人最終在更多的項目中進行切換。投入在維護所有這些功能上的時間,并不是重新投入代碼、償還技術(shù)債務(wù)、加固抽象的時間。
組織的復(fù)雜度
代碼、系統(tǒng)和產(chǎn)品復(fù)雜度,依次產(chǎn)生了組織的復(fù)雜度。團隊需要雇傭更多人來處理和維護已開發(fā)的所有東西。越大的團隊意味著越多的溝通成本、越多的協(xié)調(diào)和和越低的總體效率。招聘過程本身,涉及的所有面試和匯報,消耗了團隊很大比例的時間。當(dāng)然,所有新員工不得不被培訓(xùn)才能上崗。
雇傭更多人的替代方法,就是將工程師組成劃分成更小的團隊——或許甚至創(chuàng)建了一人小組——來承擔(dān)較多代碼、系統(tǒng)和產(chǎn)品外圍的工作。這降低了溝通成本,但是一人小組有他們自己的成本。一旦遇到難題,就完全拖延了項目中的唯一人手,因為有更少的人來分享這些低谷期,這種體驗對于士氣是有害的。與其他人合作的機會少了,會傷害到工作場所的快樂和員工的留任。除非每個人比較自覺,而且主動詢問反饋,否則個人收到的工作反饋將更少,因為分享相同項目上下文的人更少了。減少的反饋能夠降低代碼質(zhì)量、或因疏忽導(dǎo)致的復(fù)雜度引入到代碼庫或基礎(chǔ)架構(gòu)里。
如何應(yīng)付復(fù)雜度
Tony Hoare在1980年圖靈獎的演講中建議,“構(gòu)造軟件設(shè)計有兩種方法:一種是簡單,明顯地沒有缺陷;另一種方法是使其復(fù)雜,卻沒有明顯的缺陷。”提到了由于復(fù)雜度而導(dǎo)致的非明顯的缺陷是如何傷害我們的,以及我們該如何應(yīng)對這些成本?
下面是你能夠用到的一些策略:
- 為簡單而優(yōu)化。抵制增加更多復(fù)雜的主張。深思維護成本。要自問,為了解決20%的問題而引入的復(fù)雜是否值得,或者80%的解決方案已經(jīng)足夠了。
- 為你的團隊或產(chǎn)品定義一種任務(wù)說明以統(tǒng)一思想。在Team Geek,Brian W. Fitzpatric和Ben Collins-Sussman解釋了他們是如何輔導(dǎo)Google Web Toolkit(GWT)團隊、并鼓勵他們寫下任務(wù)說明的。接下來發(fā)生的、對于任務(wù)說明的內(nèi)容和形式的爭論,表明了***工程師并不真正認同產(chǎn)品方向!他們被迫面對、協(xié)調(diào)不同、并最終達成了,“GWT的任務(wù)是為用戶徹底提升web體驗,讓開發(fā)者使用現(xiàn)有的Java工具在任意現(xiàn)代瀏覽器里構(gòu)建高性能的AJAX。”如果他們不能盡早找出這些區(qū)別,隨之而來的努力上的分散又有多少呢?
- 用較小的構(gòu)建塊組成大型系統(tǒng)。Google就是個例子,致力于構(gòu)建健壯的核心抽象,然后被非常寬泛的應(yīng)用程序廣為使用。他們有基礎(chǔ)的構(gòu)建塊,像Protocol Buffers、Google File System和遠程程序調(diào)用的Stubby服務(wù)器。基于這些構(gòu)建塊之上,他們還建立了其它抽象,比如MapReduce和BigTable。在此之上,包括大型web索引、Google Analytics站點追蹤、Google News聚合、Google Earth數(shù)據(jù)處理、Google Zeitgeist數(shù)據(jù)分析在內(nèi)的數(shù)以千計的應(yīng)用程序,還有很多程序都是這樣構(gòu)建的。
- 清晰地定義模塊和服務(wù)之間的接口。模塊和服務(wù)的退耦,將減少能夠產(chǎn)生一堆代碼的組合復(fù)雜度。在Amazon,Jeff Bezos于2002年宣稱,公司將轉(zhuǎn)向面向服務(wù)的架構(gòu),所有團隊只能通過服務(wù)層級的接口彼此交流。雖然這個轉(zhuǎn)變造成了本身巨大的開發(fā)成本,但是它強制分離了代碼和服務(wù)背后的邏輯,為現(xiàn)在極度成功的Amazon Web Services的建立提供了便利。
- 優(yōu)先償還技術(shù)債務(wù)。我們總是在信息不完全的條件下開發(fā)軟件。做為條件變化的響應(yīng),代碼庫在增大,熵也在增大。增加的復(fù)雜度成為了未來開發(fā)的代價。在開發(fā)日常上預(yù)算時間可以減少這項成本。很多工程師和團隊在項目之間預(yù)算這項時間,不過召開一次性的會議會有幫助。我過去在Quora組織過一次Code Purge Day(代碼消除日)活動,工程師在這一天全部關(guān)注刪除無用代碼的工作。我們在積分牌上追蹤代碼消除的進度,這使得刪除你自己的代碼更有趣味。
- 使用數(shù)據(jù)修剪沒用的功能。在Yammer,當(dāng)工程師或產(chǎn)品經(jīng)理發(fā)現(xiàn)在代碼重構(gòu)時,強化或保留一個功能將花費不菲的時間時,他們將查看使用數(shù)據(jù),以確定這項功能是否真正被使用了。如果沒有,他們將和團隊一起決定,他們是否應(yīng)該只是砍掉這個功能以降低總體工作量。與簡化的代碼是怎樣減少技術(shù)債務(wù)一樣,這個策略也減少了技術(shù)債務(wù)。
- 基于主題對進行中的項目分組。使同事彼此分享同樣的環(huán)境,更容易地參與到設(shè)計討論、代碼評審或構(gòu)建可復(fù)用的資源庫。所有這些活動有助于提供檢查和平衡掉單個人或其他人所引發(fā)的問題。
當(dāng)我們?yōu)閷W(xué)校課程開發(fā)軟件時,我們有著世界的過于簡單的視角——維護任意復(fù)雜度的成本隨著下課而消失了。但是在我們的職業(yè)生涯中,糟糕的軟件決定將產(chǎn)生數(shù)年負擔(dān)的影響。
不要使事情復(fù)雜化。
原文鏈接:http://www.labazhou.net/2015/01/hidden-costs-that-engineers-ignore/