官术网_书友最值得收藏!

1.5 架構適應度函數

在架構師確定了組件之間的關系并用代碼實現其設計之后,應該如何保證其他的實現者會遵從設計呢?更寬泛地說,如果不親手實現的話,架構師應該如何保證他們設立的設計原則成為現實呢?

這些問題都可以歸類為架構治理,適用于對軟件開發的任何有組織的監管。因為本書主要講的是架構結構,所以我們會在很多地方介紹如何通過適應度函數自動化設計和質量原則。

軟件開發隨著時間緩慢地演變,以適應特定的工程實踐。在早期的軟件開發中,經常用制造業來類比軟件實踐,無論是大型的(例如瀑布開發過程)還是小型的(項目的集成實踐)。在20世紀90年代早期,Kent Beck和C3項目[1]上的其他工程師發起了對軟件開發工程實踐的重新思考,他們叫它極限編程(XP),詳述了增量反饋和自動化對軟件開發生產效率的關鍵作用。在21世紀初,軟件的開發和運維碰撞出了同樣的思維火花,催生了DevOps這個新角色,同時自動化了很多曾經需要手工運行的工作。就像以前一樣,自動化讓團隊前進得更快了,因為團隊成員不用再擔心什么東西突然悄無聲息地出現問題。因此,自動化和反饋成為高效軟件開發的信條。2

讓我們回想致使自動化有了突破性進展的環境和場景。在持續集成(CI)出現之前,絕大部分軟件項目都要經歷漫長的集成階段。每個開發人員被認為需要在一定程度上獨立于其他人工作,然后在最后的集成階段匯總所有的代碼。這種實踐的余音還縈繞在版本控制工具里,強制分支從而阻止持續集成。不出意外,項目規模和集成之痛這兩者之間存在強關聯。通過開創持續集成,XP團隊用事實證明了快速、持續的反饋所具有的價值。

DevOps的變革有一段相似的歷程。隨著Linux和其他開源軟件對企業來說變得“足夠好”,加上可以程序化定義(終于有了)虛擬機的工具的降臨,運維人員意識到他們可以自動化機器定義和其他很多重復工作。

在這兩種場景里,得益于技術的發展和洞察力,昂貴人力進行的重復性工作被自動化所取代,這也正是當前在大多數組織里架構治理的現狀。舉例來說,如果架構師選擇了一種特定的架構風格或者通信媒介,如何確保開發人員可以正確地實施呢?如果手動去保證,架構師需要組織代碼審查或者召開架構審查委員會來評估治理狀態。然而,就如計算機里的手動配置一樣,重要的細節很容易被浮于表面的審查所忽略。

使用適應度函數

在2017年出版的Building Evolutionary Architectures(O'Reilly)一書中,作者(Neal Ford、Rebecca Parsons和Patrick Kua)定義了架構適應度函數:它是能夠對于某個(或一組)架構特征進行客觀的完整性評估的任何機制。

任何機制

架構師可以使用多種不同的工具來實現適應度函數,本書中將會演示非常多的例子。例如,有專門用來測試架構結構的測試庫,架構師可以使用監控設備測試運維性架構的特征(例如性能或可伸縮性),使用混沌工程框架測試可靠性和彈性。

客觀的完整性評估

自動化治理的一個關鍵點便是架構特征的客觀定義。舉例來說,架構師不能說我想要一個“高性能”的網站,而必須提供一個可以被測試、監控或者其他適應度函數來測定的目標值。

架構師必須警惕那些復合的架構特征,它們本身不能被客觀地度量,卻由其他可以度量的東西組成。例如,“敏捷度”是不可度量的,但是如果把寬泛的“敏捷度”拆解開,它的目標是無論是在生態系統還是領域中,團隊都能夠快速響應、大膽應變。從而架構師可以找到組成敏捷度的可度量特征:可部署性、可測試性、周期等。通常,缺乏度量某個架構特征的能力是因為這個定義本身過于模糊。如果架構師致力于尋求可度量屬性,那么他們就可以自動化適應度函數應用程序。

某個(或一組)架構特征

這里的特征描述的是適應度函數的兩種類型:

原子的

這類適應度函數在隔離條件下驗證單一架構特征。例如,在單個代碼庫里檢查組件周期的適應度函數在這個范圍內是原子的。

整體的

整體適應度函數驗證一組架構特征。架構特征復雜化的一個征兆是它們有時和其他架構特征協同出現。例如,如果架構師想要增強安全性,則很可能會影響性能。類似地,可伸縮性和彈性可能是互相矛盾的——支持大量并發用戶可能會使得突發流量更加難以處理。整體適應度函數運用一組連鎖的架構特征來確保其綜合效應不會給架構帶來負面影響。

架構師利用適應度函數來保護架構特征不被改變。在敏捷軟件開發的世界里,程序員編寫單元、功能和用戶驗收測試,以此來驗證領域設計的不同維度。然而,直到現在,沒有類似的機制來驗證設計的架構特征部分。事實上,區分適應度函數和單元測試給架構師提供了很好的界定范圍的方式。適應度函數驗證架構特征,而不是領域標準;單元測試恰恰相反。因此,架構師可以通過問題“執行這個測試需要任何領域知識嗎?”來決定使用哪種測試,如果答案是需要,則使用單元、功能和用戶驗收測試;如果不需要,則使用適應度函數。

例如,當架構師說到彈性時,指的是應用程序承受用戶激增的能力。注意,架構師不需要知道任何領域細節,因此,彈性是一個架構考量,在適應度函數的范疇內。與此相反,如果架構師想要驗證一個郵寄地址的各個有效部分,這就是傳統測試的范疇了。當然,這種區分不是絕對的,一些適應度函數會觸及領域,反之亦然,但是其目標的差異性提供了一個很好的思維方式來區分它們。

為了使概念不那么抽象,下面舉幾個例子。

架構師常見的目標之一就是在代碼庫中保持良好的內部結構完整性。然而,黑暗勢力在各種平臺上和架構師的美好意圖作對。例如,在任何流行的Java或 .NET開發環境中編寫代碼時,一旦程序員使用了任何還沒引入的類,IDE(集成開發環境)就會善解人意地冒出一個提示框,詢問是否需要自動引入這個引用。這個場景太常見了,以至于大部分程序員養成了順手確認自動引入以便讓提示框快點消失的習慣。

然而,在組件間隨意地引用類或組件給模塊化帶來了災難性的問題。例如,圖1-1展示了一個架構師想極力避免的典型的破壞性反模式。

圖1-1:組件之間的循環依賴

在這個反模式中,每個組件都依賴其他組件。這樣的組件網絡會破壞模塊化,因為開發者不能單獨復用某一個組件,而是必須帶上其他組件。如果其他組件也這樣和另外的組件耦合,那么架構最終將雜糅成另一個反模式:大泥球(Big Ball of Mud,https://oreil.ly/usx7p)。除了一直在背后監視那些迫不及待敲回車的程序員以外,架構師該怎么管理這樣的行為呢?代碼審查可以起到一些作用,但是因為發生的太遲而鮮有成效。如果架構師允許開發團隊在代碼庫里胡亂引用,到一周后的代碼審查時,一些嚴重的破壞早已發生。

這個問題的解決辦法就是編寫一個適應度函數,來阻止組件循環,如示例1-1所示。

示例1-1:用于檢測組件循環的適應度函數

上述代碼中,架構師用度量工具JDepend(https://oreil.ly/ozzzk)來檢查包與包之間的依賴。這個工具可以理解Java包結構,當存在循環依賴的時候,不讓測試通過。架構師可以把這個測試加入項目的持續構建里,這樣就不用擔心這些手速驚人的程序員不經意引入循環依賴了。這是個很好的例子,證明適應度函數可以捍衛那些重要但不緊急的軟件開發實踐。對于架構師來說,這是一個重要的問題,而且幾乎不會影響日常編碼。

示例1-1是一個非常底層、以代碼為中心的適應度函數。很多流行的代碼整潔工具(例如SonarQube,https://www.sonarqube.org)實現了很多可供一鍵式使用的適應度函數。然而,與微服務架構一樣,架構師可能也想驗證架構的宏觀結構。當設計如圖1-2所示的分層架構時,架構師為了隔離關注點定義了不同的層。

圖1-2:傳統的分層架構

然而,架構師又該如何保證開發人員會遵從這樣的分層呢?一些開發人員可能不理解模式的重要性,為了解決一些棘手的局部問題(比如性能),另外一些人可能會秉持“先斬后奏”的態度。允許實施者破壞架構的根基會傷害其長期健康。

ArchUnit(https://www.archunit.org)允許架構師通過適應度函數來解決這一問題,如示例1-2所示。

示例1-2:ArchUnit適應度函數治理分層

在示例1-2中,架構師定義所需的層之間的關系,然后用一個驗證適應度函數來管理它。這使得架構師能夠在圖表及其他信息性工件以外建立架構原則,并且可以持續驗證它們。

在.NET領域有一個類似的工具NetArchTest(https://oreil.ly/EMXpv),在其平臺上可以運行類似的測試。示例1-3展示了一個C#中的分層校驗。

示例1-3:用于檢查層依賴的NetArchTest

這個領域中新工具持續出現,且愈加先進。隨著本書很多解決方案用到適應度函數,我們將會持續關注這項技術。

確定適應度函數的客觀結果是至關重要的。客觀并不意味著靜態。一些適應度函數會有和上下文無關的返回值,例如true/false或數值(如性能閾值)。然而,其他適應度函數(被視為動態的)的返回值是由上下文決定的。例如,當度量可伸縮性(scalability)時,架構師既度量并發用戶的總量,也度量單個用戶的性能。正常情況下,在架構師設計的系統中,用戶數量增加時,每個用戶的性能會略微降低(但不是斷崖式下跌)。因此,對于這些系統,架構師會考慮并發用戶的總量來設計性能適應度函數。只要一個架構特征的度量是客觀的,架構師就能測試它。

盡管大多數適應度函數應該是自動化并持續運行的,但還是存在一些必須要手動運行的情況。手動適應度函數需要人來進行驗證。舉例來說,對于含有敏感法律信息的系統,需要律師去審查關鍵部分的變化以保證其合法性,這就沒辦法自動化。大多數部署流水線支持手動階段,允許團隊安排手動適應度函數。理想情況下,它們最好盡可能頻繁地運行——不運行的驗證什么也驗證不了。團隊要么按需運行這些適應度函數(較少見),要么把它作為持續集成工作流的一部分(最常見)。想要盡可能從適應度函數這類校驗中獲益,就應該持續地運行適應度函數。

下面這個使用適應度函數來做企業級治理的例子告訴我們,持續性很重要。考慮這個場景:假如企業正在使用的某個開發框架或者庫發現了一個零日漏洞(zero-day exploit),這時該怎么辦?在大多數公司里,安全專家會把用到這個有問題的版本的地方翻個底朝天,確保它們都更新了。然而這個過程很少是自動化的,大都依賴于很多人工步驟。這不是一個抽象問題,一家主要的金融機構的Equifax,就因為上述這般流程導致了數據泄露事件。如前面描述的架構治理問題所述,人工環節容易出錯還容易忽略細節。

Equifax數據泄露事件

2017年9月7日,Equifax(美國一家信用評分機構)發表公告稱其發生了數據泄露。最終,這個問題被追溯到有人利用了Java生態中流行的Struts Web框架的一個漏洞(Apache Struts vCVE-2017-5638)。基金會發表了一項聲明告知了這個漏洞并在2017年3月7日發布了補丁。美國國土安全部第二天就聯系了Equifax和其他類似公司,警示它們有此問題。它們也在2017年3月15日進行了掃描,但并沒有發現所有受到該影響的系統。因此,直到2017年7月29日這天,很多老系統也沒有應用這個至關重要的補丁,此時Equifax的安全專家監測到了黑客行為,致使這次數據泄露事件發生。

想象有另外一個平行世界,在那里每個項目都運行著一個部署流水線,安全團隊在每個團隊的部署流水線上都有個“槽”,可以部署自己的適應度函數。大多數時間,這就是一些平常的安全檢查,防止開發人員把密碼存在數據庫里,以及諸如此類的日常管理瑣事。然而,一旦有零日漏洞出現,這個機制可以讓安全團隊在每個項目里插入一個測試來查驗某個特定框架及其版本號。如果發現了有危險的版本,就會使構建失敗并通知安全團隊。團隊配置流水線讓其響應系統中的任何變化:代碼、數據庫模式、部署配置以及適應度函數。如此,企業便可以全面自動化重要的治理任務。

架構師可以從適應度函數獲益良多,最重要的是他們有機會重新編寫代碼!架構師最常抱怨的就是他們幾乎沒時間寫代碼——但是適應度函數基本都是代碼!架構師必須理解這個系統及其未來的演進才能夠搭建可執行的架構規范。任何人可以在任何時間通過運行項目的構建來驗證這個規范。同時這也結合了架構師的核心目標,即隨著項目增長持續跟進項目代碼。

架構師應該避免過度使用強大的適應度函數。他們不應該蜷縮在象牙塔里籌謀一個復雜到不可思議、環環相扣的適應度函數,那樣只會讓開發人員和團隊感到沮喪和挫敗。相反,適應度函數可以是架構師為軟件項目構建重要而不緊急原則的可執行清單的一種方式。很多項目十萬火急,允許忽略一些重要的原則。這通常是技術債的來源:“我們知道這樣做不好,回頭就改”——然后永不回頭。通過把代碼質量、架構和其他保護措施寫成適應度函數代碼,并持續運行,架構師可以構建一個開發人員無法跳過的質量清單。

幾年前,Atul Gawande寫了一本杰出的書——The Checklist Manifesto(Picador出版社),書中重點介紹了外科手術醫生,飛行員等專業人士和其他領域的人員對清單的使用,清單(checklist)在他們的工作中非常重要(有時是法律要求)。這不是因為他們不了解自己的工作或者特別健忘,當專業人士一遍一遍地重復相同的任務時,很容易無意中跳過一步而不自知,清單正是用來防止此事發生的。適應度函數代表了架構師定義的重要原則清單,作為構建中的一部分用于保證開發人員不會無意或有意地忽略它們。

在本書中,當有機會演示如何讓架構解決方案和最初設計保持一致時,我們就會使用適應度函數。

主站蜘蛛池模板: 康平县| 城固县| 诸暨市| 长阳| 左权县| 诏安县| 三台县| 会东县| 水富县| 成都市| 铜川市| 特克斯县| 鹤岗市| 云和县| 淮南市| 弥渡县| 新乡市| 靖江市| 清新县| 西青区| 林西县| 谢通门县| 遂溪县| 丰县| 平昌县| 扬州市| 明溪县| 全南县| 林甸县| 张北县| 贵定县| 九龙县| 永清县| 临澧县| 广宁县| 武强县| 楚雄市| 咸阳市| 吴堡县| 鹤壁市| 五莲县|