回头看自己经手过的那些决定,当初都是为了某个具体的省事或对齐做的,可等这摊东西长大,它们一个个都变成了改不动的东西。
不是说技术上改不了,是改的代价太大、牵连太广,或者外面还有人指着它活着,于是没人敢动。明明手能伸过去,就是缩了回来。
早年我写代码,默认自己拥有全部主动权。一个名字起错了,改;一个结构不顺,重排;一个写法别扭,推翻重来。那时候项目小、用人少,改起来零成本,我就养成了「不对就改」的习惯。我没想过,这个习惯是建立在「没人依赖你」的前提上的。一旦你的代码被别人接住、被外面引用、被历史记住,主动权就不再完全是你自己的了。
最能说明这件事的,我觉得是几个最不起眼的字符串常量。这个服务对外要报一份能力说明,里面有几项版本号,它们是这么写的(等价改写):
public class ServiceConsts
{
public static readonly string ServiceVersion = "1.4.1";
public static readonly string ApiVersion = "1.4.1";
public static readonly string CapabilityVersion = "1.0.1";
public static readonly string SchemaRevision = "1.4.1.1";
public static readonly string PayloadEncoding = "gzip";
}
我数了一下,就这么七行、五个字符串。全仓引用它们的地方一共六处,五处在拼装那份对外的能力说明,一处在版本应答里。没有一处是给自己人看的,它们只往外走。
改其中一个字符,从编译到运行不会有一个测试失败。但只要对面有客户端在读这个版本号,它当场就要重新判断一次你是谁、还能不能跟你说话。
同一批「改起来两分钟」的东西还有好几个,那 4 MB 的生成文件、那个拼错的命名空间、那个对象类型的枚举名,难度都是两分钟,代价都是「得跟外面打个招呼」。
所以我动手之前会先问自己一句,这是我一个人的私事,还是已经变成了别人的协议。是私事,怎么顺手怎么来;是协议,就得像对待对外承诺那样谨慎,命名当 API,结构当约定,版本当承诺。
那五个版本号,我当初写下它们的时候,没人会当这是承诺。它就是一个字符串,跟旁边的日志文案长得一样。是外面有人开始读它,才把它变成了承诺。承诺不是在写代码那天订立的,是在别人依赖你的那天订立的,而且没人会来通知你。
能改不代表该动,我看久了才明白,把约束守住也是一种设计。