導語:設計文檔是軟體工程設計中的重要組成部分。本文根據 Google 及其它公司編寫設計文檔的經驗,並結合實際應用加以完善,系統地介紹設計文檔的目的、結構及參考模板,希望推動設計文檔在團隊中落地,傳承並沉澱經驗,構建良好的文化氛圍。 ...
導語:設計文檔是軟體工程設計中的重要組成部分。本文根據 Google 及其它公司編寫設計文檔的經驗,並結合實際應用加以完善,系統地介紹設計文檔的目的、結構及參考模板,希望推動設計文檔在團隊中落地,傳承並沉澱經驗,構建良好的文化氛圍。
1、設計文檔是什麼?
設計文檔是軟體工程設計中的重要組成部分,是對一個技術問題的解決方案的系統性描述。設計文檔的目的,是闡明設計的總體思想和設計中考慮的權衡點。
作為一名軟體工程師,我們的工作本質不僅僅是編寫程式代碼,而是解決真正的問題。因此,相比最終的程式代碼,文字形式的設計文檔,在早期能夠更加簡明扼要地傳達信息,便於讓讀者理解問題,找到解決方案。
除了作為系統設計的最初體現,設計文檔在軟體工程的開發周期中起到下麵重要作用:
通過設計文檔,我們可以:
- 在可以低成本迭代的時候,儘早發現設計中的問題 - 設計左移,代價左移,快速失敗(fail fast)
- 在團隊中對設計達成一致 - 設計的本質是取捨(tradeoff)。幾乎所有的架構設計決策都會被挑戰,原因之一是:讀者並非對所有的取捨都知曉,且與作者達成共識。在設計文檔中清晰地列出取捨,有利於幫助讀者瞭解(並認可)你的決策思路,減少被挑戰的可能。
- 將資深工程師的經驗和思想擴展到整個團隊,幫助團隊成長 - 作為作者,可以供資淺工程師學習 - 作為讀者,可以審核資淺工程師的設計並提供建議
- 形成團隊軟體設計的一致方式,沉澱團隊/公司技術積累 - 企業的生命力在於知識價值的積累
2、什麼時候需要設計文檔?
本身撰寫設計文檔是需要編寫成本的。如果問題的解決方案非常清晰,沒有明確的取捨,設計文檔中基本都是實現描述,則應該省略設計文檔而直接實現。換言之,如果編寫設計文檔的時間主要消耗在“寫”而不是在“思考”上,則這個設計文檔可省略。當你考慮編寫一個設計文檔時,想一想以下這幾點:
- 軟體的規模是否較大,值得付出額外的編寫評審設計文檔的時間來降低失敗的風險?
- 高級工程師無法確保 CR 每一份代碼,讓他們參與設計評審是否回報更高?
- 軟體設計決策是模糊的,甚至有爭議。有必要圍繞設計文檔在組織上達成共識?
- 是否需要通過設計文檔強調項目交叉問題,如隱私性(Privacy)、安全性(Security)、日誌記錄?
- 是否有必要寫一份文檔來對有關遺留系統的設計問題提供高層次的分析?
如果以上的問題的答案為“是”,那麼設計文檔可能是開始你的下一個軟體項目的絕佳方法。
3、設計文檔要怎麼寫?
在考慮通過用設計文檔解決問題,開始著手準備設計文檔前,需要釐清設計文檔易混淆的三個概念,它們也是創作設計文檔的根基。一旦出現偏差,我們認真撰寫的文檔很有可能完全不可用,在糾正偏差時也會出現大量工作返工,造成資源的浪費。所以,撰寫設計文檔前需要搞清楚這些前提。
3.1 撰寫設計文檔的三個前提
在搞清楚設計文檔撰寫的三個前提後,就會進入文檔的編寫階段。設計文檔是非正式的文檔,因此他們的內容不會遵循嚴格的準則,一個首要原則是,針對項目的具體情況可以用相對合理的方式來編寫。儘管如此,筆者也參考文獻並結合自身經驗給出一些建議。
寫作風格的三要素
設計文檔的寫作也是技術寫作(Technical Writing),因此同樣強調以下三要素:
- 清晰
- 簡潔
- 優雅
設計文檔的五個要點
系統設計及編寫設計文檔時需要註意的 5 個要點。
1. 任何架構問題都是取捨。
在軟體設計中,沒有任何一個維度有絕對意義上的優劣。每一個設計決定都需要考量很多相違背的因素。例如,可擴展性和效率相背;長期效率和短期收益相背;規模化提升了效率,但降低了靈活性。“高內聚低耦合” 便於迭代,但是會增加短期的開發成本。NoSQL 比 SQL 性能高,但代價是功能的大幅降低。如果一個設計決策看上去沒有任何的取捨,往往是因為取捨還沒有被識別。在設計時應從取捨視角切入,尋找不同需求間的平衡。
2. “為什麼” 比 “怎麼做” 更重要。
設計所解決的問題往往是複雜而模糊的,因此,解決方案往往是不唯一的。對工程設計,方案的論證通常比方案本身更重要。
3. 考慮時間維度
做設計取捨時不能忽略時間維度,只設計某個階段的終態。設計需要考慮以下方面:
- 可維護性與可擴展性
- 考慮實現路徑
- 考慮未來計劃
4. 避免過度設計
設計伊始,界定問題的範圍。
一個良好界定的問題是一個良好設計的必要條件。不要迷信設計模型、設計模式、XX 驅動設計。這些是工具,而非法則。不要為了製造問題而解決問題。不要通過複雜的設計來體現工作的難度和深度: 一個困難的問題可能會有一個簡單的答案。也不要過於擔憂設計被迅速淘汰。保留可擴展性,但不要在未知時浪費精力擴展。
5. 總結
最重要的是要知道如何設計,知道自己在設計什麼。列寧·克魯格效應告訴我們,這未必顯然。
I think test-driven design is great. I do that a lot more than I used to do. But you can test all you want and if you don’t know how to approach the problem, you’re not going to get a solution. ——Coders at Work, Peter Norvig
3.2 設計文檔的核心原則
前提一:設計文檔的讀寫比最高
實現代碼、系統介面、設計文檔,讀寫比(內容被所有人閱讀花費的時間:內容寫作花費的時間)是逐步上升的。通常,設計文檔供閱讀讀的時間往往遠多於寫的時間。因此,編寫設計文檔時就更多考慮讀者的體驗而非作者的體驗。為提升設計可讀性的時間非常值得投資。
前提二:設計文檔不是文學寫作
設計文檔的目的是為了溝通設計,而不是為了自我表達。把精力放在如何清晰、簡潔地表達,而非放在文采上
前提三:設計文檔為誰而寫
首先先瞭解你的讀者是誰?在良好的文檔分享文化下,讀者不應該只是你的 TL 以及該設計文檔的實施者,你的設計文檔實際讀者的範圍往往大得多。在不確定的時候,經驗做法是,假設的讀者群體為:公司內部的、有一定工程經驗的、但對該系統的上下文只有初步瞭解的軟體工程師。通常,設計文檔的範圍越大,假定的受眾群也會更大。這意味著受眾對目標系統的平均瞭解程度更低,也就意味著設計文檔往往需要:
- 更加詳細的背景介紹
- 更少使用內部術語或縮寫
- 更多闡述設計思路、取捨,更少解釋具體實現細節
其次,考慮讀者喜歡如何閱讀?大部分讀者不會逐字逐句閱讀你的設計文檔。大家都很忙。讀者通常只會掃描大體結構,然後閱讀(或者跳讀)自己感興趣的部分。讀者喜歡“故事”。將內容以故事的結構呈現最容易被接受,即使我們並不是需要講述一個傳統的打怪升級的故事。雖然故事內容各有不同,但大部分故事都遵循一些基本的範式。例如,約瑟夫·坎伯 總結提出全世界大部分神話故事都符合“ 英雄之旅 ”-- “啟程、啟蒙、歸程”三幕 -- 這個模式。
對於設計文檔/科技論文寫作,通用安全的選擇是 Writing Science 所介紹的 OCAR 故事結構。
- Opening:開場,背景介紹
- Challenge:挑戰,所要解決的問題
- Action:行動,執行的實驗/設計/...
- Resolution:結果
設計文檔通常遵循特定的組織結構,我們可以將每一個結構對應到 OCAR 的不同部分,以此講述故事。
最後,辯證看待設計文檔可讀性。設計文檔可讀性 vs. 代碼可讀性都稱作可讀性,兩者有些共通之處:
- 文檔著重強調的內容應該是並非顯而易見的事項:
設計文檔 | 代碼文檔 |
---|---|
說明背景 | 說明上下文 |
強調 Why/權衡/其它方案 | 強調 Why |
註意事項 | 註意事項 |
代碼可以清晰表達的內容不需要文檔 | 代碼可以清晰表達的內容不需要文檔 |
- 沒有絕對的正確答案:沒有完美的代碼,也沒有完美的設計文檔。- 不同的讀者對可讀性的理解有細微的不同。可讀性是主觀的。- 在實踐中,我們追求讓更多(而非所有)讀者更順暢地閱讀設計文檔。- 不要為完美主義付出過重代碼。平衡可讀性與時間成本。
- 我們的目標是有意識地提高文檔寫作/代碼水平。高質量的寫作是一種習慣。提高水平的方法有:- 多讀他人優秀的設計文檔。- 評審(Design doc review/Code review)有益。- 設計文檔評審往往主要關註系統設計合理性,但是可讀性方面的評審也有必要。
- 多寫作、多修改、多重寫。
3.3 設計文檔的最佳實踐
遣詞
用詞要簡練、準確、直白。
- 正確使用專業術語。- 合理地使用常見術語可以降低溝通成本。- 不要過多使用過於小眾或自創的術語。如果有必要,需要在文中 - 必要時提供對照的英文術語以方便理解。- 避免無上下文的縮略詞。
- 省略程度副詞
- 不管作者意圖為何, “非常重要” 和 “重要” 在讀者看來大同小異。
- 使用數據 - 與其說明“該系統的性能提升明顯”,不如“該系統的性能提升了 42%”更為可信,也更方便讀者做出自己的判斷。
- 忌佶屈聱牙
- 例如上文,應改為“不要使用過於生僻的辭彙,不要過度使用書面語”
- 千萬不要寫文言文
造句
- 使用短句,不要使用多從句的複雜句式。- 讀者不是來考 GRE 的。- 寫文檔也不是為了炫耀自己可以駕馭長難句。
“_系統形式問題就是下麵這樣一個問題:怎樣把各種不同的對象種類安排在一個系統中,以使較高的對象種類總能從較低的對象種類構造出來,也就是說前者可還原為後者。為瞭解決這個問題,我們必須從其相互可還原性來研究各種不同的對象種類。
為此目的,我們要根據所涉及的對象領域的實際科學知識為每一個要考察的對象尋找其基本事實存在的充分而必要的條件的各種可能性。對此我們可採取下麵的辦法來進行,即要求這門實際科學給出基本事實的一個(確實而常在的)表徵_。”Excerpt From: 【德】魯道夫·卡爾納普. “世界的邏輯構造.”
-
簡單表達,去掉無意義的修飾,去掉試圖緩和語氣的從句。- 反例:“我們可以看到, TencentDB 在一定程度上可以滿足我們對事務支持的需求。” - 修改後:“TencentDB 支持事務”。- 反例:“MR 提交信息作為讀者查閱修改歷史時第一時間看到的信息,其重要性不言而喻。” - 修改後:“讀者查閱修改歷史時會首先關註 MR 提交信息。” - 本段討論另一個問題,即……
-
語氣要冷靜。避免過於口語化。- 不要加順口溜 - 不要使用語氣詞 - 不要使用嘆號!如果希望強調,使用粗體或者斜體!也可以使用分級標題!
-
準確。描述客觀事實,避免加入主觀情緒。
段落
段落應該儘量短。通常,一個段落不要超過 8 個完整的句子。每個段落有且僅有一個清晰的主題。每個段落開頭應該是主題句,方便讀者快速瞭解段落大意。段落中的每一句話應該與主題緊密相關;否則,它應該另起段落,或者應該刪掉。
註意段落的流動。段落句子應該始於一個讀者已經熟悉的概念,將新的內容放在句子結尾。這樣,讀者可以更連貫地理解。
使用列表
使用 Bullet point 標明無順序的列表,使用數字序號明確前後順序。如何正確使用列表不在本文詳細展開,會在後續文章介紹,也可參見文末的參考文獻。
結構
- 使用模板:使用模板可以作為思考輔助,同時也提供了相對較完整且規範的結構。文末提供了一份設計文檔模板以供參考。
- 使用圖表:一圖勝千言。合理地使用圖表可以極大地降低用戶的理解成本。
- 使用標題:標題要分級、要簡短清晰
篇幅
設計文檔不要過長。太多內容堆積在一個文檔中會讓讀者喪失興趣。
對於一個大型項目來說,10 頁(~5000 字)左右是一個合適的長度。當超過這個長度時,可以考慮將問題拆分成子問題分別編寫設計文檔,併在總體設計文檔中鏈接子設計文檔。
對於小問題做增量的改進,考慮使用單頁文檔(one-pager)。通常這類文檔的範圍較小,解決問題較簡單,目標用戶群體僅限於對問題已經有充分瞭解的內部成員。這時,可以省略背景等內容,而僅使用 目標 -- 方案 兩段式論證的結構。
排版
使用統一的字體。用戶不會意識到不同的字體代表不同的含義,只會感受到混亂。微軟雅黑是安全選擇。
不要使用不同顏色來區分內容。不要在文中使用超過三種顏色。可以在標題及分級標題使用標誌性的顏色,同時正文使用黑色。
附錄:設計文檔模板
設計文檔沒有定式。即使如此,筆者參考谷歌設計文檔的結構和格式,並結合實際工作經驗加以完善。在此提供一個可供新手參考的設計文檔模版,您可以使用此文檔模板作為思考的基礎。通常,無須事無巨細地填寫每一部分,不相關的內容直接略過即可。
目標
“我們要解決什麼問題?”
用幾句話說明該設計文檔的關鍵目的,讓讀者能夠一眼得知自己是否對該設計文檔感興趣。如:“本文描述 Spanner 的頂層設計"
繼而,使用 Bullet Points 描述該設計試圖達到的重要目標,如:
- 可擴展性
- 多版本
- 全球分佈
- 同步複製
非目標也可能很重要。非目標並非單純目標的否定形式,也不是與解決問題無關的其它目標,而是一些可能是讀者非預期的、本可作為目標但並沒有的目標,如:
- 高可用性
- 高可靠性 如果可能,解釋是基於哪些方面的考慮將之作為非目標。如:
- 可維護性:本服務只是過渡方案,預計壽命三個月,待 XX 上線運行後即可下線
設計不是試圖達到完美,而是試圖達到平衡。顯式地聲明哪些是目標,哪些是非目標,有助於幫助讀者理解下文中設計決策的合理性,同時也有助於日後迭代設計時,檢查最初的假設是否仍然成立。
背景
“我們為什麼要解決這個問題?”
為設計文檔的目標讀者提供理解詳細設計所需的背景信息。按讀者範圍來提供背景。見上文關於目標讀者的圈定。設計文檔應該是“自足的”(self-contained),即應該為讀者提供足夠的背景知識,使其無需進一步的查閱資料即可理解後文的設計。保持簡潔,通常以幾段為宜,每段簡要介紹即可。如果需要向讀者提供進一步的信息,最好只提供鏈接。警惕知識的詛咒(知識的詛咒(Curse of knowledge)是一種認知偏差,指人在與他人交流的時候,下意識地假設對方擁有理解交流主題所需要的背景知識)
背景通常可以包括:
- 需求動機以及可能的例子。如,“(tRPC) 微服務模式正在公司內變得流行,但是缺少一個通用的、封裝了常用內部工具及服務介面的微服務框架”。- 這是放置需求文檔的鏈接的好地方。
- 此前的版本以及它們的問題。如,“(tRPC) Taf 是之前的應用框架, 有以下特點,…………, 但是有以下局限性及歷史遺留問題”。
- 其它已有方案, 如公司內其它方案或開源方案, "tRPC v.s. gRPC v.s. Arvo"
- 相關的項目,如 "tRPC 框架中可能會對接的其它 PCG 系統"
不要在背景中寫你的設計,或對問題的解決思路。
總體設計
“我們如何解決這個問題?”
用一頁描述高層設計。說明系統的主要組成部分,以及一些關鍵設計決策。應該說明該系統的模塊和決策如何滿足前文所列出的目標。
本設計文檔的評審人應該能夠根據該總體設計理解你的設計思路並做出評價。描述應該對一個新加入的、不在該項目工作的騰訊工程師而言是可以理解的。
推薦使用系統關係圖描述設計。它可以使讀者清晰地瞭解文中的新系統和已經熟悉的系統間的關係。它也可以包含新系統內部概要的組成模塊。
註意:不要只放一個圖而不做任何說明,請根據上面小節的要求用文字描述設計思想。
一個示例體統關係圖
自舉的文檔結構圖
可能不太好的頂層設計
不要在這裡描述細節,放在下一章節中;不要在這裡描述背景,放在上一章節中。
詳細設計
在這一節中,除了介紹設計方案的細節,還應該包括在產生最終方案過程中,主要的設計思想及權衡(tradeoff)。這一節的結構和內容因設計對象(系統,API,流程等)的不同可以自由決定,可以劃分一些小節來更好地組織內容,儘可能以簡潔明瞭的結構闡明整個設計。
不要過多寫實現細節。就像我們不推薦添加只是說明"代碼做了什麼"的註釋,我們也不推薦在設計文檔中只說明你具體要怎麼實現該系統。否則,為什麼不直接實現呢?以下內容可能是實現細節例子,不適合在設計文檔中討論:
- API 的所有細節
- 存儲系統的 Data Schema
- 具體代碼或偽代碼
- 該系統各模塊代碼的存放位置、各模塊代碼的佈局
- 該系統使用的編譯器版本
- 開發規範
通常可以包含以下內容(註意,小節的命名可以更改為更清晰體現內容的標題):
各子模塊的設計
闡明一些複雜模塊內部的細節,可以包含一些模塊圖、流程圖來幫助讀者理解。可以藉助時序圖進行展現,如一次調用在各子模塊中的運行過程。每個子模塊需要說明自己存在的意義。如無必要,勿添模塊。如果沒有特殊情況(例如該設計文檔是為了描述並實現一個核心演算法),不要在系統設計加入代碼或者偽代碼。
API 介面
如果設計的系統會暴露 API 介面,那麼簡要地描述一下 API 會幫助讀者理解系統的邊界。避免將整個介面複製粘貼到文檔中,因為在特定編程語言中的介面通常包含一些語言細節而顯得冗長,並且有一些細節也會很快變化。著重表現 API 介面跟設計最相關的主要部分即可。
存儲
介紹系統依賴的存儲設計。該部分內容應該回答以下問題,如果答案並非顯而易見:
- 該系統對數據/存儲有哪些要求?- 該系統會如何使用數據?- 數據是什麼類型的?- 數據規模有多大?- 讀寫比是多少?讀寫頻率有多高?- 對可擴展性是否有要求?- 對原子性要求是什麼?- 對一致性要求是什麼?是否需要支持事務?- 對可用性要求是什麼?- 對性能的要求是什麼?- …………
- 基於上面的事實,資料庫應該如何選型?- 選用關係型資料庫還是非關係型資料庫?是否有合適的中間件可以使用?- 如何分片?是否需要分庫分表?是否需要副本?- 是否需要異地容災?- 是否需要冷熱分離?- …………
數據的抽象以及數據間關係的描述至關重要。可以藉助 ER 圖(Entity Relationshiop) 的方式展現數據關係。
回答上述問題時,儘可能提供數據,將數據作為答案或作為輔助。不要回答“數據規模很大,讀寫頻繁”,而是回答“預計數據規模為 300T, 3M 日讀出, 0.3M 日寫入, 巔峰 QPS 為 300”。這樣才能為下一步的具體資料庫造型提供詳細的決策依據,並讓讀者信服。註意:在選型時也應包括可能會造成顯著影響的非技術因素,如費用。
避免將所有數據定義(data schema)複製粘貼到文檔中,因為 data schema 更偏實現細節。
其他方案
“我們為什麼不用另一種方式解決問題?”
在介紹了最終方案後,可以有一節介紹一下設計過程中考慮過的其他設計方案(Alternatives Considered)、它們各自的優缺點和權衡點、以及導致選擇最終方案的原因等。通常,有經驗的讀者(尤其是方案的審閱者)會很自然地想到一些其他設計方案,如果這裡的介紹描述了沒有選擇這些方案的原因,就避免讀者帶著疑問看完整個設計再來詢問作者。這一節可以體現設計的嚴謹性和全面性。
交叉關註點
基礎設施
如果基礎設施的選用需要特殊考量,則應該列出。如果該系統的實現需要對基礎設施進行增強或變更,也應該在此討論。
可擴展性
你的系統如何擴展?橫向擴展還是縱向擴展?註意數據存儲量和流量都可能會需要擴展。
安全 & 隱私
項目通常需要在設計期即確定對安全性的保證,而難以事後補足。不同於其它部分是可選的,安全部分往往是必需的。即使你的系統不需要考慮安全和隱私,也需要顯式地在本章說明為何是不必要的。安全性如何保證?
- 系統如何授權、鑒權和審計(Authorization, Authentication and Auditing, AAA)?
- 是否需要破窗(break-glass)機制?
- 有哪些已知漏洞和潛在的不安全依賴關係?
- 是否應該與專業安全團隊討論安全性設計評審?
- ……
數據完整性
如何保證數據完整性(Data Integrity)?如何發現存儲數據的損壞或丟失?如何恢復?由資料庫保證即可,還是需要額外的安全措施?為了數據完整性,需要對穩定性、性能、可復用性、可維護性造成哪些影響?
延遲
聲明延遲的預期目標。描述預期延遲可能造成的影響,以及相關的應對措施。
冗餘 & 可靠性
是否需要容災?是否需要過載保護、有損降級、介面熔斷、輕重分離?是否需要備份?備份策略是什麼?如何修複?在數據丟失和恢復之間會發生什麼?
穩定性
SLA 目標是什麼?如果監控?如何保證?
外部依賴
你的外部依賴的可靠性(如 SLA)如何?會對你的系統的可靠性造成何種影響?如果你的外部依賴不可用,會對你的系統造成何種影響?除了服務級的依賴外,不要忘記一些隱含的依賴,如 DNS 服務、時間協議服務、運行集群等。
實現計劃
描述時間及人力安排(如里程碑)。這利於相關人員瞭解預期,調整工作計劃。
未來計劃
未來可能的計劃會方便讀者更好地理解該設計以及其定位。
我們確實應該把設計限定在當前問題,但是該設計可能是更高層系統所要解決問題的一部分,或者只是階段性方案。讀者可能會對方案的完整性有所疑問,會質疑到底問題是否得到完整解決,甚至會質疑該問題在更高層的系統中是否確實值得解決。“背景(過去)-- 當前方案 -- 未來計劃” 三者的結合會為讀者提供更好的全景圖。
附錄:參考文獻
參考文檔
設計文檔
技術寫作
參考書籍
寫作/表達
- Style, Joseph M. Williams / Joseph Bizup
- 金字塔原理 : 思考、寫作和解決問題的邏輯, 巴巴拉·明托
- 寫作這回事,斯蒂芬·金
- The elements of style, William Strunk Jr. / E. B. White - Coders at work,Peter Siebel 採訪 Joshua Block 原文:Joshua Block: "Another is Elements of Style, which isn’t even a programming book. You should read it for two reasons: The first is that a large part of every software engineer’s job is writing prose. If you can’t write precise, coherent, readable specs, nobody is going to be able to use your stuff. So anything that improves your prose style is good. The second reason is that most of the ideas in that book are also applicable to programs."
- 精準表達,高田貴久
- 寫作提高一點點,Mary-Kate Mackey
- On writing well, William Zinsser
- Artful Sentences, Virginia Tufte
技術寫作/文獻寫作
- 寫作是門手藝,劉軍強
- How to write a lot, Paul J. Silvia
- Writing for Computer Science, Justin Zobel
- The craft of research, Wayne C. Booth / Gregory G. Colomb / Joseph M. Williams
- 會讀才會寫, Phillip C. Shon
- Writing Science, Joshua Schimel
故事
前文強調了要講故事。以下書目闡述了何謂故事、為什麼要講故事及如何講故事:
- Writing Science, Joshua Schimel
- 金字塔原理 : 思考、寫作和解決問題的邏輯, 巴巴拉·明托
- 故事,Robert McKee
- 如何閱讀一本文學書, 托馬斯·福斯特
架構設計
- Software engineering at Google, Titus Winters / Tom Manshreck / Hyrum K. Wright
- Build Secure and realiable systems, Heather Adkins / Betsy Beyer / Paul Blankinship / Piotr Lewandowski / Ana Oprea / Adam Stubblefield
- The Design of Design, Fredrick P. Brooks Jr.
- Fundamentals of Software Architecture, Mark Richards / Neal Ford
圖表
- Storytelling with data,Cole Nussbaumer Knaflic
列表
作者:marinewu本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/How-to-write-a-highly-readable-software-engineering-design-document.html