「當一份提案立意良善、也解決了真實需求,但起草團隊缺乏經驗或脈絡、寫不出好計畫時,我若能幫忙改進它,會覺得特別有影響力。這時一份結構良好的計畫往往能大幅縮小範圍、又取得多數價值,因而更快展現成效。」——狄米崔·佩特拉修(Dmitry Petrashko)
如果說有一件事工程師、工程主管與技術高管都會同意,那就是「技術品質正陷入危機」。有個既好診斷又好開藥的說法:我們的工程師不重視品質,所以要嘛聘更好的人、要嘛重新訓練現有的人(把「工程師」換成「產品經理」或「高管」也行)。這是個有清楚反派、又能把責任推離工程領導層的動人敘事。但如同多數「把責任推給最無權者」的敘事,它既無益又錯誤。
多數情況下,低技術品質不是危機,而是預期中的正常狀態。工程師在當下做決策時,通常做出的是合理的品質取捨;而成功的公司會隨著規模擴張、轉向、或往企業客戶市場移動,逐步提高品質標準。在一家經營良好又成功的公司裡,你過去多數技術決策都不會達到你現在的品質門檻。縮小「現況品質」與「目標品質」之間的差距,不是失敗,而是有效工程領導的例行、必要工作。
問題本質#
身為工程領導團隊,你的目標是維持適當的技術品質水準,同時盡可能把精力投向核心業務。你必須在多個時間尺度上平衡品質,而這些時間尺度往往需求相衝突——為了下週死線把關鍵合作案送出門,和為了讓下一季發布速度快十倍而打造平台,做的是截然不同的工作。
正如公司的品質標準會隨時間移動,你管理技術品質的方法也會隨之演進。以下是一套工具箱,大致由輕到重:
- 修補正在造成即時問題的熱點(hot spots)
- 採納已知能改善品質的最佳實踐(best practices)
- 優先投資能在軟體變動時保住品質的槓桿點(leverage points)
- 對齊組織改動軟體方式的技術向量(technical vectors)
- 量測技術品質以指引更深的投資
- 成立技術品質團隊來打造品質系統與工具
- 運行品質計畫來量測、追蹤並建立當責
挑選工具時,永遠先選「最便宜、最直接、又可能有效」的那一個。技術品質是一場長期賽局:沒有「贏」這回事,只有持續學習、並贏得繼續玩下去的機會。
拾級而上,但別急著登頂#
面對挑戰,深挖到找出一個「值得解決的通用問題」有種特別的樂趣;但同樣重要的直覺,是快速解決眼前狀況、然後前進到下一個緊迫問題。
一般而言,最有效的做法是從最輕量的解法開始,只有當前期努力在規模壓力下崩潰時,才逐步走向重量級方案。如果你連讓團隊採用像樣的程式碼 linting 都做不到,那推行一套全面品質計畫注定失敗。重量級方案在規模上或許更有效,但執行起來難得多。
所以,先做快的那些!就算失敗,你也會從「推不動簡單的東西」中學得比「推不動困難的東西」更快、更多,更早進入改良後的第二次迭代。隨時間你會走向全面的做法,但無需趕。別在沒有真正需求時,就用「企業規模協調的險境」去換掉早期組織那份輕鬆、樂趣與純真。
把這些階段呈現成一道線性階梯很方便,但真實組織鮮少這樣用。你更可能是:修一個熱點、推一項最佳實踐、開始跑架構審查、又廢掉那個架構審查、再回頭修一陣熱點。過早引入的流程帶來的摩擦多過價值,也很快自曝其無效。若某件事行不通,試著讓它運作一陣,然後——就慶祝它的消亡吧。
熱點(hot spots)#
面對品質問題,第一直覺常是「找出一個流程失敗、然後用流程解法」。部署造成當機,是因為作者沒正確走測試流程,於是我們現在規定每次提交都要附測試——「這下那些懶惰的開發者該學乖了!」
有個關於沙賓法案(Sarbanes-Oxley)的老笑話:它不降低風險,只是讓出事時清楚該怪誰。可惜這笑話毫不好笑地適用於許多組織推行流程的方式。當責有其角色,但理解眼前問題並直接修復它,遠比建立流程驅動的當責重要。
推行流程需要人們改變工作方式,不該輕率為之。與其伸手去拿「流程改善」,不如先戴上效能工程師的思維:量測眼前問題、找出問題大宗發生在哪、然後精準聚焦那一塊。
以「效能工程師思維」處理熱點的幾個例子
- 前面「未測試就部署」的例子,也許只要直接給那位部署工程師關於測試習慣的回饋就好;或者,也許你該承認自己的軟體設計容易出錯,改採《A Philosophy of Software Design》所述的「把錯誤定義到不存在」的做法。
- 若有開發速度問題,也許是優化測試執行時間、把 Docker 編譯步驟移到 RAM disk,或用《Software Design X-Rays》的技巧找出該改進的具體檔案。
- 系統思考是作者職涯中遇過最具轉化力的思考技術。但有時它像海妖的歌聲,誘你去「修一個其實更該丟棄的系統」。沒錯,你可以推一套新訓練教團隊寫更好的測試;但也許你只要刪掉那個「98% 測試失敗都發生在其中」的測試檔就好。這正是優先處理熱點那種「不合理的有效性」,也是它該作為改善品質第一招的原因。
當你發現組織製造品質問題的速度快過你修補熱點的速度,就是時候轉向採納最佳實踐了。
最佳實踐(best practices)#
作者曾待過一家沒有團隊規劃流程的公司。工程負責人愈來愈受不了無法預估目標日期,於是下令改用 Scrum。命令下達後,某位主管把 Scrum 流程寫上 wiki,公告我們要用 Scrum 了,主管們也叫團隊用 Scrum。任務達成!
當然,沒人真的開始用 Scrum,大家照舊。承認錯誤很尷尬,於是工程負責人宣布導入是重大勝利,也沒人忍心說破。
這則憂傷的故事,正是許多公司推行最佳實踐的縮影,也是「最佳實踐」名聲不好的原因之一。理論上,組織應在修熱點之前就採納最佳實踐;但作者建議先修熱點、後推實踐——因為採納最佳實踐需要一定程度的組織與領導成熟度,而那需要時間培養。
推行新實踐時記住:好的流程是演化出來的,不是命令出來的。 研究別家公司如何採用類似實踐、記錄你打算的做法、找幾個投入的團隊實驗、把粗糙處磨平、依遇到的挑戰改進文件,然後才進一步推廣。倉促的流程就是失敗的流程。
同樣重要的是限制同時進行的流程推行數量。若你想讓團隊同時採納多項新實踐,就是在跟自己搶他們的注意力,之後也更難歸因哪項該回退或修改。作者甚至相信:任一時刻只該推行單一最佳實踐,把全部精力灌注在讓一項成功上,而非分散在好幾項之間。
一次只採納一項,也逼你認真思考該優先哪一項。挑下一個流程聽來容易,但常常搞不清哪些是「真・最佳實踐」、哪些只是「熟悉」或「有名」。真正的最佳實踐必須有研究支撐,而這方面最好的來源是《Accelerate》。
作者建議早期採納的幾項實踐
《Accelerate》的所有建議都以數據為本、相當不錯;作者發現最值得早期採納的少數幾項是:版本控制、主幹式開發(trunk-based development)、CI/CD、生產環境可觀測性(包含開發者為自己寫的系統值班),以及以小而原子化的變更工作。還有許多實踐作者很想倡議(誰沒有花過一段職涯歲月鼓吹更好的內部文件呢),但他已不像從前那樣信任自己的直覺了。
從「修熱點」轉向「最佳實踐」的時機,是你被太多冷卻不完的熱點淹沒時。而下一個轉折——從最佳實踐轉向槓桿點——出現在你想在「進行中的最佳實踐」尚未見效前,就急著採納新的一項。這時別提高你的「進行中最佳實踐上限」,改用下一個工具。
槓桿點(leverage points)#
效能工程師思維的優化很適合你「已經有」的問題,但它刻意不適用於未來:效能工程最大的罪,就是把力氣花在未經證實的問題上。然而,觀察軟體如何隨時間變化,有一小撮地方——額外投資能長期保住品質,既防止嚴重的品質崩壞,也降低未來品質投資的成本。
作者稱這些為品質槓桿點,三個最具影響力的是:介面、有狀態系統、資料模型。
- **介面(interfaces)**是系統之間的契約。有效的介面讓客戶端與被封裝的實作解耦;耐久的介面暴露所有本質複雜度、隱藏所有偶然複雜度;令人愉悅的介面「熱切地有分辨力,有分辨力地熱切」。
- 有狀態系統(stateful systems):狀態是任何系統中最難改變的部分,這種對變動的抗性使其成為關鍵槓桿點。狀態比其他系統更快變複雜,且有一種慣性,讓它日後改善相對昂貴;當你把安全、隱私、合規等業務義務納入後,改動有狀態系統更是難上加難。
- 資料模型(data models)是介面與狀態的交集,把有狀態系統的能力約束到應用程式認可為合法的範圍。好的資料模型是剛性的:只暴露它真正支援的、防止非法狀態被表達;好的資料模型也容許隨時間演化;有效的資料模型一點都不「聰明」。
在工作中辨識出這些槓桿點時,多花點時間刻意對待它們:若是介面,拿半打客戶端去對接模擬實作;若是資料模型,用它表達半打真實情境;若是有狀態系統,操練其失效模式、檢查一致性行為、建立貼近生產情境的效能基準。然後把學到的一切匯入一份技術規格文件,在團隊間傳閱、向業界同儕收集回饋;即便已開始實作,也要傾聽現實的聲音、保持對改變的開放。
投資槓桿點有個隱藏優勢:你不需要全組織對齊就能做。要寫技術願景或推最佳實踐,你需要那種認同;正因如此作者建議先從槓桿點著手。但當你耗盡槓桿點能觸及的影響力,也許就該轉向推動更廣的組織對齊了。
技術向量(technical vectors)#
有效的組織會把大多數努力導向一個共享願景。若你把每個技術決策畫成格子上的一個向量,這些向量指向愈一致,你長期能成就的就愈多。反之,作者共事過一些最令人印象深刻的工程師,創造的向量量值極大、方向卻不一致,最終在試圖領導組織時反而傷害了它。
對齊技術方向有兩個極端:把所有相關決策都導向某個頭銜裡有「架構師」的人(有效,但難擴展,且架構師離真實程式碼與流程愈遠、決策品質愈差);或讓每個團隊獨立決策(但一個「什麼工具都准」的組織,是一個「所有工具都無人支援」的組織)。實務上你需要以下這組工具:
- 給予直接回饋。 遇到不一致時,第一反應常是改流程;但請先直接找你認為方向偏了的人談。他們缺你的脈絡,你也缺他們的——一次快速對話往往能省下多年不必要的流程。
- 精煉你的工程策略:從 tech spec,到策略,再到願景。
- 把你的做法封裝進工作流與工具。 清楚的願景文件有幫助,但有些人就是不會讀。刻意設計的工具所形成的工作流,遠比訓練與文件更能培養習慣。例如:開通新服務時必須到某網站、附上該服務的技術規格連結;或是若服務沒設好值班(且當前有人值班、其推播通知也開啟),就封鎖上線部署。
- 在新成員入職時訓練。 習慣形成後再改很難;但若能在人們加入時就把他們指向正確方向,習慣的動能就會站在「保持一致」這一邊。
- 善用康威定律(Conway’s Law)。 組織會打造出反映其結構的軟體。結構糟糕會導致緊耦合或糾纏的軟體;但若組織設計良好,它同樣是一股品質的力量。
- 策展技術變革:用架構審查、投資策略、以及採用新工具的結構化流程。多數不一致源於缺乏脈絡,而這些正是把脈絡注入決策的組織槓桿點。
許多組織「從這裡開始」(架構審查等),但這卻是作者建議最後才打開的工具箱。沒有清楚闡述的願景,你怎麼提供一致的架構審查?為什麼要等別人設計完才告訴他們策略,而不是在入職流程裡就講?
不論你用哪些方法對齊技術向量,這都是以「月」與「年」為單位的工作。不存在「你寫完願景文件、組織就立刻臣服於它的卓越」的世界;更可能的是它積灰,直到你投入去建立支持。
多數公司把「修熱點」到「向量對齊」的技巧結合起來,就能成功管理技術品質,希望你也如此。但有些人會發現這還不夠、得走向更重的做法。這時的第一步,一如既往,是量測。
量測技術品質#
軟體工程裡「想量測」的渴望,普遍超前了我們「能量測」的實況。《Accelerate》定義了量測速度的指標,對定位流程與工具問題很有力,但這些指標都是在程式碼合併「之後」才開始。你要怎麼量測程式庫的品質,好讓你能找出缺口、提出行動計畫、並評估改善成效?
有些流程量測與有效變更相關。例如量測每個 pull request 改動的檔案數(較小的 PR 通常品質較高),或量測每檔行數(超大檔案通常難以擴充)。這些都有幫助、作者也建議量,但它們充其量只是程式碼品質的代理指標。
作者的經驗是:有用地量測程式碼品質是可能的,關鍵在於發展出一個極其精確的品質定義。定義愈細,量測程式庫就愈有用,對想改善所在區域的人也愈有指導性。(此法在《Building Evolutionary Architectures》與〈Reclaim unreasonable software〉中有詳述。)
可納入品質定義的代表性成分
- 多少比例的程式碼是靜態型別的?
- 多少檔案有對應的測試?
- 程式庫的測試覆蓋率是多少?
- 模組間的公開介面有多窄?
- 多少比例的檔案使用偏好的 HTTP 函式庫?
- 端點在冷啟動後能否於 500ms 內回應?
- 多少函式有危險的「寫後讀」行為?或對主資料庫實例做不必要的讀取?
- 多少端點在單一交易內完成所有狀態變更?
- 多少函式取得低粒度的鎖?
- 存在多少「熱檔案」——在超過半數 PR 中被改動?
你大可不同意其中某些屬性該出現在你的定義裡:你的定義應專屬於你的程式庫與需求。重點是發展出一個精確、可量測的定義。發展過程中一定會有分歧,你也必然會隨時間修改它。
定義好之後,接下來是埋點(instrumentation)——這領域可能真的很有挑戰,而埋點是有用指標的前提。埋點的複雜度,是實務上採用這些技術最大的摩擦點;但若你能突破,就能解鎖相當驚人的東西:一個真實、動態、可長期追蹤的品質分數,用它創造出「概念性對齊」所無法企及的方法清晰度。
有了定義與埋點,下一步是在投資品質團隊與投資品質計畫之間抉擇。專責團隊容易協調、頻寬可預期,通常是較容易的起點。
技術品質團隊#
技術品質團隊是一支專門在你的程式庫中創造品質的軟體工程團隊,可能叫「開發者生產力」「開發者工具」或「產品基礎設施」;不論名字為何,其目標是在公司軟體全域創造並維護品質。
它不是所謂的品保(QA)團隊。雖然兩者都會投資測試,但技術品質團隊的職權更廣,從工作流、建置、測試到介面設計都涵蓋。
建立這支團隊時,先從三到六人的固定規模開始。小團隊逼你依影響力狠狠排優先序,也確保你聚焦於可達成的事。隨時間它會累積需要持續投入去維護的系統(Jenkins 叢集是常見例子),這時要依整體工程組織的規模來調整團隊大小。經驗法則不好給,但也許是「每十五名產品工程師配一名做開發者工具的工程師」,另加你原有的基礎設施工程投資。
這類團隊很少有產品經理,通常由一名以上的 Staff-plus 工程師搭配工程主管夥伴來填補此角色;有時會用技術專案經理(TPM),但那多半是在跨入下一節的「品質計畫」之後。
運作成功的幾個基本功:
- 信指標勝於直覺。 每個專案都該有量測方式。品質是複雜系統,正是直覺最容易騙你的地方。而且你愈資深,你的經驗就愈不能代表多數人的經驗——你早已熟知那些粗糙邊角、遇到新問題時也第一個排到協助,但多數人並非如此。指標讓你誠實。
- 保持直覺新鮮。 程式碼與流程隨時間改變,你每離開產品開發一週,直覺就變舊一點。多數人發現「團隊嵌入」與「團隊輪調」是保持直覺相關的最好方法;有些人監看聊天室找問題、並固定與產品開發者做 1:1;最厲害的人兩者都做,還隨手備好指標儀表板。
- 傾聽並向使用者學習。 有個流行觀念叫「品味水準」,暗示某些人天生就知道什麼是好。設計有效品質投資的人之間差異巨大,但這不是天賦。最厲害的人深入理解使用者想達成什麼,把使用者需求置於實作限制之上。工具的採用率與易用性遠比純粹的強大重要——一個強大但難用的工具只會吸引少數高手,多數人會略過。慢下來把細節做對:藏起所有偶然複雜度、看一個工程師第一次用你的工具而不出手相助、補起缺口,再做十次!若你不對自己的工具做使用者研究,作為品質投資團隊你就注定失敗。
- 少做,但做得更好。 當你在為整個工程組織打造東西,任何你做得好的都會加速整體;任何你做得差的(包括「差一點就很棒、卻粗糙邊角太多」的東西)都會拖累所有人。「做少數最重要的事勝過一堆平庸專案」幾乎永遠成立,而當你想把工具與工作流推向整個組織時尤其如此(別忘了組織層級的「進行中流程上限」在這裡同樣適用!)。
- 別囤積影響力。 中央品質團隊與其支援的團隊之間有根本張力:中央團隊常有一個「全域最優」的做法,卻嚴重刺痛少數在非典型領域或工作負載上的團隊。例如某公司後端全用 JavaScript,就不讓機器學習工程師用 Python 生態系,只因不想維護兩套生態;或某公司所有 API 標準化為 REST/HTTP2/JSON,某團隊卻想改用 gRPC。這裡沒有完美答案,但務必建立一套深思熟慮的做法,在「探索的好處」與「標準化的好處」之間取得平衡。
一支運作成功的技術品質團隊,其生產力無疑會高於同樣人數直接做產品工程。理論上,衡量這種團隊影響力的正確方式是「折現後的開發者生產力」(比照現金流折現的精神);說「理論上」,是因為這種計算大半是在評估你的自信程度。
即使相當成功,你手上永遠會有一堆想做卻沒頻寬做的高影響力工作。組織不會做純理性的人力配置決策,你可能既缺頻寬完成重要專案、又拿不到增員核准。
團隊「可做的高影響力工作多過你能承接的量」是好徵兆:若你對承接哪些專案不加篩選,代表你想得還不夠廣。所以有 backlog 不代表你就該擴張品質團隊。但若你發現有「關鍵品質工作」實在挪不出手來做,也許就該考慮啟動一個品質計畫了。
品質計畫#
品質計畫(quality program)根本不是程式碼,而是一項由專責團隊領導、用以維護全組織技術品質的倡議,肩負「達成組織目標軟體品質水準」的廣泛職權。這類計畫相對少見,但你大概遇過類似的東西——負責公司事故回顧與補救的「事故計畫」。
計畫的技術成分就是前面談過的那些,這裡聚焦於「如何有效經營一個計畫」。
第一步是找一位能共同領導、負責運轉計畫機制的技術專案經理(TPM)。你或許能在沒有 TPM 的情況下,把計畫的資訊面推進不少;但那是陷阱——在大型組織裡單槍匹馬驅動一個計畫,你會被協調開銷壓垮。
經營組織型計畫是個龐大主題,但核心做法是:
- 找到計畫贊助者(sponsor)。 沒有一位被授權的贊助者,你無法改變組織行為。組織之所以如此運作,是因為那是它當前約束下的最優解;而你無法在沒有有力者倡議的情況下撼動那些約束。
- 產生可持續、可重現的指標。 常見到有人每週花四小時以上手動維護資料集——這行不通:資料會有洞、無法與後續步驟的自動化整合、你也會耗盡做真正改變的精力(刷新一個指標儀表板本身毫無價值)。
- 為每個受影響團隊訂出計畫目標,並給一條清楚的達成路徑。 你的計畫必須為每個受影響團隊訂出具體目標(例如降低測試不穩定性、更快關閉事故補救);但關鍵是你得提供成功地圖!太多計畫要求別的團隊參與,卻不給清楚方向。計畫負責人是主題專家,別把你的策略卸給每個團隊各自重新發明。
- 打造支援團隊達標的工具與文件。 一旦有了清楚路徑,就想辦法幫他們做出改變:提供「黃金範例」、示範把某段難改程式碼重構成新模式的範例 PR、驗證遷移正確的測試腳本、甚至自動產生可測試/驗證/合併的轉換 commit,讓工程師無需親手寫。盡你所能,避免每個團隊都得深入理解你想推進的問題空間。
- 建立目標儀表板並廣為分享。 提供能讓團隊看見「現況、目標狀態、以及沿途進展回饋」的儀表板。最好的儀表板既是每個團隊的計分卡,也為他們指出下一步該聚焦哪裡。它該支援三種縮放層級:完全拉遠(評估計畫整體影響)、完全拉近(讓單一團隊看到剩餘工作)、以及介於兩者之間(讓組織領導能對團隊當責,並支援你的贊助者對這些領導提出具體要求)。
- 對落後目標的人發出程式化的輕推(nudges)。 大家都很忙,不會總把你的目標排前面;有時他們做得很好、之後卻又退回被淘汰的舊做法。用輕推把團隊的注意力導向「下一步該做的工作」。記住注意力是稀缺資源:若你用一封輕推信浪費了他們的時間,下一封他們就不會理了。
- 定期與贊助者檢視計畫狀態。 計畫試圖推進的是一項「與各團隊在地目標並不自然對齊」的組織優先事項;許多團隊難以跳脫在地排序去成就全域優先。這時與贊助者檢視整體進度、把他指向那些願意優先做計畫工作的團隊,就至關重要。有效借力於贊助者去橋接錯位的優先序,將是你成功的關鍵。
某種意義上,計畫就是一場永無止境的遷移(migration),適用於遷移的技巧也適用於計畫。
計畫失敗的三大主因
- 純從流程視角運行,變得與「你真正想達成什麼」脫節;
- 純從技術視角運行,以為可以略過「為目標倡議、傾聽你想激勵的人」這些必要步驟;
- 想一個人同時扛起兩種視角——別單打獨鬥!
一個糟糕的計畫很像一個沒效率的非營利組織:目標是對的,但很少資金真正抵達目標。不論你如何量測技術品質,運行品質計畫時最該牢記的是:計畫本身不是目標,創造技術品質才是。 組織型計畫龐大、慣性極強,往往在早已停止見效後仍靠慣性向前推進。讓你的計畫精簡到「隨時可取消」,並保持足夠的自我批判,一旦它不再驅動品質創造就取消它。
從小處著手,慢慢加#
當你意識到實際技術品質已遠遠落後目標,很自然的第一反應是恐慌,然後一口氣鋪開一大堆技術與方案。把所有材料一股腦倒進鍋裡,結果必然不好,更糟的是你根本不知道該留下哪些。
若你正為技術品質苦惱——我們都會,而且很頻繁——那就從一件小事開始,反覆迭代直到它有效;然後再加一項技術,再迭代。慢慢堆疊出真正管用的東西,即使得忍受「動作不夠快」的指責。面對複雜系統與交互依賴,「快」只是表象;是有條不紊的推進才真正把事情做成。