开发手记 · 第 4 / 5 篇 合集目录 ‹ 上一篇 下一篇 ›

回头看自己经手过的那些决定,当初都是为了某个具体的省事或对齐做的,可等这摊东西长大,它们一个个都变成了改不动的东西。

不是说技术上改不了,是改的代价太大、牵连太广,或者外面还有人指着它活着,于是没人敢动。明明手能伸过去,就是缩了回来。

早年我写代码,默认自己拥有全部主动权。一个名字起错了,改;一个结构不顺,重排;一个写法别扭,推翻重来。那时候项目小、用人少,改起来零成本,我就养成了「不对就改」的习惯。我没想过,这个习惯是建立在「没人依赖你」的前提上的。一旦你的代码被别人接住、被外面引用、被历史记住,主动权就不再完全是你自己的了。

最能说明这件事的,我觉得是几个最不起眼的字符串常量。这个服务对外要报一份能力说明,里面有几项版本号,它们是这么写的(等价改写):

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,结构当约定,版本当承诺。

那五个版本号,我当初写下它们的时候,没人会当这是承诺。它就是一个字符串,跟旁边的日志文案长得一样。是外面有人开始读它,才把它变成了承诺。承诺不是在写代码那天订立的,是在别人依赖你的那天订立的,而且没人会来通知你。

能改不代表该动,我看久了才明白,把约束守住也是一种设计。