活文档总结
幾年前在 oreilly 看到一本叫 《living documentation》的書(shū),可惜當(dāng)時(shí)爛尾了。
最近圖靈出版了該書(shū)的中文翻譯版,才想起來(lái)有這么回事。。。正好最近有時(shí)間,把這個(gè)坑了幾年的東西給填上了。
簡(jiǎn)單來(lái)說(shuō),這本書(shū)基本可以叫 living java doc(誤。我們可以先看看日常開(kāi)發(fā)中會(huì)涉及到哪些文檔:
需求文檔
接口文檔
業(yè)務(wù)詞匯表文檔
模塊業(yè)務(wù)流程文檔
系統(tǒng)間交互文檔
系統(tǒng)架構(gòu)文檔
系統(tǒng)依賴文檔
發(fā)布文檔
示例文檔(tutorial)
項(xiàng)目進(jìn)度文檔
案例文檔
匯報(bào)文檔
要寫的類型文檔還是不少的,無(wú)論哪種文檔,都與項(xiàng)目的代碼是脫節(jié)的。所以軟件行業(yè)以往的文檔編寫方式有很多缺點(diǎn):
文檔易過(guò)時(shí),因?yàn)榕c代碼是分開(kāi)的,改了代碼忘改文檔是很正常的,代碼是新版本,而文檔是歷史版本
文檔誤導(dǎo)人,因?yàn)榍耙粭l,功能上的修改沒(méi)有反映到文檔上,讀文檔的人會(huì)被錯(cuò)誤信息所誤導(dǎo)
多余的工作量,工程師在寫完代碼之后還要再去寫一遍文檔(也可能是先寫文檔后寫代碼),基本是雙倍工作量
文檔的編寫工具無(wú)統(tǒng)一的規(guī)范,大家喜歡不停地嘗試新工具,從 word 到 markdown 到 asciidoc 到 notion
文檔與真實(shí)的代碼內(nèi)容是脫節(jié)的,比如在 DDD 中常講的業(yè)務(wù)詞匯表,在文檔與代碼中可能用的不是同一個(gè)詞,編碼和看文檔的時(shí)候需要腦內(nèi)額外做映射
為了克服這些缺點(diǎn),這本書(shū)提出了一些讓代碼變 living 的方法。既然代碼和文檔分開(kāi)使得兩者不易統(tǒng)一,那么我們就去追求把代碼與文檔寫在一起,并通過(guò)技術(shù)手段把文檔從代碼中生成出來(lái)。
live_doc上面提到的幾種文檔,挨個(gè)來(lái)看看:
需求文檔
因?yàn)樾枨笞罱K會(huì)變成 BDD 測(cè)試,所以可以直接用 BDD 測(cè)試來(lái)生成這些需求文檔。
詞匯表
用 DDD 來(lái)做業(yè)務(wù)的話,一般可以用一些一眼就能看出含義的詞對(duì)類型和接口做注解:@DomainService,@DomainEvent,@BusinessPolicy,@AbastractFactory,@Adapter,@BoundedContext,@ValueObject。
同時(shí)為了強(qiáng)調(diào)一些業(yè)務(wù)概念,也可以用一些自定義的詞匯,比如:@CoreConcept,@CoreBehavior,@StateMachine。
然后通過(guò)掃描代碼,就可以直接把項(xiàng)目中的上述概念抽出,每次代碼有更新,能夠自動(dòng)生成業(yè)務(wù)的詞匯表。
自動(dòng)接口文檔
這個(gè)應(yīng)該很多人也見(jiàn)過(guò)了,就是類似 swagger 的項(xiàng)目??蚣茏龅暮玫钠髽I(yè)基本也都會(huì)有基于類似 swagger 的手段來(lái)做自己的接口平臺(tái)(其實(shí)做的好的沒(méi)幾個(gè))。下面是在 beego 中使用 swagger 的例子:
swagger因?yàn)?Go 語(yǔ)言不支持 annotation,所以這些在注釋里寫的注解沒(méi)有語(yǔ)法層面的檢查和約束,這點(diǎn)上可能比那些支持的語(yǔ)言算是個(gè)弱勢(shì)。
業(yè)務(wù)流程文檔
在代碼中編寫相應(yīng)的流程節(jié)點(diǎn)和前后關(guān)聯(lián)節(jié)點(diǎn):
business_process通過(guò)掃描代碼生成文檔:
business_process2跨系統(tǒng)的交互流程:
business_process3掃描生成相應(yīng)的文檔,其實(shí)和前面的差不多:
business_process4除了上面這些風(fēng)格的注解,以前就已經(jīng)存在從文本生成文檔的手段,比如 plantuml,graphviz
plantuml上圖這樣的 plantuml 的文本,本身就可以和代碼一起在同一個(gè)代碼倉(cāng)庫(kù)中進(jìn)行管理。
在一些業(yè)務(wù)邏輯中,涉及到難懂的邏輯,可以用 @Policy 或者 @BusinessConvention 來(lái)說(shuō)明相應(yīng)的業(yè)務(wù)決策原因。
發(fā)布文檔
現(xiàn)在 Github 上的很多開(kāi)源項(xiàng)目,已經(jīng)可以根據(jù) commit message 自動(dòng)生成 release note 了。這歸功于其 commit message 本身就有一定的規(guī)范:
commit_msg只要用簡(jiǎn)單的 bash 腳本就可以將兩個(gè)版本之間的 commit message 進(jìn)行匯總,并寫出完整規(guī)范的 release note。
國(guó)內(nèi)不少開(kāi)源項(xiàng)目現(xiàn)在還是人肉寫 release note 的。
tutorial
新人入職需要看一些代碼范例,如果項(xiàng)目?jī)?nèi)有寫的規(guī)范的代碼,可以專門挑出來(lái)作為例子:@Exemplar(“這個(gè)訂單流程是一個(gè)很好的 CQRS 學(xué)習(xí)示例”)。掃描代碼找到所有的 @Exemplar 注解就可以成為 tutorial 文檔了。
穩(wěn)定文檔
穩(wěn)定文檔指的是那些基本不怎么變動(dòng)的文檔,這種文檔和代碼分開(kāi)是可以接受的,為了使我們的文檔穩(wěn)定,應(yīng)該遵循一些基本的編寫原則,比如:
不要帶公司的那些容易變化的信息:公司名,子公司,品牌,商標(biāo)等等
如果在文檔中附帶有鏈接,應(yīng)該從非穩(wěn)定文檔指向穩(wěn)定文檔,比如你可以從自己的 blog 貼一個(gè) wikipedia 的鏈接,但是如果你去編輯 wiki 并粘貼了個(gè)人獨(dú)立 blog 的鏈接,那么可能就不太合適
不要復(fù)制粘貼,盡量用鏈接引用原有內(nèi)容
匯報(bào)文檔
如果是項(xiàng)目進(jìn)度相關(guān)的內(nèi)容,可以用 @wip 或 @pending 之類的注解。
如果是給老板展示的案例,老板忙的沒(méi)時(shí)間,所以可以用 @keyexample 把這些需要單獨(dú)展示的案例摘出來(lái),
如果是給新人學(xué)習(xí)的,除了前面說(shuō)到的 tutorial,還可以有 @normalcase,@specs 等。
架構(gòu)文檔
一種是單模塊架構(gòu),常見(jiàn)的是 DDD 中的六邊形架構(gòu),可以引入 DDD 中的關(guān)鍵詞,并生成最終的文檔,比如 @HexagonalArchitecture.DomainModel,@Adapter,最終生成的文檔是下面這樣的:
hex如果是跨系統(tǒng)的文檔,比如 CQRS 之類的,本身也是 Given(event),When(cmd),Then(output event) 的形式,所以用 BDD 可以生成這樣的文檔,前文有提到。
運(yùn)行時(shí)文檔
作者認(rèn)為 zipkin 和 dapper 這種 tracing 系統(tǒng)也是一種形式的文檔。服務(wù)發(fā)現(xiàn)系統(tǒng)中繪制出的服務(wù)依賴也是一種文檔。
聲明式配置
聲明系統(tǒng)配置、資源需求、依賴的,也可以生成文檔。
從這個(gè)意義上講 k8s 的 yaml 既是文檔,也是代碼。
架構(gòu)全景圖
當(dāng)我們可以將所有文檔都通過(guò)掃描代碼生成之后,最好能夠?qū)⑦@些文檔管理在統(tǒng)一的架構(gòu)全景圖中,相應(yīng)的全景圖可能也可以用一些注解的輔助手段來(lái)生成,比如 @Layer(LayerType.INFRASTRUCTURE),@Repository(aggregateRoot = Customer.class)。
總結(jié)
- 上一篇: 改来改去把微服务改成了分布式单体
- 下一篇: 几十万实例线上系统的抖动问题定位