程序员和项目经理有时认为“文档驱动开发”这个短语意味着在代码中添加大量注释,或者在开发过程中与文档编写人员紧密合作。这是因为很难想象开发如何在文档之后发生,因为肯定要等到实际有东西要记录时才能编写文档。
传统上,文档被视为一种新闻事业。文档编写人员拿到一些软件,然后在实验室里摆弄它,直到他们弄清楚所有内容,然后他们将其写下来,供其他人永远不读。
这忽略了一个至关重要的过程,这个过程在开发人员一天晚上闲来无事喝着咖啡,突然产生一个应用程序的想法时自然而然地发生。开发人员可能没有意识到,但随着想法的形成,一种文档编制工作已经在发生了。想法并非凭空出现,每个细节都已在整洁的蓝图中绘制出来,随时可以连接到孤单的代码行。想法是逐渐产生的。
代码可以说是一种艺术形式,所以这里用艺术界的一个类比。
静物画代表物理世界中的事物。但静物画首先从抽象的形状开始,然后逐渐细化成可识别的东西,然后再添加纹理、阴影等等。
这个过程可以称为自我文档化,因为在绘画生命周期的每个阶段,你都会有一个绘画进展的快照。

Seth Kenlon,CC0
在代码中,这些快照可能是 git 提交加上非常好的注释。虽然这对于稍后加入并想了解代码库的哪个部分负责哪个任务的其他程序员来说很棒,但它通常对普通最终用户没有用处。
在静物画的类比中,这意味着虽然绘画的自我文档化可能对有抱负的艺术家有用,但对目标受众来说并没有太大作用。
绘画的最终用户文档实际上是最终结果:艺术家在绘画时所看到的东西是绘画旨在捕捉的完全实现的图像。如果绘画的“最终用户”想了解绘画主题的最真实形式,最终用户会参考实际的物理对象。
信不信由你,代码也是如此。唯一的区别是正在记录的应用程序尚不存在。但这绝不意味着你不能像它存在一样记录它。
为你想要的应用程序编写文档
当你坐下来编写应用程序时,你对应用程序的预期功能有一些想法。我将以实用程序 trashy 为例,因为它范围狭窄
Trashy is a command-line trash bin.
这通常是开发人员的起点,但如果你实践文档驱动开发,那么这就是你的主页或用户手册的起点,并且它发生在在你坐下来编写代码之前。
如果你的唯一任务说明是“编写一个命令行回收站”,那么你的代码很可能会朝着任何首先想到的方向发展。你可能知道也可能不知道 Free Desktop 回收站规范,因此你可能一开始不会想到遵循其方案。你可能知道已经存在回收机制,因此你的代码的第一个迭代可能只是将文件转储到用户已建立的回收站中,而无需考虑应随附的关联元数据。
但是,如果你先坐下来记录它,你就会被迫考虑细节。例如,将此想象为 trashy 文档的初稿
Trashy is a command-line trash bin.
trash foo : moves foo to system trash
trash empty : empties system trash
它已经比最初的概念更强大了,因为它还提供了一种清空回收站的机制,而不仅仅是将文件发送到回收站。而它之所以存在,仅仅是因为记录用户如何与你的应用程序交互的行为迫使你从用户的角度思考应用程序。
这就像是绘画和实际将绘画挂在画廊墙上供其他人观看之间的区别的预演。
文档作为框架
当你继续为你的命令行回收站应用程序编写文档时,你最终会想到其他“显而易见”的期望,作为用户而不是开发人员,你会对这样的工具抱有期望。你会想到约定,例如能够列出当前回收站中的文件,甚至恢复错误移动到回收站的文件(这反过来可能会引导你了解 Free Desktop 回收站规范,这将使你了解在将文件移动到回收站时应写入的元数据)。
更好的是,你的文档现在是你的伪代码。你的应用程序开发已经从编写在你进行第一次修订时被丢弃的代码,转变为拥有代码的骨架,可以以此为基础进行构建
while [ True ]; do
# trash --help: print a help message
if [ "$1" = "--help" -o "$1" = "-h" ]; then
echo " "
echo "trash [--empty|--list|--restore|--version] foo"
echo " "
exit
# trash --list: list files in trash
elif [ "$1" = "--list" -o "$1" = "-l" ]; then
list
shift 1
# trash --version: print version
elif [ "$1" = "--version" -o "$1" = "-w" -o "$1" = "--which" ]; then
version
shift 1
# trash --empty
elif [ "$1" = "--empty" -o "$1" = "-e" -o "$1" = "--pitch" ]; then
empty
# trash --restore: restore a file to original location
elif [ "$1" = "--restore" -o "$1" = "-r" ]; then
RESTORE=1
shift 1
# trashy foo: moves foo to trash
else
break
fi
done
# more code here...
文档作为路线图
这是一个简化的示例,但对于更大的项目,这些原则甚至更重要,而且文档通常来自并非也是开发人员的人。结果是更集中的开发周期,因为开发人员不是朝着对应用程序的模糊想法前进,而是朝着应用程序预期工作方式的特定蓝图编写代码。每个菜单、每个按钮、每个上下文菜单都已在虚构应用程序的文档中绘制出来。开发人员需要做的就是填写代码。
敏捷的道路战士
如果所有这些听起来都非常刻板和不灵活,请不要假设用文档驱动开发不符合敏捷挑战。敏捷开发方法需要来自用户和利益相关者的反馈,以指导下一步开发的方向。文档不会改变这一点。事实上,在最好的文档驱动开发环境中,文档本身的处理方式与源代码完全相同。它被提交到与其余源代码相同的存储库,并且在其他任何事情之前进行更新。
事实上,将文档视为错误报告和功能请求的第一层是防止项目 UI 蔓延的好方法。当用户在一个看似无害的 UI 更改中请求一个看似无害的更改时,UI 可能会迅速开始带有委员会设计的经典标记。这是因为,如果程序员添加了所有请求的内容,而没有通过对全局有看法的人进行审查,那么实际上就是这样。
将文档用作持续设计过程的一部分是有道理的。这是一种廉价的原型制作。五个错误请求在一个旨在成为干净的单按钮启动面板的主面板上添加一个新按钮,相当于六个按钮使曾经简洁干净的面板变得杂乱。但是,如果启动面板首先经过文档记录和审查,它们就不会出现在印刷品上。
文档,就像编码一样,不是在应用程序生命周期的早期完成的一次性过程。你的项目文档是一个活文档,与代码相同,不断更新和修订,以反映开发计划和项目的当前状态。
一致性
文档倾向于在应用程序的工作方式中产生一致性,因为内部逻辑已提前很好地规划出来。
在应用程序开发的第一周,很容易将一个功能编码为按钮单击,然后在第八周,当 UI 中的空间有限时,将一个同样重要的功能放到一个不起眼的右键单击菜单中。
当你为尚未有实际空间的东西编写文档时,这样做就更难了。在文档编制阶段,一切都是虚构的,因此你会看到为一项任务提供按钮,但隐藏相关任务的逻辑漏洞。当修复它只需快速重写一段段落时,它不会长时间保持这种状态,这与更改许多文件中的多个代码块(并可能重新设计你已经花费数周时间完善的整个 UI)截然不同。当你只是编写文档时,你可以编写任何你想要的东西;这是一种低成本的修复,最终它为开发人员构建提供了更好、更智能的蓝图。
今天就试驾文档
文档驱动开发可能最棒的事情是没有入门门槛。任何非程序员都可以为一个不存在的应用程序发明文档,而且它出奇地有用。我在电影行业和教育部门编写的应用程序都是由其他人设计的。当然,仍然存在来回的用户测试和对纸面上看起来不错但最终运行起来不如设计师希望的那样流畅的东西进行改进,但这远少于没有设计师的东西。当然,有时非程序员会梦想出超出范围的东西,并且必须将其拉回正轨,但有时这会导致开发人员学习一些新技巧,以使以前认为无法实现的东西实际工作。
无论你是开发人员还是只是有一些好主意的用户,都可以坐下来为你希望看到的应用程序编写一些文档。或者,为你认为可以做得更好的现有应用程序版本编写一些文档。你会惊讶地发现它对你思考软件、直观设计和开发的方式产生了多大的影响。
5 条评论