文檔代碼化:重塑軟件開發的文檔系統

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

最近一個月裡,我在開發一個基於 Git + Markdown 的全新文檔系統。我定製了一個基於 markdown 的標記語言,以支持起雷達圖、條形統計圖、思維導圖等圖表的文檔系統。這個系統將在未來幾個月內發佈。當然了,視進度而看,也可能是月底。

過去的幾年裡,我們一直在討論各種各樣的代碼化,基礎設施代碼化、設計代碼化、需求代碼化……。在我的那一篇《雲研發:研發即代碼》中,設計了一個完全代碼化的軟件開發流程。而今天我們將討論另外一個有趣的存在:文檔。

在《架構金字塔》中,我將文檔定義為支撐五層架構模型的一種存在。因為文檔在一個系統中是非常重要的存在,我們用它來指導開發工作,用它來記錄問題,用它來寫下規範……。總而言之,它很重要,所以我們重新討論一下這個話題。

引子 1:架構決策記錄:格式化文檔

文档代码化:重塑软件开发的文档系统

三年前,當我第一次接觸到『架構決策記錄』的概念時,我被它的理念所吸引:

  • 使用輕量級文本格式化語言描述重大決策

  • 跟隨代碼一起版本化

  • 使用某種特定的文檔格式(標題、上下文、決策、狀態、後果)

隨後,我使用 Node.js + TypeScript 寫了一個 ADR 工具。現在,在我的大部分開源薦中,我都會使用它來管理一些技術決策。因為基於這個理論設計的這個文檔系統真非常棒,我可以查詢到:

  • 一個技術決策發生的時間和架構改變,對應的修改人

  • 回溯所有的技術決策,從中整理出架構發展過程

  • 所有的決策都是記錄在版本控制系統中,可恢復

  • 易於管理和維護

對於一個長期開發的系統來說,它真的非常有用。

引子 2:靜態站點生成:數據代碼化

文档代码化:重塑软件开发的文档系统

靜態站點生成是一種混合式的 Web 開發方法,它通過部署預先構建的靜態文件進行部署,來讓開發者在本地構建基於服務器的網站。

當 GitHub Pages 成為了程序員首選的 博客/內容/文檔 服務器時,他/她也採用了靜態站點生成這一項技術。靜態站點生成有各種各樣的優點:

  • 可靠性、安全性、穩定性、可用性等更好

  • 可版本控制

  • 易於測試

  • 易於實踐持續部署。提交即可上線

  • 靈活,易於定製

而事實上,靜態站點生成所做的最主要的一件事是:將數據庫中的數據進行代碼化。採用諸如 Wordpress 這樣的 CMS 時,我們是將數據存儲在數據庫中,以實現對於數據的 CRUD。一篇文章變為數據庫二進制文件中的一個片段。

隨後,靜態站點生成工具做了第二件事情便是將文本內容可視化出來,便於人們閱讀。這樣一來,我們便實現了發佈-開發分離。

引子 3:定製的標記語言:擴充

文档代码化:重塑软件开发的文档系统

將數據代碼化時,我們面臨了一個非常大的挑戰:易於編寫、閱讀的標記語言(如 markdown)只設計了內容的形式,缺少了內容相關的其它信息,諸如於創建時間、作者、修改時間等等。

於是各個靜態站點生成器定製了自己的 markdown,添加了一些額外的信息,如 hexo 採用 <code>:year-:month-:day-:title.md/<code>的形式來管理文章的日期和標題等。這樣一來說,就不需要通過讀取這個文章的 Git 信息來構建出整個信息。

我們所熟悉的 GitHub Flavored Markdown 也是如此,通過不明顯破壞內容格式的兼容模式來擴展 markdown 數據字段。

除此,我們可以定製基於 markdown 數據的圖表、思維導圖等內容。

引子 4:編輯-發佈-開發分離:面向非技術人員

文档代码化:重塑软件开发的文档系统

面向非技術人員設計是代碼文檔化的一大挑戰。作為一個程序員,我們覺得 markdown 語法再簡單不過了,但是對於非技術人員來說並非如此。他/她需要:一個易於上手的可視化編程器。而要實現這樣一個目的,我們需要在架構上做一些轉變,我們可以嘗試使用 『編輯-發佈-開發分離』 模式來解決這個問題。

即,我們將過程拆為了三步:

  • 編輯人員,可以使用常用的編輯器或者是定製的編輯器

  • 開發人員,編寫內容的展示

  • 發佈的時候,集成這兩部分代碼

我們依舊可以選擇用源碼管理的方式來管理內容。只需要將數據庫接口,轉變為 Git 服務器接口即可 —— 當然它們是稍有不同的。不過呢,把本地的 Git 轉換為 Git remote 那就基本一致了。

如此一來,最後我們的成本就落在改造出一個基於 Git 的 markdown 編輯器。

文檔代碼化

文档代码化:重塑软件开发的文档系统

完美,我又一次在引子裡,把中心思想表達完了。

為什麼你需要將文檔代碼化?

主要原因有:文檔不代碼化,就沒有重構的可能性。

剩下的原因有:

  • 二進制的文檔難以進行版本管理。想象一下 <code>2020-02-30.docx/<code>和<code>2020-02-31.docx/<code>。

  • 無法準確地知道誰是文檔的修改者,大家可能都是 admin,又或者是會議上的張三

  • 找不到哪個是最新的文檔

  • 文檔寫得很爛,但是你沒辦法重構二進制文檔

  • 供應商綁定

  • ……

應該還有更多。

什麼是文檔代碼化?

回到正題上:

文檔代碼化,將文檔以類代碼的領域特定語言的方式編寫,並借鑑軟件開發的方式(如源碼管理、部署)進行管理。它可以藉助於特定的工具進行編輯、預覽、查看,又或者是通過專屬的系統部署到服務器上。

它具備這麼一些特徵:

  • 使用標記語言編寫內容。如 markdown

  • 可通過版本控制系統進行版本控制。如 git

  • 與編程一致的編程體驗(除了內容寫不了測試)

而一個高效的文檔代碼化系統,還具備這麼一些特徵:

  • 持續部署,即修改完內容可自動發佈。

  • 與特定的形式組織內容索引。如以知識庫的形式來組織內容。

  • 特定的文本格式。如架構決策記錄、靜態內容生成,以用於以提供更好的用戶體驗

  • 可支持 REST API。以通過編輯器來修改內容

  • 可以支持多種方式的輸出。如網站標準 HTML,又或者是 Docx、Latex 等

  • 支持搜索

  • 多人協作

而事實上,大部分的團隊並不需要上述的高級功能,而且它們都已經有了成熟的方案。

如何設計一個文檔代碼化系統?

事實上,我們在四個引子中標明瞭我們所需要的要素:

  • 使用格式化的文檔

  • 藉助靜態站點生成技術來發布系統

  • 通過定製標記語言擴充能力

設計一個標記語言及其擴展語法,然後實現它即可。

1. 確立關鍵因素

考慮到我和我的同事們最近實現了這麼一個系統,我還是忍受一下手的痛楚,簡單說一下怎麼做這樣一個系統。我們所考慮的主要因素是:

  • 圖表渲染

  • 流程圖渲染

  • 可視化展示

因為由 DSL 轉換成的圖表易於修改,並且可以索引。於是乎,我們:

  • 通過 markdown 的 Code 語法來擴充這些能力

  • 使用 markdown 的 table 和 list 來提供數據

  • 使用 D3.js 來支持流程圖繪製

  • 使用 Echarts 來進行圖表繪製

  • 儘量使用 SVG 圖片

  • ……

2. 實現一個 MVP

我們使用 Angular + GitHub,快速實現了一個 MVP:

  • 我們使用 Git 作為數據庫.它就可以實現多人協作的目的,並且可以追蹤所有的變化

  • 我們使用 GitHub Pages 作為服務器。只要一修改文檔或者代碼,就會部署最新的文檔。

  • 我們使用 marked.js,它可以讓我們快速擴展語法。

  • 使用 textarea 結合 markdown 製作了一個簡易的編輯器。

隨後,我們在這個基礎上進行了快速的迭代。

3. 擴展語法

我們使用了 markdown 的 <code>code/<code>作為圖表的 DSL,擴展了這麼一些語法:

  • echarts。直接渲染 Echarts 圖表

  • mindmap。Markdown List 轉為思維導圖

  • radar。Markdown List 轉為雷達圖

  • process-table。帶流程的圖表

  • process-step。另外一種帶流程的圖表

  • pyramid。金字塔圖形

  • quadrant。四象限圖

  • class。直接調用 CSS 的 class

  • graphviz。使用 Dot 渲染圖片

  • mermaid。使用 mermaid 可視化

  • webcomponents。調用 WebComponents 組件

  • toolset。調用 Toolset 相關的組件

  • slider。權衡滑塊

  • line-chart。表圖

所以,對於使用者來說,只需要編寫下面的代碼:

  1. <code>```radar/<code>

  2. <code>- 質量成熟度評估模型/<code>

  3. <code>- 質量內建: 3 -> 4/<code>

  4. <code>- 優化業務價值: 2 -> 2/<code>

  5. <code>- 質量統一,可視化: 1 -> 5/<code>

  6. <code>- 全員參與: 3 -> 4/<code>

  7. <code>- 快速交付: 4 -> 5/<code>

  8. <code>- 測試作為資產: 2 -> 3/<code>

  9. <code>- 快速反饋: 5 -> 5/<code>


  10. <code>config: {"legend": ["當前", "未來"]}/<code>

  11. <code>```/<code>

就可以生成對應的圖表:

文档代码化:重塑软件开发的文档系统

<figcaption>RadarChart/<figcaption>

我們還通過 config 來輸入 JSON,進行一定的懶惰化處理(不要累死自己)。

3.1 重寫 markdown 渲染器

我們在這個過程中,遇到的最大的挑戰是,隨著我們對 markdown 語法的不斷擴充,相關的代碼已經變成了一坨大泥球。所以,我們不得不重寫了這部分代碼:

藉助於 marked.js 的 lexer 解析出 token

根據 token 修改生成新的 token

遍歷新生成的 token,渲染出元素

結合虛擬滾動,解決性能問題

已經開源在 GitHub,併發布對應的 npm 包: <code>@ledge-framework/render/<code>。

4. 發佈這個項目

我們已經在 GitHub 上發佈了這個文檔化系統,你可以參與到其中的使用和開發。

GitHub:https://github.com/phodal/ledge

項目首頁:https://devops.phodal.com/

5. 開放與協作

讓每個人都可以訪問,並提出修改意見。文档代码化:重塑软件开发的文档系统

總結

然後,你就成為了一個 Markdown 工程師,D3.js 設計師,Echart 配置管理員。


分享到:


相關文章: