仅有文件即代码是不够的

376 阅读8分钟

文档即代码

这是向正确方向迈出的第一步

把你的文档和程序代码一样对待的做法是朝着正确的方向迈出的一步,但它还远远不是最先进的。这种做法在许多提倡使用文档即代码(DAC)的网站上都有详细介绍。例如,Write the Docs社区有一篇关于docs-as-code的好文章。该文章列出了

  • 问题跟踪器
  • 版本控制(Git)
  • 纯文本标记(Markdown、reStructuredText、Asciidoc)
  • 代码审查
  • 自动测试

作为这种方法的必要工具。另一个例子是docs-as-code,它是一个用于文档维护的工具集。他们写道

有了docs-as-code,你就像对待代码一样对待你的文档。

你用...

你的IDE来编写它
你的版本控制系统来存储和发布它
你的测试运行器来测试它
你的构建系统来构建和部署它"

这与Write the Docs的方法非常相同:

你必须把你的文件做成DAC工具支持的格式。使用版本控制、文件审查、自动构建、问题跟踪器和自动测试。这与我们在代码开发中使用的方法非常相似。

从本质上讲,它是专业软件开发过程中编码部分的一个副本。然而,文档不是编码。虽然重用编码方法技术的某些部分是个好主意,但它还有更多的内容。

docs-as-code忽略了什么

编码是一个转换过程,将文档,即需求文档,转换为代码。需求文件可能不是传统意义上的文件。它可能是一些笔记,在一张纸上的愿望清单。然而,其本质是将一些人类的亲和力转化为机器的亲和力。一些技术试图支持这个过程,但这些技术大多在生产中死亡。例如,将文档创建为UML,并使其精确到之后的代码自动生成,是不可行的。原则上你可以这样做,但努力的成本太高了。创建代码要比用数学的精度定义功能的文档更便宜。

记录一个应用程序恰恰是一个相反的方向:

在一个方向上失败的东西,当我们试图向另一个方向发展时,不一定会失败。你可能无法从河上跳到桥上,但另一个方向是非常有可能的。

在创建程序文档时,我们的源头正是在描述我们想要记录的功能。毕竟,代码是应用功能的最精确的文档。我们已经有了精确性,这对于另一种方式是不可行的。

大多数文章所描述的 "文档即代码 "的方法,忽略了这一点。然而,它可以被修正,而且应该被修正。我们可以把它看成是docs-as-code进化的下一步。

下一步

我们可以把文档分为两类:

  • 解释性的和
  • 参考性

有时一个文档属于其中的一个类别,但文档在大多数时候都是这两个类别的混合体。甚至可能出现这样的情况:你甚至无法判断一份文件中的某句话是属于哪一类的。

创建一个解释性的文本不可能是自动化的。它需要人的努力来创造清楚和容易理解的句子。你可能已经注意到,本文中缺乏这些内容,这就证明了其重要性。

然而,创建参考文本或多或少是一项机械任务。文档管理员(来自Write-the-docs的一个术语)复制钥匙的名字来解释如何配置系统,并围绕它写一个句子。将单元测试中的一些示例代码复制到Asciidoc文档中的一个代码段中,并添加解释。在这些例子中,参考文献是逐字逐句地从代码中提取的,而解释部分是添加的:

你可以自动复制参考信息。大多数情况下,它是不会自动化的。

问题是,自动化,就像测试的情况一样,比做一次手工工作要贵。当实际操作(复制和粘贴)多次发生时,它就会得到回报。

而且通常是这样。更准确地说,它是应该发生的。然而,在实践中,文件维护会错过任务,文件变得陈旧。这就是 "文档即代码 "自动化测试可以帮助的地方。原则上,可以创建一个测试来检查文档,发现代码中的名称和文档中的不一致之处。它可以是启发式的,也可以是精确的。为了做到精确,文档和/或代码需要元信息来帮助测试进行一致性检查。

这样的测试可以提示文档可能已经过时,需要改变。例如,它可以给出一个警告,如

"字段XYZ的名称与文档中的ZZZ不一样。将文档中的ZZZ改为XYZ"。

这是一个愚蠢的、离谱的错误信息。当我看到这样的错误信息时,我立即知道程序架构被搞乱了。如果测试能如此精确地告诉我该怎么做,它就能以同样的努力来解决问题。

如果我们让自动构建复制实际的名字,而不是检查人类是否做得正确,那会好得多。为了做到这一点,文档管理员应该把元信息放到文档中,而不是复制的值。元信息由自动构建工具读取,并使用该信息;它复制实际的值或数值。

如果值发生变化,构建过程将自动改变它。

另一个优点是出错的可能性较小。如果记录员在复制字段名时出错,文本将不会抱怨。如果他写的是XXY而不是XYZ,那么文档将包含错误的名称,除非某个人工审查过程发现并修复了这个错误。如果文档管理员插入元信息并犯了一个错误,构建过程很可能会失败。如果我不得不写{java:field com.javax0.jamal.api#XYZ}而不是XYZ,那么任何简单的错字都会被发现。如果有一个 com.javax0.jamal.api#XYZ 的字段,就不可能同时有 com.javax0.jamal.api#XXY。

通过这种方法,"文档即代码 "的工作流程得到了扩展。文档的 "源代码 "开始表现得像一个源代码。自动构建不再是简单的格式化和执行语言检查。我们的目标是把所有你能自动化的东西都自动化。这可能不会比手动工作更便宜,但肯定会减少错误。

工具

所有上述理论都是令人愉快和有吸引力的,但除非有工具来实现它们,否则毫无价值。我写这篇文章的动机部分是为了倡导使用开源工具Jamal。尽管Jamal是一种通用的宏语言,可以用于许多领域,但它的主要目的是支持文档维护。它是一种简单的、非侵入式的宏语言。使用它,你可以在文档中插入元信息,由自动构建来处理。你可以将它用于任何纯文本文档格式,如Asciidoc、markdown、apt等。最新的版本还支持DOCX格式,甚至可以在Microsoft Word中使用它。

宏的集合是相当广泛的,而且很容易添加你自己的宏。文档支持模块可以从应用程序的源代码中收集信息作为片段。然后,片段可以被转换、提取并插入到文档中。从代码中的信息可以使用文本工具使用源代码文本进行提取。然而,在Java应用程序的情况下,文档转换也可以使用反射收集信息。可以这样做是因为Jamal本身就是一个Java应用程序。

它可以作为一个maven插件和一个maven扩展在命令行上启动。它还被嵌入为doclet和taglet,允许Jamal在JavaDoc文档中使用宏。

您可以使用宏来检查文档和代码的一致性。你可以将代码的某些部分标记为片段,与特定区域相关的文档可能包含该片段的哈希代码。当该部分在源代码中发生变化时,宏评估将自动发出错误信号。

Jamal的应用是独立于构建自动化的。它可以是antora、jBake,或者只是一个带有不同插件的maven项目。Jamal的应用也与文档格式无关。它可以是Asciidoc、markdown、apt等,只要文档格式是文本。使用命令行版本中的Word扩展,甚至可以是微软DOCX Word格式。

结论

将文档视为源代码是一个好主意,也是一个好的开始。然而,它可以,也应该被扩展,以包括更多的功能。当你把你的文档当作源代码时,你不应该仅仅停留在使用构建的自动化、自动化测试、审查过程和版本管理。你还应该应用像Don't Repeat Yourself(DRY)的技术。额外的工具是存在的,并与已经存在的构建和格式化工具无缝集成,以做到这一点。