編者按:本文作者 Boyan Mihaylov,36 氪經(jīng)授權轉載自微信公眾號“聊聊架構”(微信號:archtime)。
軟件架構構成了一個(gè)系統的骨架。它定義了當面對不同的功能性和非功能性需求時(shí)的系統行為。一方面,傳統瀑布式方法對項目開(kāi)發(fā)的所有階段提出了硬性約束要求,因此傳統瀑布式方法顯得僵化。另一方面,敏捷運動(dòng)讓我們擁抱改變,即使是處于開(kāi)發(fā)階段后期的改變。盡管我們正推動(dòng)自己從僵化的開(kāi)發(fā)模式邁向更靈活的模式,軟件架構由于其系統骨架的定位,天然地對變化敏感。因此關(guān)鍵之處在于,敏捷運動(dòng)擁抱的軟件架構必須是可持續的——具備可持續概念的軟件架構,支持在項目復雜度不斷增加的同時(shí),系統能以漸進(jìn)式的、簡(jiǎn)單的以及可維護的方法進(jìn)行擴展。
在這篇文章里,我回顧了自己在傳統瀑布式軟件架構和敏捷軟件架構下的工作經(jīng)歷。描述了兩者在以下三個(gè)方面表現出來(lái)的相似性及差異性:
什么是軟件架構?
軟件架構的定義(實(shí)際上你也能添加你自己下的定義)成百上千。存在這么多種定義的原因在于每個(gè)人都是基于自身情境下定義。我對 IEEE 給出的定義特別推崇,這個(gè)定義描述的基本概念非常形象化。此外,該定義描述出了軟件架構的精髓本質(zhì),同時(shí)適用于瀑布式和敏捷流程,而不是只能匹配某一個(gè)。在本文的后續部分,我會(huì )引述到該定義:
一個(gè)系統的基本組織結構、基本組成構件和互相之間的關(guān)系,以及構件于外部環(huán)境間的關(guān)系。同時(shí),軟件架構為后續的設計和架構演化提供了指導性原則。
瀑布式軟件架構
傳統瀑布式開(kāi)發(fā)的特征在于其由一系列有明確的開(kāi)始和結束時(shí)間的階段構成,每個(gè)階段包含確定的活動(dòng)集。所有階段串接在一起,每個(gè)階段嚴重依賴(lài)于前一個(gè)階段的交付產(chǎn)出。圖 1 闡述了瀑布式開(kāi)發(fā)過(guò)程涉及到的常見(jiàn)階段。
圖 1:傳統的瀑布式模型
軟件架構工作通常在軟件需求確定后開(kāi)始啟動(dòng),認為在此時(shí),關(guān)于系統應做什么,已經(jīng)確定好了。下一步是審查負責確定軟件架構的人以及當前階段的實(shí)際輸出結果。
傳統軟件架構
軟件架構實(shí)踐中通常由軟件架構師完成。軟件架構師擁有豐富的技術(shù)知識和經(jīng)驗——往往是由公司中已經(jīng)達到一定等級的開(kāi)發(fā)人員晉升而來(lái)的。軟件架構師負責分析軟件需求,并基于這些需求為未來(lái)系統的演化做某些技術(shù)決策。許多公司通常是一個(gè)項目對應一個(gè)軟件架構師,但在一些更大的公司里,軟件架構師們可能以團隊的形式共同合作。當項目所在領(lǐng)域非常復雜或者項目的周期很長(cháng),比如長(cháng)達 2 到 3年 甚至更久時(shí),通常情況下就會(huì )有軟件架構師團隊。不是所有的公司都有指定的軟件架構師角色——許多公司把這部分職責委托給他們的高級開(kāi)發(fā)人員承擔。在本文的后續部分,我所談的是指由軟件架構師執行的具體活動(dòng)項,而不是執行活動(dòng)項的人,除非另有明確說(shuō)明。
傳統的軟件架構師有 4 大主要特征:
真實(shí)世界之痛
我曾經(jīng)為全球最大的啤酒公司的其中之一做一個(gè)軟件項目。項目用了 2 到 3年 的時(shí)間,使用典型的瀑布式方法,在不同的階段有對應的負責人——軟件架構師、開(kāi)發(fā)人員、測試人員。我是一個(gè)小團隊里的開(kāi)發(fā),我們根據軟件架構師傳達的指示和指導意見(jiàn),執行系統的開(kāi)發(fā)工作。最初軟件架構師提供在場(chǎng)支持,但過(guò)了不久他轉到另一個(gè)項目去了,因此就減小了在這個(gè)項目上的工作量。當新的依賴(lài)不斷出現,有時(shí)候很難照著(zhù)擬定的軟件架構推進(jìn),因為和已有架構設計的規定不相符。盡管?chē)L試過(guò)讓我們的高級開(kāi)發(fā)人員接管架構設計,但項目還是逐漸成為所謂的意大利面條式代碼(spaghetti code),每個(gè)人都害怕去改代碼,因為很可能在哪個(gè)地方就出問(wèn)題了。遺憾的是,項目已經(jīng)來(lái)不及做任何重大的改變了,無(wú)法回到正軌,所以盡管項目最終發(fā)布了,但日后仍然被停掉了。
敏捷運動(dòng)
傳統的瀑布式架構的性質(zhì)是一次性活動(dòng),活動(dòng)有明確的起止時(shí)間,而敏捷軟件架構是一個(gè)持續不斷的過(guò)程,也許沒(méi)有終點(diǎn)。敏捷軟件架構使我們可以對架構設計實(shí)施更改,如果需要的話(huà),可以定期實(shí)施。擁抱變化的一大機制是在項目里運用迭代和增量開(kāi)發(fā)。在 Scrum 里,這些迭代被稱(chēng)之為 sprint,如圖 2 所示,典型的一個(gè) sprint 的周期約為 2-4 星期。周期窗口如此之小,所以能對提出的任何改變做快速討論。此外,敏捷非常關(guān)注團隊的協(xié)作,團隊成員之間存在的任何問(wèn)題應立即解決掉,以防止出現誤解及溝通不暢的情況。
圖 2:Scrum 中的一個(gè)典型 Sprint
敏捷運動(dòng)使得人們可以擁抱項目中的變化,但它并沒(méi)有告訴你應當以多快的速度響應變化。軟件架構設計作為系統的骨干支柱,對變化非常敏感。比如說(shuō),在項目中期,你覺(jué)得你能更改項目使用的平臺或編程語(yǔ)言嗎?這樣的更改,即便很罕見(jiàn),也需要通過(guò)多輪的迭代才能完成。這種改變甚至能把你重新拉回到項目的啟動(dòng)階段。當牽扯到軟件架構設計時(shí),有些類(lèi)型的變化就比較苦楚,需要較多的執行時(shí)間。
敏捷軟件架構師
Scrum 定義了三類(lèi)角色:
為了將傳統軟件架構師角色轉換為適配敏捷世界,我們需要先分析下一些可能的變種。構建 Scrum 團隊的一個(gè)方法是讓開(kāi)發(fā)團隊和一個(gè)單獨的軟件架構師團隊一起緊密地合作。圖 3 說(shuō)明了多 Scrum 團隊的構建場(chǎng)景。
圖 3:一個(gè)單獨的軟件架構師團隊和多個(gè)開(kāi)發(fā)團隊的合作
憑這種方法確實(shí)可以完成團隊的構建,但存在兩個(gè)問(wèn)題:
另一種做法是將軟件架構師直接置于開(kāi)發(fā)團隊中,如圖 4 所示。
圖 4:每個(gè)開(kāi)發(fā)團隊都有一名軟件架構師
這種情況下,敏捷軟件架構師的責任發(fā)生了一些變化:
如果想在不同的開(kāi)發(fā)團隊(也可能是不同的項目)之間共享軟件架構師資源,可以選擇構建擁有獨立軟件架構師團隊的組織結構。除此之外,如果從事的領(lǐng)域很復雜,需要考慮的視角很多,也可以采用擁有獨立軟件架構師團隊的組織結構。在這類(lèi)情況,必須保證軟件架構師與開(kāi)發(fā)人員的合作緊密,并展現出了對開(kāi)發(fā)人員的支持。敏捷方法關(guān)注協(xié)作,將軟件架構師從開(kāi)發(fā)人員從分離出來(lái),使得協(xié)作變得困難了。結果開(kāi)發(fā)過(guò)程變得更貼近于瀑布式模型。我個(gè)人更青睞第二種變體,在那軟件架構師處于開(kāi)發(fā)團隊中,因此團隊成員之間的溝通交流更有效。在一個(gè)更高的層次上架構師仍能(也應當能)協(xié)調一致。
敏捷軟件架構的時(shí)間跨度
敏捷軟件架構的一個(gè)重要方面是何時(shí)開(kāi)始進(jìn)行架構設計。不同于瀑布式模型對每一個(gè)階段都做了明確定義,在敏捷的世界里,不存在一個(gè)所有人都同意開(kāi)始的確定的時(shí)間點(diǎn)。一個(gè)典型的做法是引入 sprint #0,這是一個(gè)特殊的 sprint,開(kāi)發(fā)環(huán)境已經(jīng)配置好了,一些重要的決策已確定(比如編程語(yǔ)言、平臺、數據庫等等)。
這種方法有個(gè)常見(jiàn)的陷阱,即人們傾向于延長(cháng) sprint #0,因為總會(huì )發(fā)現事情 “幾乎就快準備好了”。經(jīng)常聽(tīng)到 “再給一個(gè)星期,我們就能開(kāi)始進(jìn)入常規的 sprint” 這樣的話(huà)。很多時(shí)候你會(huì )發(fā)現自己已經(jīng)在開(kāi)發(fā)系統了,但用戶(hù) story 還沒(méi)見(jiàn)著(zhù),因為 “提前幫忙完成功能實(shí)現真的很酷”。這種情況你應當預先商定出 sprint #0 的結束日期,可以設置在一個(gè)常規 sprint 的持續周期內,或者類(lèi)似相近的時(shí)間。
可能有人會(huì )疑惑,萬(wàn)一到常規 sprint 應啟動(dòng)的時(shí)候,還沒(méi)完成架構設計,要怎么辦。嗯,其實(shí)這也沒(méi)關(guān)系。事實(shí)上有可能永遠不會(huì )有準備好的一天。那也沒(méi)關(guān)系。軟件架構設計是一個(gè)持續不斷的過(guò)程。你應當經(jīng)常性地重新看回來(lái),去修正系統的骨干。在架構設計不能給予支持保證時(shí),你是無(wú)法進(jìn)行系統開(kāi)發(fā)的。Simon Brown 說(shuō):
敏捷團隊沒(méi)必要創(chuàng )建敏捷軟件架構。但一個(gè)好的架構確能做到敏捷。
控制原則
我們生活在一個(gè)復雜的世界,每一個(gè)業(yè)務(wù)領(lǐng)域也都是如此復雜。當構建一個(gè)軟件的架構時(shí),真的很容易從一開(kāi)始就把事情復雜化了,進(jìn)而讓后續開(kāi)發(fā)更容易出錯。以下兩條原則是做決策時(shí)事實(shí)上的標準:
如果在那一刻我們真的需要一個(gè)具體的功能和做成決策,這兩條原則試圖讓我們對此做慎重的思考。如果我們把做決策推遲到一個(gè)更晚的時(shí)段,就能保持架構的簡(jiǎn)單,并因此在一個(gè)更長(cháng)的時(shí)間里方便管理。軟件架構變得復雜的一個(gè)通常做法是引入抽象——可能變成一個(gè)新的花式層,以一種格式復制數據,然后轉換為另一種格式,或者為了讓代碼具備可測試性,可能創(chuàng )建出許許多多的類(lèi)、接口、工廠(chǎng)等等。
不過(guò),在運用這兩條原則時(shí)還要提防一處陷阱,我們傾向于把所有事情都延緩處理直到最后一刻。到那個(gè)時(shí)候,可能已經(jīng)變得太難實(shí)施所需的變更了。為了避開(kāi)陷阱,我們的任務(wù)難得多,因為:
我們不應在最后一刻做出決策,而應在最有責任這么做的時(shí)刻做出決策。
當我準備做出架構上的決策決定時(shí),如果做這個(gè)決定的確定性很高,我通常的做法是先做一些不那么花時(shí)間的小的準備。我也會(huì )去咨詢(xún)我的同事,我們一起討論問(wèn)題。
真實(shí)世界之痛
我曾經(jīng)做過(guò)一個(gè)輪渡票務(wù)在線(xiàn)銷(xiāo)售的項目。這是一個(gè)復雜的系統,它需要和 4 個(gè)其它第三方系統進(jìn)行通訊。一開(kāi)始我們的首要關(guān)注點(diǎn)是基于用戶(hù) story 完成功能實(shí)現。盡管我們知道我們需要一個(gè)更復雜巧妙的緩存機制,但那時(shí)還不必要——我們得先完成當前的用戶(hù) story,于是我們選擇了延緩處理。然后到后面,由于與其它第三方系統的大量通訊,系統變慢了。我們別無(wú)選擇,只能停止用戶(hù) story 開(kāi)發(fā),一門(mén)心思撲在緩存機制上——但這時(shí)事情已經(jīng)不好辦了。
如何組織文檔
瀑布式方法要求編寫(xiě)大量的文檔,因為需要用文檔來(lái)在不同的階段(以及每個(gè)階段的參與者)之間進(jìn)行信息傳遞。編寫(xiě)文檔的過(guò)程不僅耗時(shí)間,而且由于文檔存在對功能的不當描述,還經(jīng)常造成誤解。更進(jìn)一步,難以保持文檔的及時(shí)更新,因為開(kāi)發(fā)傾向于快速推進(jìn),很多時(shí)候不會(huì )再理會(huì )文檔了。正如我們使用敏捷來(lái)迭代地編寫(xiě)代碼,同樣可以如此處理文檔。我們開(kāi)始只描述系統的重要方面,然后在需要時(shí)持續地加入更多新的信息。
哪些內容應寫(xiě)入文檔
切勿對同一個(gè)東西以不同的方式做多次的文檔化處理。舉個(gè)例子,有工具能幫助你從代碼中生成圖表——和創(chuàng )建獨立的圖表相比,這樣做方便很多,圖表很容易過(guò)時(shí)的。除此之外,假如你為了描述系統的某一方面而創(chuàng )建了可視化工件,就沒(méi)必要再使用文字(除非你想要增加一些不能用視覺(jué)方式表達的細節)創(chuàng )建一大堆文本文檔去描述同一件事情。這里有一點(diǎn)很重要,你和你的團隊應對使用的繪圖符號有一致的理解。
怎樣文檔化
可能會(huì )想到使用 UML 來(lái)對軟件架構進(jìn)行文檔化。UML 是標準語(yǔ)言,每個(gè)人都能理解,大學(xué)里也教,因此 UML 一定是團結起組織內每個(gè)人的不二選擇。我的經(jīng)驗卻顯示,實(shí)踐中很少有人使用 UML。原因之一可能是 UML 提供的描述系統的方法非常多,可以從不同的視角進(jìn)行描述,所以不經(jīng)常使用 UML 的人會(huì )感到挫敗。
在敏捷業(yè)界,沒(méi)有特定的工具用來(lái)文檔化軟件架構??梢允褂猛繉?xiě)白板、便利貼、文本文檔、wiki 等等(見(jiàn)圖 5)。實(shí)踐中,只使用 2 到 3 種不同的格式會(huì )比較穩妥些,要不然信息可能會(huì )變得難以存儲和檢索。比如說(shuō),在白板上涂寫(xiě)一陣后,你可能需要對其進(jìn)行拍照,這樣就保存下來(lái)了圖表的電子版本。如果后面你要再次編輯,這時(shí)你得選擇到底是直接數字編輯照片,還是重新在白板上畫(huà)一遍然后再照一張相。
我用圖表工具來(lái)生成系統構件的簡(jiǎn)圖。通常用 Microsoft Visio 或 draw.io(已集成在 Google Drive 中),不過(guò)還有大量的其它工具可選,在線(xiàn)和離線(xiàn)的都有。如果開(kāi)會(huì )時(shí)在白板上做了繪制,我會(huì )在會(huì )議結束后用圖表工具重畫(huà)一遍一模一樣的圖,以保持我畫(huà)的東西的格式統一,彼此不存在大的差異。如果我需要對圖表添加額外的注解,我一般會(huì )另行創(chuàng )建文本文檔。
圖 5:白板畫(huà)和便利貼形式的文檔化
總結
軟件架構定義了未來(lái)系統的骨架。它不只是由線(xiàn)點(diǎn)組成的圖畫(huà),而是一系列管理支配著(zhù)系統開(kāi)發(fā)的完整決策,包括代碼本身。應細致地考慮每一個(gè)做出的決策與決定,它們都是一種權衡折衷。敏捷理念要求對變化持開(kāi)放心態(tài),甚至是來(lái)自項目晚期的變化,和傳統的瀑布式模型不同,瀑布式模型希望需求比較穩定。不過(guò),系統骨架的變更往往并不容易實(shí)施,甚至可能把你拉回到開(kāi)發(fā)階段。因此,不要等到最后一刻才做出該做的決策決定,這點(diǎn)很重要,而應該在最有責任這么多的時(shí)候做出決定,為此甚至可以不惜冒點(diǎn)小風(fēng)險,實(shí)現某些在那個(gè)時(shí)間點(diǎn)不做要求的東西。再者,敏捷軟件架構要求綜合全面地看待宏圖愿景和 “現在”,構建出一個(gè)每個(gè)人都能在上面添磚加瓦的可持續的平臺。
本文來(lái)自讀者投稿,不代表 36氪 立場(chǎng),如若轉載,請注明出處:http://36kr.com/p/5042920.html
“看完這篇還不夠?如果你也在創(chuàng )業(yè),并且希望自己的項目被報道,請戳這里告訴我們!”
聯(lián)系客服