InfoQ 编辑部出品——2021年度技术盘点与展望 了解详情
写点什么

代码文档的文艺复兴:代码走读

作者:Omer Rosenbaum,Tom Ahi Dror

  • 2021 年 11 月 17 日
  • 本文字数:3153 字

    阅读完需:约 10 分钟

代码文档的文艺复兴:代码走读

我们正在进入一个代码协作的新时代,一个具有实质性的重大变化即将出现。它到底是什么?更重要的是,它为什么会出现?


本文是“持续文档化宣言”的第二部分。我们在宣言中呼吁将创建和维护高质量的文档纳入开发流程。这一次,我们重点关注经常被低估的第三类文档——代码走读文档。


首先,我们一致认为,开发者和开发团队需要好文档。从理论上看,这个很容易做到。我们每个人都写出好文档,一切都会变得更好,不是吗?


事情并没有那么简单。当前的文档并不是为开发者服务的。在大多数情况下,文档不是缺失就是过期,所以开发者不信任文档,或者干脆从一开始就不创建文档。这会影响到整个团队或业务。要想走出这种困局,需要新的方法和工具。

持续文档化宣言回顾

持续文档化方法有助于创建和维护稳定的高质量文档,确保文档化成为开发过程的关键组成部分,就像测试或编码一样。持续文档化依赖三个原则:


  • 与代码耦合——文档应该显式引用部分代码。

  • 保持最新——随着代码的不断演化,持续保证文档的当前状态与代码的当前状态是保持一致的。

  • 即时创建——在知识最“新鲜”的时候创建文档。

常见的文档类型

内联文档(策略性的底层文档,比如代码注释)和高阶文档(可以为你提供更高层的视图)是最为常见的文档类型。但要向希望了解代码逻辑或者要修改代码的人解释清楚,这两种文档并不是很管用。


内联文档


这是一种策略性文档,用于解释代码行或代码块,最常见的是代码注释。例如,有一行代码包含了除数是 24601 的数学运算,注释里解释了为什么是这个除数,那么这个注释就是一种用于解释这行代码的内联文档。它并不会告诉我们更多东西,也不会告诉我们这行代码在一个更复杂的架构中承担了怎样的角色。另外一个例子是位于函数下面的注释,用于解释这个函数做了什么以及它的参数和返回值是什么。这个注释只解释了函数本身,并没有说明它为什么被用在特定的代码流程中。


有些人不写注释,但会尽力提高代码的可读性,我们认为,这也是一种内联文档。


高阶文档


如果说内联文档提供的是细节,那么高阶文档提供的是大视图,可以是整体的代码架构、代码背后的业务逻辑以及与二者有关的重大决策的缘由。有时候,高阶文档用于描述各种代码库或者某个特定代码库的主要模块。在初次了解一个代码库时,这种文档是非常有用的。不过,它的价值也就止于此。因为,它很少会包含与日常任务有关的东西,开发者不会频繁阅读它。


为什么这两类文档不是很管用?内联代码注释描述的是与之相关的代码块,范围狭小。高阶文档可以提供大视图,但缺乏开发者需要知道的细节。例如,在一份有关如何扩展 Git 的文档中,你可以从高阶视图描述创建一个新 Git 命令的过程,但如果缺乏细节,或者没有代码示例,你就不可能实现。这些细节包括文件的路径、函数的签名等。如果你将这些细节加到文档中,那它就不是高阶文档了。

代码走读文档

代码走读文档至少会让阅读代码的人看到代码的两个地方。它们描述了代码流程和交互,可能还会依赖代码片段。换句话说,它们与代码是耦合的(符合持续文档化原则之一)。


这种文档有点类似在有经验的代码贡献者的帮助下熟悉代码库。就像他们会向你解释代码的各个部分一样,代码走读文档做的是同样的事情。


下面列出了三个例子。


反复出现的代码模式


所谓反复出现的代码模式,可以是添加一个配置文件、继承一个类、创建一个新命令或者从数据库读取数据。这些模式都是很小的模式,很容易理解,通常涉及多次调用一个函数(调用日志函数也是一种模式)。更有意思一点的模式是跨多文件模式。因为这些模式在代码中多次出现,很有必要了解它们。阅读代码的人必须对整体有所了解,还需要知道特定的实现细节。


以给 Git 的 CLI 添加新命令为例。假设你是 Git 的贡献者,想要创建一个新命令,比如git new-command。我们来看一下如何添加这样的一个命令。我们不会涉及太多的细节,因为这不是这篇文章的重点,我们也不是要介绍 C 语言。


为了了解如何增加一个新命令,我们先看一下现有的例子——`git


add命令。这个命令的入口点位于builtin/add.c中,因为每一个命令在builtins`目录都有一个与之对应的文件。


int cmd_add(int argc, const char **argv, const char *prefix)    {
复制代码


这个函数的实现细节并不会影响对这个模式的理解,关键的是这个函数的签名——因为所有命令都有相同的参数。


函数签名声明需要被包含在builtin.h中:


int cmd_add(int argc, const char **argv, const char *prefix);
复制代码


但模式并未结束。要让 Git 知道add命令的存在,需要在git.c文件的commands[]数组中加入一个cmd_struct


static struct cmd_struct commands[] = {    { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },};
复制代码


为了能够构建项目,我们必须在 Makefile 文件的BUILTIN_OBJS中添加这个命令:


BUILTIN_OBJS += builtin/add.o
复制代码


注意,这里有很多与代码库耦合的东西,并带有很多特定的细节:cmd_前缀、文件目录结构(特别是builtin目录)、命令函数的签名、文件名称(builtin.hgit.c等)、变量名称(BUILTIN_OBJS),等等。阅读代码的人还能看到一个跨四个文件的模式,尽管从逻辑上讲它们都与创建一个新的 CLI 命令相关。


直接在一个单独的文档中写明这些有助于理解这个模式:



当然,这是一个简单的例子。对于更为复杂的模式和跨更多文件的情况,代码走读文档的作用就愈加凸显。


代码不同部分之间的交互


代码的某些部分(甚至是关键部分)可能难以理解,因为涉及各个部分之间的交互,而且交互细节不是那么显而易见。以一个简单的 Web 应用为例,为了了解它的流程,我们需要在前端和后端之间跳来跳去。特别是对于一个项目的新人来说,一份解释代码不同部分之间交互的文档可以显著降低他们上手的门槛。


多代码库或服务之间的流程


如果代码跨多个代码库,那么上述的例子就会变得更加复杂。假设前端代码位于一个代码库中,后端位于另一个代码库中。随着涉及的代码库数量的增加,情况会变得越来越糟糕。如果有一份文档对各个相关代码库的代码做了说明,理解整个流程就会容易得多。


那么这些东西有哪些共同点?


  1. 它们对代码做了解释,不仅仅是代码的某个方面。

  2. 它们包含了可用于理解代码的信息。

  3. 当相关的代码发生变化,它们就过时了。


第三点也是为什么如果没有持续文档化就很少会有这类文档,因为开发者倾向于一开始就不创建它们。


不管怎样,创建代码走读文档是一种善意的行为,不管是对你的团队还是对未来的你来说都是好事。如果没有代码走读文档,代码膨胀会成为一个需要你花费数月时间去解决的繁琐流程,还会导致知识孤岛在企业中蔓延。

结论

为什么很难找到详细的描述代码流程和交互的文档?这个与任何一段长期、稳定的关系一样,如果你不持续投入,它就会分崩离析。对于大多数开发者来说,当代码发生变化,用于更新文档的时间、注意力和工作量是很大的开销,他们宁愿选择将时间花在开发新功能和修复 bug 上……


遵循持续文档化实践可以发挥代码走读文档的无限价值。在知识还“新鲜”的时候创建文档,并保持最新,将其作为开发流程的一部分,其价值就会彰显出来。


Omer Rosenbaum 和 Tom Ahi Dror 是Swimm的联合创始人。这家公司专注于在团队之间同步代码。


作者简介:


Omer Rosenbaum 是 Swimm 公司的首席技术官和联合创始人。该公司将持续文档化作为开发生命周期的组成部分。Omer 创立了 Check Point 安全学院,并担任 ITC 的网络安全主管。ITC 是一个教育组织,培训技术方面的人才。


Tom Ahi Dror 是国会预算办公室和 Swimm 公司的联合创始人。该公司将持续文档化作为开发生命周期的组成部分。Tom 是一位在技术、培训、战略和业务发展方面有独特经验的领导者。作为以色列最负盛名的军事学院塔尔皮奥特的毕业生,Tom 后来成为该项目的指挥官。在联合创立 Swimm 之前,Tom 是 ITC (以色列科技挑战)的业务发展副总裁。


原文链接


The Renaissance of Code Documentation: Introducing Code Walkthrough

2021 年 11 月 17 日 09:172562

评论

发布
暂无评论
发现更多内容

【LeetCode】搜索旋转排序数组 IIJava题解

HQ数字卡

算法 LeetCode 4月日更

Python OpenCV 修改一寸照片底色,图像处理取经之旅第 20 天

梦想橡皮擦

Python OpenCV 4月日更

区块链系统的层级架构如何及在供应链中的应用

CECBC

区块链

平面设计之PS(前序)

空城机

PhotoShop ps 4月日更

洞察细节深究业务—数据分析的进阶

小飞象@木木自由

数据分析 数据分析体系 理解业务 数据分析进阶

Nginx 除了负载均衡,还能干点啥

Java小咖秀

nginx 负载均衡

1小时800箱,动力机器人真·搬砖16小时不续航;苹果官宣 WWDC 全球开发者大会召开时间;基于图神经网络的分级相关性匹配

京东科技开发者

人工智能 AI 开发者

白金熊项目奖金制度玩法介绍

飞亚科技

小白都能学会的Java注解与反射机制

陈皮的JavaLib

Java 注解 java反射

区块链商品溯源平台搭建及防伪溯源解决方案

13828808769

商品溯源

「开源免费」基于Vue和Quasar的前端SPA项目crudapi后台管理系统实战之用户登录(二)

crudapi

Vue Cookie crud crudapi quasar

带你厘清事务一致性(上篇)

小舰

4月日更

对于数据,科技小白提出了灵魂三问:从哪儿来?到哪儿去?能干什么?

华为云开发者社区

人工智能 安全 数据 数据采集 网络人工智能

面试笔记(二)线程池连环炮

U+2647

Thread 4月日更

GitHub 如何修改默认的分支

HoneyMoose

架构实战营模块一作业

sandy

Crash 游戏是公平的吗?

飞亚科技

架构实战营模块1作业

小荷才露尖尖角

架构实战营

模块一课后作业

王瑞强

架构实战营

斩获三项大奖!浪潮云精彩亮相数字化转型发展高峰论坛

浪潮云

云计算

让云原生应用的交付变得更简单 | KubeVela v1.0 正式发布

郭旭东

Kubernetes 云原生 OAM KubeVela

Linux find 命令

一个大红包

4月日更

ES9的新特性:正则表达式RegExp

程序那些事

正则表达式 ecmascript 程序那些事 es9

区块链BaaS平台的搭建,底层BaaS服务平台的开发

13828808769

行业资讯 产品资讯

区块链让版权与创作如影随形 应用深度逐渐加强

CECBC

产权保护

nsqlookupd:高性能消息中间件 NSQ 解析

华为云开发者社区

集群 消息中间件 nsq nsqlookupd

openresty源码编译

上海派拉基础研发

nginx openresty

猪肉价格跌至去年最低水平 区块链养猪成热点

CECBC

农业

MUSO挖矿系统开发

飞亚科技

架构实战营第一模块作业

DZ

提升漏洞修复率,DevSecOps真的很有一套

华为云开发者社区

软件 安全 DevSecOps 漏洞修复 软件漏洞

代码文档的文艺复兴:代码走读-InfoQ