天天看點

《Microsoft.NET企業級應用架構設計(第2版)》——2.2 軟體項目的機制

本節書摘來自異步社群《microsoft.net企業級應用架構設計(第2版)》一書中的第2章,第2.2節,作者: 【意】dino esposito(埃斯波西托) , andrea saltarello(索爾塔雷羅)著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

如果你問:“什麼導緻項目失敗?”,你得到的最常見的回答可能會把失敗歸咎到與業務有關的問題,比如說,缺少需求,項目管理不到位,成本估算不正确,缺少溝通,甚至各個團隊的人員互相不配合。你很難看到壞代碼可能導緻問題這種情況。

有鑒于此,我們認為未被發現的bbm可以嚴重損害軟體項目,但未能處理的bbm卻可以真的毀了它。

最終,個體以及個體之間的實際互動才能真的決定軟體項目的成功或失敗。但是,組織結構及其整體文化也會影響最終結果。

2.2.1 組織文化

apple公司的組織看起來很受“一人秀”(one-man-show)創意的啟發,至少在史蒂夫·喬布斯時代是這樣。一個人推動創意和政策,所有團隊支援和實作這個政策。隻要這個創意是偉大的,政策是合适的,成功就會到來。

你可能還記得發生在2008年的一件事,當時microsoft内部的兩個組出品了兩個幾乎等同的架構—linq to sql和entity framework。外界很難了解是什麼原因導緻了這樣的情況。

注意:

在walter isaacson寫的史蒂夫·喬布斯傳記裡,你可以讀到關于按部門劃分公司的一段非常有趣的見解。史蒂夫·喬布斯分享了他對為什麼是apple而不是sony在ipod的創意上取得成功的看法。為了實作創意,apple首先要建構硬體和軟體,然後就音樂的版權進行談判。sony這樣的公司在硬體和軟體上擁有的經驗至少與apple一樣,此外,自家已經擁有音樂和電影的版權。那麼,為什麼sony沒有建立ipod業務呢?

根據喬布斯的看法,sony的文化是在公司裡擁有多個分部,每個分部都有自己的盈利/虧損賬戶。或許,從音樂版權獲得盈利的分部認為從mp3播放器賺錢的分部是一個威脅。兩個分部互相打架而不是為了公司的成功共同努力。這本書是walter isaacson寫的《steve jobs》(simon & schuster,2011)。

1.團隊和隊員

有一個笑話是關于一個意大利隊和一個德國隊參加八人劃船比賽的。德國隊隻有一個領隊,其餘都是隊員,他們赢了這場比賽。意大利隊調查輸掉的原因,發現他們的隊隻有一個隊員,其餘都是領隊。

團隊就是讓在技能上互補的人們互相合作。

劃船隊笑話的後續是讨論意大利隊怎樣計劃報複賽,但這可能超出本書的範圍了。不管怎樣,如果你好奇我可以告訴你,意大利隊解雇了隊員,重組隊伍,裡面有4個領隊、兩個領隊的上司、一個總司令以及一個新的老隊員(因為他更有經驗)。

在軟體項目裡,管理者和開發者都有自己的目标。管理者的做法比大多數開發者的做法更有壓迫感。開發者向管理者回報,這有時會使開發者更傾向于直接接受任何任務和期限。不應該低估大多數開發者想要成為英雄的本能。開發者期望成為超級英雄,來到這個地球就是為了拯救世界。

舉個例子,假設經理打算在星期一早上給一個潛在的客戶做一場示範。他想確定他的示範可以給人留下深刻的印象。是以經理找到開發團隊,要求在星期一準備一個示範。這可能打破目前沖刺(sprint),甚至影響了原來計劃的工作。在公司或者團隊裡處理這種不可避免的利益沖突使用哪種方式最好?以下是幾個可能的場景。

開發團隊可能嘗試适應新的期限,這樣經理就能實作他自己的正當目标了。

開發團隊不接受新的期限,堅持目前安排,這會妨礙經理讓客戶滿意。

開發團隊和經理一起找到合理的目标,既适合雙方的安排,又不會妨礙任何人實作各自的目标。

根據我們的經驗,第一個場景是最常見的,而最後一個則是最令人滿意的。就開發者嘗試适應任何安排改動而言,我們發現uncle bob的看法特别有價值。承諾嘗試适應新的期限,開發團隊似乎在暗示自己留了一手,就像在說:“是的,我們可以接受更多工作,但出于某種原因我們一直沒有這樣做;現在是時候用盡我們所有精力了。”但是,如果團隊沒有保留精力,嘗試适應更緊的期限會強迫成員加班,犧牲他們自己的私人時間。這對開發者來說是不公平的,他們也該有自己的生活;同樣對管理層來說也是不公平的,他們聽到了謊言。

我們有過多少次嘗試适應更緊的安排?如果我們做過這樣的事,我們可能是為了避免潛在的不禮貌的對抗。成為一個團隊的好隊員的唯一金科玉律是成為一個好的溝通者,永遠坦誠,永不說謊。

這裡的關鍵字是協商,目的是分享合理的目标,在各自需要和安排之間尋求合理的折中方案。就前面的例子而言,一個好的折中方案可能是為某些工作建立分支,使用虛構代碼建立一個僅适用于示範的專門建構。這不會從主分支删除很多内容,不會導緻目前沖刺出現明顯延遲,也不需要團隊成員加班或砍掉特性。

2.scrum救火員

尤其在靈活環境裡,每個沒有預見的事件都是潛在的危機,都需要恰當地、及時地處理。

在scrum裡,常見的做法是賦予團隊的一個或多個成員救火員頭銜。

scrum救火員負責疊代之外保護其他隊員工作所需的任何額外工作。就像現實世界的救火員有時候會空閑一樣,scrum救火員也會空閑,或者在疊代的過程中,在項目上保持最低活躍度。

因為成為scrum救火員可能非常無聊,是以這個角色應該讓團隊的所有成員輪流來做。根據我們的經驗,20%的開發精力應該是你騰出來救火的最大值。由此看來,你可能會想,你的生産力将會縮減20%;實際上,你可能會得到更高的産出。

3.上司與老闆

我們都知道成本是軟體項目的痛點。成本是實作所有特性所需的時間的函數,包括測試、調試、文檔以及一些其他周邊工作。開發團隊上司會負責這些,他通常向項目經理彙報。

有時候,這兩号人物互相之間缺乏信任:經理認為開發團隊保留精力,開發團隊認為經理隻想付出更少而得到更多。

毋庸置疑,上司藝術是關鍵的技能。

經理腰斬估算然後抱怨項目延遲的情況并不罕見。他們跑到老闆那裡指責開發團隊,并要求更多資源。在這種情況下,他們會體驗到brooks法則的效果,即“向已經延遲的軟體項目增加人手會使之更加延遲。”

上司和老闆之間有着巨大差別。

首先,也是最重要的,老闆期望團隊為他們服務,而不是他們為團隊服務。老闆位于商業等級制度的最高點,可以指令其他人執行他們不願意做或者不會做的任務。相反,上司專注于業務以及帶領開發團隊走出深溝。簡單來說,這個差別就是老闆培養跟班,而上司培養其他上司。

2.2.2 幫助團隊更好地寫代碼

我們發現很多開發者似乎認為爛代碼最終并沒有帶來太多傷害。

如果你數一下有記錄在案的因代碼問題而失敗的項目個數,那麼,我們認同這個數字并不會很大。但是,你不必創造真正的災難導緻軟體項目損失大量金錢。

作為一名架構師,你可以做什麼來幫助團隊更好地寫代碼呢?

1.爛代碼真的比好代碼更昂貴

我們不清楚你的情況,但我們肯定認為寫爛代碼真的比寫好代碼更加昂貴。至少,我們認為在生命周期比較長,業務影響比較大的項目裡是更加昂貴的。

聽起來可能很簡單,當使用爛代碼(即建立、測試和維護它)的成本超過業務模型可以忍受的代價時,項目才會因它而敗。同樣地,如果公司設法使代碼的成本保持在極低水準,沒有項目會因為代碼問題而失敗。

這就是痛點。

你如何定義最終影響代碼成本的因素?哪些動作組成了“寫代碼”:編碼、建構、調試?你應該把測試當作一個附加的按需的特性嗎?文檔呢?缺陷修複呢?

有時候,管理者隻是投機取巧,通過雇傭廉價開發者或砍掉測試和文檔等縮減開發成本的手段來解決問題。

不幸的是,這些管理者沒有意識到他們隻是縮減了産生可能(但不一定)工作的代碼的成本。産生剛好可以工作的代碼隻是問題的一面。現在,需求經常改變,複雜性不斷增長,更糟糕的是,複雜性通常僅在行進的過程中才能完全了解。在這種情況下,産生代碼隻是影響總體成本的一個因素。代碼維護和進化才是最大因素。

好的架構師都很清楚,隻有寫得好的代碼,對軟體原則和語言特性有很好的了解,恰當使用模式和實踐,以及注重可測試性才能解決代碼維護的問題。這使得編碼比産生剛好可以工作的代碼更加昂貴,但比維護和進化剛好可以工作的代碼就廉價得多了。

2.使用工具輔助編碼

我們認為成功的項目基于兩個因素:懂得上司藝術的管理層,以及懂得代碼品質的開發團隊。

就編碼而言,不一定有時間讓開發者現在寫代碼,然後在往後的某個時間修複和整理它。每個開發者都會發誓,第二遍處理永遠都不會發生,即使發生,也不會造成很大影響。

如果想在第一次就寫出更好的代碼,最好使用代碼輔助工具。這些工具通常內建在ide裡,可以簡化常見開發任務,使開發者的工作進展得更快,可以寫出更好的代碼。在最壞的情況下,代碼可以寫得更快,留有一些時間做第二遍處理。

自動完成、慣用設計提示(即根據語言或架構建議的慣用方式寫代碼)、代碼檢查、支援鍵盤輸入的預定義代碼片段,以及支援預定義和自定義模闆等服務都是加快開發以及確定一緻性和更好、更幹淨代碼的實踐。

代碼輔助工具使開發得以持續發展,隻需兩次點選就能極大地改善你所寫的代碼的品質。代碼輔助工具可以發現重複和沒用的代碼,使重構體驗變得愉快,簡化導航和檢查,以及強制使用某些模式。

比如說,所有開發者原則上都同意适當的命名規範對于代碼的可讀性和品質來說是很關鍵的。(參見第4章“編寫優質軟體”。)但是,當你意識到你應該重命名一個命名空間或者一個方法時,你就會面臨至少要在你自己的整個代碼庫裡這樣做的問題。這麼瘋狂的工作原本需要你自己在極短的時間内完成,現在可以由代碼輔助工具代勞了。

但是,你要記住,代碼輔助工具不是魔法,它們所做的隻是讓你付出更低的代價和更少的努力就可以寫出更好和更幹淨的代碼。除此以外,一切仍然取決于你。你在重構過程裡以及在代碼編輯階段操作工具。

3.如何告訴别人他們的代碼很爛

假設你發現你團隊裡有人在寫爛代碼。你會如何跟他們說?

這裡涉及一些心理學方面的東西。你不想表現得尖銳,你也不想傷害任何人;與此同時,你不想其他人的工作在某一時刻傷害到你。溝通是關鍵,不是嗎?是以你需要找到最佳方式在别人的代碼很爛時告訴他們。

總體而言,我們認為讓人注意到某些代碼的最佳方式是不經意地問為什麼用這種方式來寫。你可能會找到更多背後的動機,不管是資訊有誤,态度不好,技能局限,或者你所不知的限制。

在沒有确鑿證據之前,不要斷定你的編碼方式更好。那麼,你隻需對問題代碼背後的真正動機表現出好奇和興趣,并且表達出想了解更多的意願,因為如果換了你會用不同的方式來編碼。

4.使每個人都變成更好的開發者

下面總結一下讓團隊寫出好代碼的金科玉律:

針對代碼,而不是寫代碼的人。但通過寫代碼的人來嘗試改善代碼。

你可以通過某種方式修複任何一塊爛代碼。但是,當這種情況發生時,你不要責怪寫代碼的人;你可以幫助寫代碼的人改進他做事的方式。如果你可以這樣做,你至少可以得到兩方面的好處:你的團隊得到一個更好的開發者,你的團隊可能得到一個更快樂更有動力的開發者。你使這個開發者感覺更像英雄,因為他現在有了完成他的工作的最佳方式。

為了改進某些方面,每個人都需要教育訓練和實踐。最有效的方式是以靈活的方式結合教育訓練和實踐。但是,我們經常看到一些公司購買了教育訓練服務,讓他們在短短幾天内完成和傳遞,然後期望人們在接下來的星期一就能投入工作。事情并不是這樣的,至少不會如此有效。

這讓我們想起幾年前非常流行的一個短語:在職教育訓練。它指的是一邊學習、一邊做實際的工作。這起因于擁有不同技能的人在同一個團隊裡協同工作。

5.在簽入代碼之前檢查一下

你的公司可能會有最好的編碼标準,但你怎樣實施它們?信任開發者是好的,但驗證可能更加有效。結對程式設計和正常設計稽核是檢查代碼庫健康程度的具體方式。在一次典型的設計稽核裡,你可以和大家一起開放地讨論某些示例代碼。這些代碼可以是來自項目的真實代碼片段,它是某些參與者寫的,或者為了避免牽涉到情緒問題,也可以是為了闡明你想表達的觀點而專門寫的一段代碼。

為了實施編碼标準,你也可以考慮對你的代碼控制系統采用簽入政策,不管是microsoft team foundation server(tfs)、teamcity或者其他系統。這個過程可以自動化嗎?

今天,幾乎任何源代碼管理工具都提供針對簽入檔案實施控制的方式。比如說,tfs支援封閉簽入(gated check-ins)。封閉簽入本質上就是根據規則簽入。換句話說,檔案隻有在符合既定規則的時候才會被系統接受。當你選擇建立封閉簽入時,tfs會要求你指定一個現有的建構腳本。隻有在建構成功完成的時候,這個檔案才會簽入。

在tfs裡,一個建構隻是有一個msbuild腳本,它可以使用各種任務來定制。tfs自帶一些可以內建的預定義任務。比如說,你會找到代碼分析(以前的fxcop)任務和一個運作標明測試清單的任務。因為msbuild任務隻是一個實作了約定接口的注冊元件,是以你可以自己建立新的任務,添加自己的驗證規則。

值得注意的是,jetbrains的resharper,前面提到的其中一個代碼輔助工具,在它的最新版裡提供了一組免費的指令行工具,可以在自定義的msbuild任務裡檢測重複代碼以及執行常見檢查,包括根據你定義的自定義模闆執行自定義檢查。有趣的是,你甚至不需要resharper許可證就能使用這個指令行工具。

6.值得欣喜的是,這個項目不需要英雄

開發者傾向于超越自我,至少在他們深藏的夢想裡,希望他們每周可以工作超過80小時來拯救項目,讓客戶滿意,并且成為管理者和開發者同伴眼裡的真英雄。

我們想改編詩人和劇作家berthold brecht的一句名言:我們總想活在不需要英雄主義的世界裡。對英雄的需要以及由此而來的高壓力通常源自不足的期限。

有時候,期限從項目一開始就不公平了。在其他情況下,期限是在進展的過程中被證明為 錯的。

當這種情況出現時,情感上容易默許,但對指出不公平的期限的害怕産生對英雄的需要。溝通以及把問題挑明是一種坦誠,也是恢複更多控制以及降低壓力的有效途徑。

在軟體裡,我們可以說,你感到壓力是因為迫在眉睫的最後期限或者缺少所需技能。如果及時溝通,兩種情況都能很好解決。

我們不想要英雄,雖然我們自己也做過幾次英雄(我們猜你們中的大多數也做過),我們認為英雄主義是一種例外情況。在軟體裡,例外通常是要避免的。

7.鼓勵實踐

幾乎任何運動的專業運動員每天都會花上好幾個小時來實踐,到底是為什麼呢?開發者和專業選手之間是否存在某種相似之處?看情況而定。

一種看法是開發者每天在工作中實踐,并且沒有與其他開發者競争。有鑒于此,有人可能會得出沒有相似之處的結論,是以沒有必要實踐。

另一種看法是選手經常練習基本動作,以便他們可以自動地重複這些動作。定期回顧面向對象基礎、設計模式、編碼政策以及某些領域的api可以使這些知識記憶得更牢固,回憶得更 快速。

寫過多本asp.net的書,也實踐過驗證和成員系統,時隔多年,dino最近在使用基于角色的asp.net系統時感到問題很大。“老兄”,他最近跟我說,“我上次處理角色是什麼時候?”最終,建立基于角色的ui基礎設施以及相關的成員系統耗費比預期更多的功夫。

8.持續改變是工作的一部分

持續改變是描述現代軟體項目動态的有效方式。軟體項目始于一個想法或者一個相對模糊的業務想法。架構師和領域專家需要收集一些正式的需求,使原來的想法或業務需要更加明顯。

根據我們的經驗,大多數軟體項目就像活動目标,而需求就是把目标到處移動的東西。每次添加一個新的需求,這個環境以及系統的動态(在沒有這個特性的情況下可以正常工作的設計)也會改變。需求的改變是因為問題領域有了更好的了解,問題領域變化很快,或者時間壓力的問題。

需求波動(requirements churn)這個術語通常用來表示軟體項目裡的需求變化率(功能性需求、非功能性需求,或者兩者都有)。高需求波動将會為bbm提供理想的藏身之所。

每當處理新的需求都重新審視整個系統的架構是切實避免bbm的唯一辦法。重新審視整個系統的架構确實需要重構,也确實具有較大成本。這裡的重點是找到保持低成本的方法。重構是其中一個很難察覺會為項目帶來價值的東西。未能重構會導緻這些價值流失,這很糟糕。

twitter在2010年上線,當時的web前端充滿了用戶端功能。大量功能是通過在動态下載下傳的json資料之上即時生成html來提供的。在2012年,twitter重構了整個系統,選擇了伺服器端渲染。這是一個架構層面的重構,毫無疑問是昂貴的。但他們認為這是必要的,并且持續服務數億使用者,不管是對還是錯,反正它能工作。

作為一名架構師,架構和設計重構都是關鍵工具。架構師無法控制曆史以及業務場景和現實世界的發展。架構師需要一直做出調整,避免重新建構。這正是重構派上用場的地方。