写有价值的技术文档

背景

传统瀑布开发模式下非常重视文档,每个开发环节的衔接都通过文档实现。这种重视在CMMI达到了极致,软件开发的每一步从形式到内容都要求文档化,需要设计者花费大量的精力在文档的撰写和维护上。高度文档化需要投入巨大的成本,这种成本在相对固定,变化较少的问题域(如传统的制造、管理)可以从软件后期的维护收益上得到补偿,实践中也得到了较好的效果。但在变化较多的问题域(如互联网、创业企业),高度文档化会造成整个软件生产过程的反应迟滞,进而造成企业竞争力的下降。于是这些要求快速反应,快速迭代的行业逐步放弃了高度文档化的要求,开始追求原型设计、分步迭代以及“代码及文档”。

可是物极必反,实践过程中很多“敏捷”项目却从“高度文档化”走向了“无文档”:需求只有几句定性的描述,一个或几个开发自己鼓捣着就把功能完成了,最终交付的只有一个svn地址,基本没有任何文档,或者只有少数更新不及时的随笔。从结果看,绝大多数这样的项目无论技术上还是业务上,最终都是失败的。个别项目业务需求很大,技术后期满足不了,只能进行痛苦的重新设计和重写,这个过程往往耗时甚多,并对业务有或多或少的影响,背离了当初通过"敏捷"保障业务快速发展的初衷。

作为开发,我们的目标是正确认识文档的作用,制定合适的规范,撰写 必要而够用的文档。提升文档的编写和阅读能力,同时使文档为我们服务,为开发和维护过程创造价值。

文档的作用

  1. 帮助设计者克服恐惧

    面对新的业务需求,尤其是脱离了熟悉"甜区"的需求,设计者的内心多少都会有恐惧。

    新的业务需要全新的设计,需要通盘考虑各种情况如何处理,这个过程中往往还存在很多互相抵触的点,需要设计者作取舍。如果不写文档,设计者在脑海中构建一个初步的想法之后就会动手编码。这个初步的想法一般来说并不全面,但恐惧往往会驱使程序员尽快开始行动,试图看到一些产出,同时也能给(管理)上层一些响应。

    再差的设计者都能想办法满足眼前的需求

    可是未经全面考虑的产出往往有着较大的(设计)漏洞。幸运的情况下这种产出后续会被覆盖和改写成更有好的设计和实现,如果比较不幸,这些有缺陷的产出则会直接把后续的开发带歪。

    写文档能帮助设计者抵御立即动手编码的冲动。因为文档比代码的抽象程度更高,写文档促使设计者从更加抽象的角度思考问题。借助文档的抽象,设计者能从概念,而非实现的角度看待整个系统。脱离了实现细节,设计者更容易发现哪些概念属于错误的抽象(错误的抽象使某个概念和其它概念间存在不合理的依赖或交叉)以及整个设计拼图中有哪些缺失(概念间缺少必要的联系)。通过撰写文档,设计者为自己提供了一幅“全景图”,从而有勇气去作全局的设计。

    不谋万世者 不足谋一时
    不谋全局者 不足谋一域

    软件的设计者规划出模块、接口、服务等一系列概念,由实现者将其变成代码。这个过程中,设计者最重要的责任就是保证所有这些组件彼此间兼容,能够正常通信并实现需求,同时还要考虑到未来的可扩展性。设计者要不停的追问自己“如果发生了某种变化,现有的组件布局是否能够处理?如果不能,是否能快速定位要找到的组件,用最小最清晰的修改承载这种变化”。这个高度抽象的过程,脱离了文档的帮助,直接在代码层面进行效率会低很多,也更容易出错。

  2. 沟通和交流

    作为一个团队成员,仅仅交付功能是不够的,我们要交付的是 可理解,可维护的功能。为了这个目标,我们和各方的交流,把设计思路向他们讲清楚:

    • 设计评审者

      设计评审者通常对项目细节不会非常熟悉,他们关注的是整个项目的核心诉求,技术难点和实现方案是否自洽。他们比设计者(文档撰写者)考虑的更加抽象,看的往往只是几张图或者表格,但这几张图和表格并不会凭空出现,一定是从设计文档中抽象出来的最核心的设计要素。

    • 服务使用者

      按照“对接口编程”的思想,工作的边界应该落在接口上。接口上的文档通常有两类,一类是独立的接口描述文档和示意图,用于团队内部review;另一类是程序内文档(javadoc),作为接口说明(spec)供接口使用者参考。由于javadoc支持HTML,设计时可以先写interface,用详细的javadoc描述接口信息,再用工具抽取成独立的接口描述文档。这样即可以避免两份文档之间不一致,也更容易实现代码和文档的一致。当然,示意图这类更抽象的文档仍然需要手工整理。

    • 服务维护者

      包括进入项目的新同学和(项目交接过程中)的接手者。这些同学需要更加详细的文档,才能了解最初设计者的意图,并在后续设计中保持这个意图

      • 项目中有哪些状态,状态的格式,状态的物理分布
      • 项目采用什么原则进行模块划分,出于什么考虑(如果有多个方案时选择了其中一种)
      • 某些特殊设计是出于什么考虑,背景知识(性能、吞吐量、一致性、复杂性...)

      最后一点尤为重要。很多时候接手的同学通过翻代码能了解作者是怎么作的,但缺乏文档很难去了解作者是怎么想的。如果维护者不知道设计者的思路,再好的设计也无法得到贯彻。如果你作了一个正确的设计并为这个设计骄傲, 务必在文档中说清楚你的想法和目标 ,就像手工艺大师在作品上刺上自己的名字一样。

  3. 衡量产出

    种瓜得瓜 种豆得豆

    衡量程序员的产出是特别麻烦的事。各种衡量方式会带来不同的导向

    • 统计代码行
      这是外包经常采用的指标,统计代码行会造成大量的复制/粘贴。但实际上完成同样的功能,篇幅少的方案往往更清楚,也更易维护。所以代码行不适合我们的需要。
    • 看业务产出
      从更高层次衡量团队贡献时,业务价值毫无疑问是最重要的指标,但衡量单个程序员的能力和产出时,业务价值并不是一个很好的指标,毕竟很多业务因素不是程序员能控制的。
    • 看技术产出
      这要求能明确程序员的技术产出包括哪些方面,比较客观的指标就是看技术文档和代码。由于评审者实际不可能看完一个人产出的所有代码,技术文档就在这里起到了索引的作用。技术文档可以让评审者快速了解

      • 程序员代码中价值最大的部分
      • 设计者思路是否清晰,是否有原则性错误
      • 程序员是否有能力提交工业级别的设计和代码(重点在于合理、可读和可维护性)

      衡量产出时,文档和代码的比重通常会在三七开或者四六开。我们随后的考核中会采用文档占40%,代码占60%这样一个标准

必要的文档

增一分则太长 减一分则太短

我们需要文档,但不需要冗余的文档浪费程序员的生命和精力。我们希望程序员写的每份文档都是有价值的,有信息量的。

目前来说,对新功能需要提供以下设计文档

实体关系图(必选)

实体关系图是对功能抽象程度最高的文档,它包括

  • 新的功能要引入哪些(主要)实体
  • 新实体之间有什么关系(一对一,一对多,多对多,父子,组合,继承,。。。)
  • 新实体和原有实体之间有什么关系

通过实体关系图,可以尽快了解设计者的思路。实体关系图的重点是看 实体抽象是否正确 ,新的抽象能否正确实现所有用例。

//TODO 补充例子

状态设计(必选)

系统设计中很大一个工作就是规划系统状态(数据)的分布,通过状态分布可以大致了解实现能达到的性能、一致性和鲁棒性。这份文档包括

  • 新的功能会新增哪些状态(包括持久化状态和非持久化状态),会对已有状态造成什么影响。
  • 状态的格式(数据库的DDL或者no-sql的json/KV)
  • 状态的分布(集中式,分片,对分片要指明Sharding方法)
  • 状态的一致性方案(对不同状态的一致性需求,实时/定时, 推/拉, 读写分离等)
  • 状态的存取(状态通过什么方式存取和暴露给外界,直接访问,消息,API等)

一般Web Server无状态,系统扩展性多半取决于状态分布,所以需要专门的状态设计文档详细阐述。 状态设计关注的重点是 设计方案能否满足性能和扩展性需求 ,另外对C端系统还要考虑 是否有高可用性方案(放松一致性,提供可用性)

// TODO 补充例子

系统交互(可选)

新功能牵涉到系统交互时,需要提供系统交互文档。系统交互文档重点描述系统间的数据流,这份文档包括

  • 新功能牵涉到系统内部哪些模块,模块内的交互方式(API/MESSAGE/直接访问/etc.)
  • 和哪些外部系统发生交互,包括引入的新系统以及之前有交互的老系统,采用什么具体的交互方式
  • 交互接口是否有限制(性能/吞吐量/稳定性/etc.)
  • 外部系统哪些是强依赖,哪些是弱依赖
  • 数据流图,描述完成特定功能的闭环中,数据在各个系统(模块)间如何流转,从一个模块到另外一个模块的过程中,数据的形式如何转换。

通过系统交互文档,可以从更高的层次了解整个系统的复杂度和依赖。这里的重点是数据流转过程中是否暴露了过多细节或引入了不必要的依赖,评审的重点是数据流图有没有可能简化,将系统间的依赖降到最低

// TODO 补充例子

接口文档(必选)

接口文档是接口两端程序员的约定(Contract),任何需要多人合作的边界上都需要提供接口文档。

前后端接口文档

采用前后端分离的开发模式,前后端接口文档需要详细列明 每一个前后端接口的格式和说明。这个文档一般由前端提供,后端实现。形如

接口名称 listFoo
描述 查找Foo
Request:

```javascript
{
    "id": 1, //主键,可以为空
    "keyworkd": "abc" //关键词,可以为空,需模糊
}
```
Response:

```json
{
    {
        "id":1,
        "name": "Clinton"
    },
    {
        "id":2,
        "name": "Obama"
    }
}
```

后端接口文档

为了便于同步代码和文档,后端接口文档以javadoc为主,评审时抽取javadoc即可。javadoc也可以直接用IDE书写,更加方便。评审的以interface javadoc为主,当然对class/method也能有清楚的javadoc更好。

以下内容必须有接口文档

  • 所有HSF服务的接口
  • 跨开发者调用的接口 (提供给别的开发者使用的接口)
  • 有复杂实现的接口 (实现超过200行)

javadoc的目标不是应付评审,而是让别人了解设计者的想法。以下是对javadoc的一些要求

  1. 20个字以内写清楚这个接口是干嘛的。写完后站在接手者的角度读一下,描述是否清楚。如果没法在20个字内描述清楚,多半就是设计上有问题,不符合单一责任原则,需要考虑下是否要重新设计。
  2. 如有必要,用几句话描述下背景。这个一般出现在有特殊业务背景,进而需要某些特殊设计的场合。通过描述背景,接手者可以了解上下文,知道如何演进现有设计。
  3. 对入参和出参的描述。如果参数本身是专门的实体或bean,代码的类型已经很明确,不需要详细描述。但如果参数是泛类型(Object,集合类)。一定要详细说明具体的值是什么。
  4. 如果采用了特殊的选型或设计者有特殊的想法,要在文档中说明此决定所基于的前提,使用的场景,作了哪些折衷。避免接手的人踩坑。

这里有几个例子,考虑到脱敏,抹去了package name

/**
 * {@link ValveChainAuditor} consists of some valves, each valve may permit or deny an access request independently, an
 * {@link Permission} is granted only when all valves permit the access.
 *
 * @author lotus.jzx
 */
public interface Valve {
    interface AccessResult {
        /**
         * If the valve permit the request
         *
         * @return true if permitted, false else
         */
        boolean isPermitted();

        /**
         * Valve can attach an object to the {@link ValveChainAuditor}, this attachment will be returned when
         * releaseAccess is invoked. With attachment valve can store and fetch state in
         * auditor that itself can be designed as stateless service.
         *
         * @return the state needed when releaseAccess is invoked. Return null if extra state is unnecessary.
         */
        Object getAttachment();
    }

    /**
     * If valve to current time to make decision, current time will be passed in when tryAccess and releaseAccess are
     * invoked, or the now param will be null.
     *
     * @return true if need, false else
     */
    boolean needNowTimestamp();

    /**
     * Return result of access request
     *
     * @param key resource key
     * @param now current time, null if needNowTimestamp() return false
     *
     * @return {@link AccessResult}, can not be null
     */
    AccessResult tryAccess(String key, Date now);

    /**
     * release access
     *
     * @param key            resource key
     * @param now            current time, null if needNowTimestamp() return false
     * @param accessHappened true if all valves accepted the request (commit), false else (rollback)
     * @param attachment     the attachment returned in the {@link AccessResult}.
     */
    void releaseAccess(String key, Date now, boolean accessHappened, Object attachment);
}


/**
 * 数据项操作符,能对数据项进行操作
 * @author lotus.jzx
 */
public interface DataItemOperator {
    /**
     * 操作符知道如何解析数据项上用户需求(的字符串),将其转换为具体对象,供后续使用以及露出
     *
     * @param dataItem   数据项
     * @param attributes 校验用到的属性(最初是validateAttributes+sessionContext.params,经过FilterExecutor处理),
     *                   可以在这里添加需要露出的变量
     *
     * @return 需求对象
     */
    Object parseRequirement(QualificationDataItem dataItem, Map<String, Object> attributes);

    /**
     * 给定以下内容,操作符知道如何对其进行运算,得到资质项校验的结果
     *
     * @param dataItem                资质数据项
     * @param value                   资质数据项(从数据源)取到(经过Filter处理)的值
     * @param attributes              校验用到的属性(最初是validateAttributes+sessionContext.params,经过FilterExecutor处理),
     *                                可以在这里添加需要露出的变量
     * @param parsedRequirementObject 操作符自身parse后的对象
     *
     * @return
     */
    QualificationDataItemValidateResult runOn(QualificationDataItem dataItem, Object value,
                                              Map<String, Object> attributes,
                                              Object parsedRequirementObject);

    /**
     * 是否允许需求为空(一些表单操作符不是从活动上而是从sessionContext取requirement,允许活动上的requirement不配置)
     *
     * @return
     */
    boolean isAllowNullRequirement();

}

接口文档评审的重点主要有

  • 命名是否清楚,interface中的func与其所在的interface是否有 "has-a"关系
  • 入参、出参最小化,尽量针对接口而非实现,模块间暴露最少的信息,便于模块间隔离
  • 站在使用者的角度,说明文档是否易懂,能否无歧义的使用API

单元测试

单元测试也是文档的一部分,尤其是在持续集成中,单元测试除了验证正确性,自身也是一个(始终和代码同步)的说明文档。具体详见 《写有价值的单元测试》一文

此时此刻,非你莫属

由于业务、排期、环境等原因,很多开发都写过"脏"代码,可能也都接手过"脏"代码。每个接手"脏"项目的人都会吐槽没有文档的项目就是一堆坑,每次交接带来一堆问题。可是这样的吐槽并没有实际价值,尤其是在你并没有为项目的文档化作出任何贡献时。

临渊慕鱼 不如退而结网

在我们团队中,我们尝试改变"苦恼没有文档,又不生产文档"的困局,达成以下这些目标:

  • 我们要认识到
    文档并不是负担,而是帮助开发提升效率的工具
  • 我们要实现
    通过文档高效沟通,而不是通过代码低效沟通,提升所有人的效率
  • 我们要作到
    把文档能力作为评价程序员的重要指标。在评价体系中,把文档放到和代码相同甚至更高的位置上。

文档化,开始行动吧!

时间: 2024-08-03 20:04:58

写有价值的技术文档的相关文章

《Cocos2D-X游戏开发技术精解》一1.6 技术文档

1.6 技术文档 Cocos2D-X游戏开发技术精解前面的章节内容,曾介绍了两个包含引擎的技术文档的地方,细心的读者一定会留有印象.在引擎的官方网站,提供了在线版本的API指南.以防读者忘记了,下面列出的网址就是访问路径. http://www.cocos2d-x.org/reference/native-cpp/index.html 在引擎的下载包中也提供了技术文档.不过,技术文档的内容其实包含在代码中,读者需要一个工具才能生成网页版本.这个工具就是Doxygen.它是一个C++.C.Java

中文技术文档的写作规范

很多人说,不知道怎么写文档,都是凭着感觉写. 网上也很少有资料,教你写文档.这已经影响了中文软件的发展. 英语世界里,文档非常受重视,许多公司和组织都有自己的文档规范,清楚地规定写作要求,比如微软.MailChimp.Apple.Yahoo.docker.Struts 等等(维基百科有一份完整的清单).中文的也有不少,但都不令人满意,要么太简单,要么不太适用. 我就动手,参考上面的规范,也结合自己的实践,总结了一份简单的<中文技术文档的写作规范>. 标题 文本 段落 数值 标点符号 章节结构

MDN Web 技术文档 - JavaScript Strict mode

MDN Web 技术文档 - JavaScript Strict mode 在 JavaScript 1.8.5 引入         ECMAScript 5的严格模式是Javascript中的一种限制性更强的变种方式.严格模式不是一个子集:它在语义上与正常代码有着特意的差异.不支持严格模式的浏览器与同支持严格模式的浏览器行为上也不一样, 所以不要在未经严格模式特性测试情况下使用严格模式.严格模式可以与非严格模式共存,所以脚本可以逐渐的选择性加入严格模式.         严格模式在语义上与正

技术文档归档

这个文章用于保存某些技术文档,便于事后参考. [1]. 基于表格存储的高性能监控数据存储计算方案

java技术文档如何写(项目完成后)

问题描述 如题,之前没写过,现在不知道如何下手,请问写作的重点应该突出什么方面,是程序的设计,还是程序的使用,细节方面如何处理,要细化到每一个变量,方法嘛.请教高人!!! 解决方案 解决方案二:你要先确定你要写什么文档吧?需求分析,概要设计,详细设计,测试报告,用户手册等等,每一个侧重点都不相同啊解决方案三:这些文档基本都有规范,找找你们公司以前写的文档参考下解决方案四:各个公司的规范可能不一样,如果公司没有这样的规范,就参考别人的写一下咯.解决方案五:我也没写过,扯草草.zoubalon(这个

软文的价值体现在哪些方面 如何写有价值的软文

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 我们每天在互联网上面看到很多文章,文章里面为你介绍某些品牌的名称或者是一些人物等,通过软文,我们可以知道很多东西,这些就是软文的价值体现.下面通过例举为大家详细解释软文的价值与如何写有价值的软文: 一.软文的价值 1. 美丽说通过软文推广软件 笔者记得美丽说写过不少关于网赚类型的软文,之前笔者在很多的网赚论坛看到过,不过那个时候已经很早了,相

ChinaUnix.net技术文档手册中心_常用工具

(CU网友csfrank热心提供)" href="http://man.chinaunix.net/newsoft/ApacheMenual_CN_2.2new/index.html" target=_blank>Apache2.2中文手册(2006-10-20重要更新版)(CU网友csfrank热心提供) 构建和安装 Linux 包(CU网友bobkey翻译并热心提供) GTK+ Reference Manual(英文版) 备份工具Amanda官方文档 备份工具Bac

C#技术文档

问题描述 跪求C#技术文档.要全一点的,最好像JAVA那样可以快速打开的. 解决方案 解决方案二:"C#技术文档"."快速打开"?没明白不是有现成的MSDN吗?解决方案三:借贵宝地同求,文档.解决方案四:要的是CHM文件的吧,微软现在不给做这种的了,因为有MSDN在,所以也没有爱好者去做chm了解决方案五:MSDN怎么打开啊?解决方案六:MSDN也不好用啊,毛都找不到.SqlConnection都找不到.解决方案七:开始->程序->MicrosoftDe

MDN Web技术文档 - HTML 元素

MDN  Web技术文档  HTML  元素 元素   HTML元素 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z  (快速链接 HTML Category) HTML元素 这个HTML引用列表所有HTML元素,定义在HTML5或在之前的规范.把尖括号放置在HTML元素的两边,就构成了HTML标签 : <elementname>.元素实体指定HTML文档如何应该建立,什么样的内容应该放置在一个HTML文档的哪些部分. 此页列出了所有的标