前言
很多时候我们骄傲于中国的市场效率之高和发达国家常见的繁琐冗长的监管,我的感受是一样的。大部分死板的低效的规则非常荒谬。但本文讨论的是另一个角度:这种死板在于规则设计错误、和应该如何正确设计,而非认为完全不该有规则。不难想象失序对效率的损耗有多严重。效率才是最根本的追求,由 Spec 达到可预测性是追求效率的必要手段。
先看一道题判断下你是否需要读这篇文章。下面有四张机械制图,请尝试找出里面错误的一张
无论你是否学过机械制图,聪明如你仅从题目设置应该也猜到错的是哪一张。答案的原理给只学过几何学的同学来看,大概会感觉很反常识。反常识的地方正式本文的重点:标准是做什么用的。
稍微解释一下这个类比。图纸是给车床工人加工参考的,里面隐含的是加工的过程:要么先推整体、再推一边,要么各推一边组成整体。每一张图都对应着一种加工过程,那么图二显然是无法实施的。工人需要知道以哪边为准,而不是虚构的数值。
也就是说,写 Spec 是绝对不可以脱离实施去讲“本质”,不然根本没有意义和价值。它必须是关于实施的。下一章会开始详细说明。
关于我
TikTok Web Architect 之一,起草了协调了 TikTok Web 第一篇 Spec: TikTok Web Monorepo libs specification,以及对应的检测工具(project expansion)、libs 对应的初始化脚手架(EdenX Module)、自动化流水线服务(Luban TT Monorepo 公共)和接入命令行。(以上多由各路英雄开发支持,我只从中协调,功必不在我)。
京人,生活在中国三十多年刚刚转到坡市几年。勤学多干,Always day one。
I. Spec 不该做什么
Spec 最常见的错误,是自始至终介绍“事实”而不是“确认事实的方法” 。也就是说,正确的 Spec 永远不应该告诉读者一段距离有多长,而是应该介绍用什么工具量、量几次、结果在哪个范围中时可以怎样。
举一个极端的例子,假如 spec 里写“可读性要好”,这个要求毫无可执行性,何谓可读是完全没有评判标准的。这样写和“必须够好”一样因为含糊所以毫无实际意义。对可读性这类的要求应该拆解成可观测、可量化的指标。比如单个函数的行数上限和单行的宽度上限,可以明确测量和计算。指标也许有时会不准,我们可以动脑去提高准确性,但不可以根本没有指标。
引申一点说,有些深入的话题是难于取舍的。比如约定必须使用 React,那么我们不会太困惑于如何评判,反而如果约定可执行的检测手段,例如检查 package.json 里是否包含 denepencies,这样是会隐含一些作弊机会的,并不是我们的初衷。这种时候表达清楚要求即可,详细检测则要认真设计,比如检查treeshake 之后被引用。此种复杂情况结合下面一条更容易处理。
另一个常见的误区是拿 spec 当做刑法条文,只关注别人应当遵守什么而不说为什么要这样、做了能得到什么。像前文所说 Spec 必须是为效率服务的,约束仅在不得已、且有非常明显具体收益的时候才可以做。Spec 一开头就应该介绍清楚会用于何处。并且详情里保持对执行时 utilities 和 services 有哪些应用。
举例来说,我们发现团队使用模块分解方案的时候会要求写很多配置到 package.json 里。这些要求不是吃饱了撑的或者看起来好美。在 Spec 里介绍这些配置将会被哪些系统和哪些包引用、如何被读取是非常有意义的。不仅提出了收益作为奖励,更是帮助读者深入理解这种设定,方便调试和决策。
方便决策是一个非常有价值的功能。甚至可以说是 Spec 应该存在的最大价值。例如通过解释一个问题(problem)给读者,你们可能遇到的困难是什么,以及其他方案为何无法满足。他们读过既理解了好处、又知道了生效原理以及如何灵活应用。
II. 应该有哪些桥段
总结上面的论述,一篇 Spec 的章节应该有如下必需:
-
这篇 spec 会检测哪些卡点,怎样的卡点会指示通过与不通过。这些卡点不是用来责罚的,而是用来界定被测的项目是否满足本协议。
- 遇到不通过的情况,如何检测和修改,确保错误好排查和易于理解。
-
这篇 Spec 的用户会得到哪些便利,哪些服务或者功能,是针对本 spec 设计的,只要遵守了本 spec 就会相应的享受到。
- 至少应当体现为何如此的约束是必要的或者推荐的
- 每一个提供的服务,由何种机制起作用,可协助用户理解使用此协议的要领
总体来说任何一篇 Spec 的作者应当时刻牢记是初衷来提升效率的,做一切规则约定都应当服务于此。不难想象如果重复环节、相同功能不能简单的一致,会对维护性带来多大的损失。这种效率问题就是 Spec 重点服务的对象。但这个内容常常最容易被作者忘记或忽略。所以非常重点要注意以下章节。
-
这篇 Spec 的用户(符合 Spec 的项目)会有哪些特征、它们可以如何被使用或维护
-
可以不用看就放心用的 feature 会有哪些
-
如果需要看的时候,可以快速地从哪里检查到
-
III. 服务,而非限制
前面多次提到 Spec 核心是提高效率,不仅仅是协作效率,也包括直接的开发效率。Spec 作者很多时候会预想各种各样的 fancy 的 features,想着如果全部实现会很美好,但是并不提供实现它的助力、甚至必要的设施。前面提到了两个必要部件,1. 检测验证工具;2. 协议条款。下面介绍第三个, 3. 必须提供合适的转换工具。
公司内的 Spec 和社区项目的一点区别是没有其他人会帮忙建设发展工具,仅提出来要求而不给基础工具非常不负责任。有些验证和修改是为将来的协作提效区发挥作用的,也就是说没有眼前的即刻收益。这些都是Spec 受到抵触和负面评价的重要原因。对此有各种方案和办法去应对和协调本文不做讨论。但是非常确定的是,对大部分修改、不需要决策和人工关注的调整,完全可以和应该用转译等手段自动化的生成和完善。如果我们的 Spec 要求使用者拿很长时间去改一堆繁琐、重复、毫无建设性的配置或格式,这个条款的推出是不负责任的。相比没有检测工具的 Spec 缺少转换工具更加失职。
综上,完整的 Spec 应当包括以下三部分
-
可观测量化、衡量可靠的检测工具
-
介绍了如何检测、有何收益、如何使用的详细条款
-
提供各个条款的配套生成转化工具
IV. 总结
先说这么多,相信至少会引发聪明的读者去思考。很多事实由于常见,会使我们产生思维惯性,从而影响到客观判断。举个例子,如果有一天朋友转账给你填错账号,我相信没有人会认为“去找柜员帮忙先改一下余额”是一件可能会效率很高的事情。死板、低效是说那些没价值和设计错误的规则的。用快速地避开去解决一个显然的问题,从来不等于系统里的问题越来越少;损失一点点秩序去追求效率仅在百废待兴的阶段有效;当积累成为常态,秩序就是效率。设计 Spec 的理想应当是“可以由柜员来改一下余额,并且这个修改客观、可靠、不会损坏其他相关系统的稳定与准确”。这个理想不容易达到但是应当是设计者的努力方向。希望本文的读者在编写 Spec 时能以这个为最终理想发挥创造力。为了效率去创造,同时坚信一定能达成效率的提高。