文檔同構:文檔與代碼的雙向綁定

先說一下對於結論的定義:

文檔同構是一種將代碼與文檔保持一致的技術理念,它能讀取格式化的文檔,並將文檔自動加入到代碼中,如以註釋的形式或者是隻在 IDE 呈現;同時,還能將讀取代碼中的文檔,自動更新到文檔中,或是對文檔進行測試和差異對比。

引子 1:文檔與註釋的痛點

最近,我邊設計架構描述語言 Forming,邊圍繞於這個概念體系編寫新書。期間,在翻閱了一系列的架構書籍,如在《領域驅動設計》的 Highlighted Core 一節中提出了一個 “精煉文檔” 的概念。

精煉文檔是編寫 一個非常簡短的文檔(3 ~ 7 頁,每頁內容不必太多),用於描述核心域和核心元素之間的主要交互過程 。

寫文檔的痛苦,我想大部分程序員是懂得的,它的痛苦主要體現在兩方面:自己不想文檔、自己想看文檔的時候沒有。而如書中所說,獨立文檔的常見風險主要是在兩個方面:

  1. 文檔可能得不到維護

  2. 文檔可能沒有人閱讀

  3. 由於有多個信息來源,文檔可能達不到簡化複雜性的目的

同樣的,對於代碼中的註釋來說,問題是相似的,可以說:註釋即文檔。而且,另外一個常見的問題是,項目中可能即有文檔,還有註釋,又有代碼,三者的不一致性是一個更嚴重的問題。

因此,在這篇文章裏,我們將探討一下文檔同構,即如何實現註釋與文檔的自動化同步。事實上,我一直在想的是,註釋 - 代碼 - 文檔的一致性檢查,可能我會在下一篇文章裏,結合契約式設計一起討論。

引子 2:正向生成與反饋設計

在設計時,諸如於 Forming 或者 UML 這樣的架構設計工具時,我們所做的事情是:正向生成,諸如於通過 UML 直接生成相關的代碼。從流程上,它一般是:

  1. 編寫、設計 DSL。設計描述這個領域的 DSL(領域特定語言)。

  2. 編寫代碼。編寫編程工具提供的 DSL 描述系統 。

  3. 工具生成代碼。即工具解析相關的 DSL,並生成目標系統上的代碼。

在演進時,諸如於採用 ArchGuard 或者 Guarding 這樣的架構守護工具時,我們所做事情是:反向設計,即分析代碼將其與系統原先的設計進行對比。從過程上,它一般是:

  1. 自動化分析。尋找合適的工具對開發人員編寫的代碼進行自動化分析。

  2. 設計比對。將分析完的數據結構與 DSL 生成的數據結構進行對比。

對於文檔來,它也應該如此,所以我們可以設計一個文檔工具,用來進行註釋的自動生成,並識別系統中的註釋,從而與原來的文檔進行比對。

文檔同構

基於上述的兩個基本的思想,我們就可以定義出文檔同構的概念:

文檔同構是一種將代碼與文檔保持一致的技術理念,它能讀取格式化的文檔,並將文檔自動加入到代碼中,如以註釋的形式或者是隻在 IDE 呈現;同時,還能將讀取代碼中的文檔,自動更新到文檔中,或是對文檔進行測試和差異對比。

在起初我構思的時候,我只想把這概念用在註釋與代碼的自動化同步上,而隨着我對於相關內容的進一步深入瞭解,我發現這是一個很有的東西,我將會在後續的模式上展開相關的介紹 。

在我設計 Forming 實現時,我嘗試着去總結了一些要點:

  1. 高亮核心。即區分核心域與通用域,將重要精力投入到系統的核心部分設計。

  2. 代碼與文檔雙向綁定。即上一部分所說的正向生成與反饋設計。

  3. 文檔代碼化。即設計領域特定語言來描述用描述,通過結構化的形式來實現與代碼的同構。

高亮核心:區分核心域與通用域

在一個系統中,它必然會充斥着大量的領域相關的概念,我們無法展開每一個的概念的討論。所以,在設計的時候,我們向《領域驅動設計》一書所說,提煉出系統的核心部分。結合我們在進行統一語言相關設計時,會採取詞彙表相關的概念。所以,在這部分的設計裏,它由兩部分組成:

由這兩部分的文檔,形成系統的代碼與文檔的映射。

代碼與文檔雙向綁定

對於文檔同構工具來說,它的難點依舊是:

  1. 編程語言的解析。即生成代碼的定製數據模型,記錄關鍵的概念所在行數、文件、位置等相關的信息,以便於自動修改。

  2. 代碼與文檔的顯示與更新機制。即我們是否顯示文檔,是否需要對文檔進行校正等。

從實現來說,現有的技術都已經比較成熟了。

文檔代碼化:領域特定語言設計

最後,再回顧一下我對於文檔代碼化的定義:

文檔代碼化,將文檔以類代碼的領域特定語言的方式編寫,並借鑑軟件開發的方式(如源碼管理、部署)進行管理。它可以藉助於特定的工具進行編輯、預覽、查看,又或者是通過專屬的系統部署到服務器上。面向非技術人員的文檔代碼化的一種常見架構模式是:編輯 - 發佈 - 開發分離』

在那篇《文檔代碼化的文章裏,我們定義了文檔代碼化的三個主要特徵:

在這種模式下,我們也可以支持起多個代碼倉庫,諸如於微服務架構的系統。

文檔同構模式

在過去的一段時間裏,在思考這個設計的時候,我便在思考文檔和代碼如何相處,便也順便總結了一些模式。

文檔同構文檔模式:文檔測試

Rust 對文檔的哲學,是不要單獨寫文檔,一是代碼本身是文檔,二是代碼的註釋就是文檔。Rust 不但可以自動抽取代碼中的文檔,形成標準形式的文檔集合,還可以對文檔中的示例代碼進行測試。

去年,我思考文檔代碼的主要原因是看到了 Rust 中的文檔測試: rustdoc 支持執行文檔示例,作爲測試,以此可以確保文檔是最新的和有效的。

嗯,我們所做的模式,就是在這的基礎之上,做一些升級,即將業務概念文檔同步到代碼中。

文檔同構模式:可執行的文檔

可執行的文檔即文檔是可編譯、可直接運行的。這個是在看到 rustdoc 之後,我嘗試性地編寫了 Exemd 項目。可以自動化 Markdown 文件中中的一些代碼,支持:Rust、Ruby、JavaScript、TypeScript 等語言,並支持依賴的形式,即可以通過 // 引入了 Rust 的 color 依賴。

// exemd-deps: colored;version=1.8.0
extern crate colored;
use colored::*;
fn main() {
println!("{} {} !""it".green()"works".blue().bold());
}

而這種模式是以文檔爲主的模式,在我最初的設計裏,它還可以直接 import 可執行的源碼。它適用於我在寫文章和寫書的模式下進行的。

文檔同構模式:代碼註釋分離

代碼註釋分離,即我們可以不需要在代碼裏寫註釋,註釋是寫在代碼中的其它地方註釋,是寫給人看的,諸如於採用後續的 IDE 呈現的方式。

文檔同構模式:IDE 自動呈現註釋

這個模式之下,註釋是以文檔的形式存在的,但是不編寫在代碼中,是獨立存在的。我們可以使用 IDE 插件方式加載註釋。

基於雲 IDE 的理念之下與及 雲研發架構模式,它就可以解決文檔在傳輸中不存在的問題。

其它

自我開始研究 “雲研發” 以來,我一直在研究對軟件研發的代碼化,從各類的自動化到各類的代碼化,如設計各類的領域特定語言。文檔也是其中的重要一環,我們的目的應該是:註釋 - 代碼 - 文檔自動一致性。

歡迎圍觀 Forming:https://github.com/inherd/forming

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/kQPDOgb030eE9r0vxMmYpw