Blog

Blog

PHODAL

文档同构:如何实现文档与代码的双向绑定?

先说一下对于结论的定义:

文档同构是一种将代码与文档保持一致的技术理念,它能读取格式化的文档,并将文档自动加入到代码中,如以注释的形式或者是只在 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. 文档代码化。即设计领域特定语言来描述用描述,通过结构化的形式来实现与代码的同构。

高亮核心:区分核心域与通用域

在一个系统中,它必然会充斥着大量的领域相关的概念,我们无法展开每一个的概念的讨论。所以,在设计的时候,我们向《领域驱动设计》一书所说,提炼出系统的核心部分。结合我们在进行统一语言相关设计时,会采取词汇表相关的概念。所以,在这部分的设计里,它由两部分组成:

  • 概念词汇表。可以基于简易的表达方式来维护,诸如 Markdown 里的列表,又或者是 CSV 形式。
  • 精炼文档。从概念上来说,我偏向于使用 DSL 来设计。但是使用 YAML 或者 CSV 的形式,它在解析和维护上会比较简单。

由这两部分的文档,形成系统的代码与文档的映射。

代码与文档双向绑定

对于文档同构工具来说,它的难点依旧是:

  1. 编程语言的解析。即生成代码的定制数据模型,记录关键的概念所在行数、文件、位置等相关的信息,以便于自动修改。
  2. 代码与文档的显示与更新机制。即我们是否显示文档,是否需要对文档进行校正等。

从实现来说,现有的技术都已经比较成熟了。

文档代码化:领域特定语言设计

最后,再回顾一下我对于文档代码化的定义:

文档代码化,将文档以类代码的领域特定语言的方式编写,并借鉴软件开发的方式(如源码管理、部署)进行管理。它可以借助于特定的工具进行编辑、预览、查看,又或者是通过专属的系统部署到服务器上。面向非技术人员的文档代码化的一种常见架构模式是:编辑-发布-开发分离

在那篇《文档代码化的文章里,我们定义了文档代码化的三个主要特征:

  • 使用领域特写语言编写内容。如 markdown
  • 可以通过版本控制系统进行版本控制。如 git
  • 与编程一致的编程体验。

在这种模式下,我们也可以支持起多个代码仓库,诸如于微服务架构的系统。

文档同构模式

在过去的一段时间里,在思考这个设计的时候,我便在思考文档和代码如何相处,便也顺便总结了一些模式。

文档同构文档模式:文档测试

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


或许您还需要下面的文章:

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Developer, Consultant, Writer, Designer

ThoughtWorks 高级咨询师

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 与我沟通

标签

最近的一些事

  • 最近我和我的同事们,一起在创建一个新的编程语言:Charj 。它是一个使用 Rust 编写的描述式、中间编程语言。GitHub: https://github.com/datum-lang/datum

    Nov. 14, 2020, 9:27 p.m. | China