使用 adr 轻松创建 “程序员友好” 的轻量级文档
是的,我又写了一个 markdown 工具,它对我来说非常有用。
上下文
在一周里,我看到了一个名为 “轻量级架构决策记录” 的技术实践。在看到了一个简单的示例之后,并阅读了文章《架构决策记录》之后,我开始对于这种工具有了一个好的印象。这似乎就是我,以及敏捷团队、程序员所梦寐以求的工具。
作为一个程序员,我们并不喜欢阅读又长又臭的文档,它往往不如一个 hello, world 来得实在。更不用说自己去写一个又长又臭的的文档了。事实上,我们对于文档的痛恶的原因是:文档经常是落后的、老旧的。因此,一个更合适的方案是,创建一种轻量级的文档。
作为程序员,我们常说代码即文档。是的,代码本身是文档的一部分,但是代码往往告诉你的是结果,代码不会告诉你原因。尽管从版本控制中,我们仍能通过 log 找到对应的 Author,可往往找到的这个人,可能已经没有人认识了(至少隔了一代开发人员)。
因此,如《架构决策记录》一文中所说:
项目在其生命周期中,最难追踪的事情之一就是:某些技术决策背后的动机。
这时,我们往往需要一个工具来记录产生这些技术决策的原因。
随后通过 ThoughtWorks 技术雷达(在 2016.11 期上提到 ADR),我找到了一个相关的库:adr-tools,但是发现这个库有一些小的缺点:
- 使用 shell 编写,不易读懂、只支持类 Unix;
- 模板里使用的是英语,不支持中文及其他语言
决策
于是,我便决定自己写一个这样的工具。它应该采用 markdown,并使用《架构决策记录》一文中提到的格式:
标题,这些文件的名称是短名词短语。例如,“ADR 1: Deployment on Ruby on Rails 3.0.10” 或 “ADR 9: LDAP for Multitenant Integration
上下文,这一节描述了当前的技术、政治、社会和项目。这些力量可能处于紧张状态,应该这样说。本节中的语言是价值中立的,只用于描述事实。
决策,这一节描述我们对这些力量的回应。这是充分的句子,以及积极的声音。 “我们会...”
状态,如果项目利益相关方尚未同意,或者一旦达成一致,则 “决定” 可能被 “提议”。如果以后的 ADR (架构决策记录)更改或撤消决定,则可能会将其标记为 “已弃用” 或 “已取代”,并参考其替换。
后果,这部分描述了应用决策后产生的上下文。所有的后果应该列在这里,而不仅仅是 “积极的”。一个特定的决策可能会产生积极的、消极的和中性的后果,但是它们都会影响未来的团队和项目。
并且,它应该可以轻松地实现:
- 采用一种能用的语言环境,如 Node.js,以支持主流的操作系统
- 多语言支持,我的意思是它至少可以支持 English 和 中文
- 支持状态日志查询,即我应该可以看到一个决策在生命周期里的变化
- 一个更好的列表展示,我应该可以查看到某条决策,以及对应的最后状态、修改时间等等
- 使用 markdown 展示,以便在 GitHub 上显示
- 拥有一个 ToC 页面,方便用户查看
状态
2017-11-22 提议
2017-11-22 通过
2017-11-22 完成第一个版本
结果
最后,我使用 TypeScript 与 Node.js 创建了一个 adr.js 的库。
它的安装很简单:
npm install -g adr
然后,你就可以创建你的 ADR 了:
adr new 'hello, world'
并结合提供的工具来查看这些技术决策:
$ adr list╔══════════════════════════════════════╤══════════════╤═══════════════════╗║ 决策 │ 上次修改时间 │ 最后状态 ║╟──────────────────────────────────────┼──────────────┼───────────────────╢║ 1.编写完整的单元测试 │ 2017-11-27 │ 2017-11-26 已完成 ║╟──────────────────────────────────────┼──────────────┼───────────────────╢║ 2.添加目录生成 │ 2017-11-27 │ 2017-11-25 已完成 ║╟──────────────────────────────────────┼──────────────┼───────────────────╢║ 3.图形生成功能 │ 2017-11-27 │ 2017-11-24 已完成 ║╟──────────────────────────────────────┼──────────────┼───────────────────╢║ 4.生成在线图形 │ 2017-11-27 │ 2017-11-22 提议 ║...╟──────────────────────────────────────┼──────────────┼───────────────────╢║ 15.考虑添加-export-功能来导出-adr │ 2017-11-27 │ 2017-11-26 提议 ║╟──────────────────────────────────────┼──────────────┼───────────────────╢║ 16.使用不同色彩来标注不同的状态 │ 2017-11-27 │ 2017-11-27 提议 ║╚══════════════════════════════════════╧══════════════╧═══════════════════╝
不过,在这里我犯了一个错误,就是把功能需求也放到里面了。
我们也可以查看某个决策在生命周期的变化:
$ git logs 9╔════════════╤══════╗║ - │ - ║╟────────────┼──────╢║ 2017-11-23 │ 提议 ║╟────────────┼──────╢║ 2017-11-23 │ 通过 ║╚════════════╧══════╝
除了这些,还有额外的功能:
- 内置 update 命令,方便同步决策到标题上
- 生成决策的目录,方便快速定位
- 支持导出 CSV 格式,以便在 Excel 中查看
- 支持导出 JSON 格式,以便进行二次开发
有赞赏码了~~
项目 GitHub 地址:https://github.com/phodal/adr
- 运用适配器模式应对项目中的变化
- 开车啦!小爬虫抓取今日头条街拍美女图
- C语言中随机数相关问题
- 算法决策兴起:人工智能时代的若干伦理问题及策略|AI观察
- Win10配置人工智能学习平台Tensorflow的正确姿势
- mysql left( right ) join使用on 与where 筛选的差异
- 一条长sql的排错过程
- (41) 剖析HashSet / 计算机程序的思维逻辑
- Python时间运算的详细机制初探讨
- C++编译错误cannot have cv-qualifier
- Python写TCP端口扫描工具之IP协议的讲解
- mac上nginx+jetty负载均衡部署一览
- Python标准库(1) — itertools模块
- C++从键盘输入文件结束符
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- spring security oauth2使用refresh_token报错UserDetailsService is required
- 你所不知道的React| 趋势解读、底层逻辑、学习路径、实战应用
- js中setTimeout的用法和JS计时器setTimeout与setInterval方法的区别和confirm方法
- TKinter Label 和 Button
- Java类如何防止被实例化
- PHP中的json_encode和json_decode
- Java重写equals和hashCode方法
- upload-labs第11~12关 00截断
- java 踩雷日记--new
- 用一个通俗易懂的例子彻底说清楚单例模式
- Manytasking Jmetal 代码反向解析 1_MATP 测试函数集
- PHP中的ereg()与eregi()
- Java 用反射实现实体类属性 not null 校验
- 数据结构:用实例分析ArrayList与LinkedList的读写性能
- vue-element-admin上传图片的功能