电脑技术

向后兼容的谎言:版本号救不了你的接口

每个设计接口的人都对自己说过同样的话——"加个字段就行了,老的调用方不受影响。"

三个月后,你发现老调用方也在读新字段,但逻辑完全不是你预期的。半年后,当初保证"兼容"的那个人转岗了。一年后,没人动得了任何一个字段,接口文档变成了考古现场——一半字段没人用,另一半的含义靠口头传承。你在注释里看到 `// 这个字段别删,XX团队在用`,去找XX团队,他们说早就不用了,但不敢确认,要再查查。

这不是向后兼容,这是技术债在穿正装。

 

兼容性承诺是一种债务

 

接口设计里最危险的一句话不是"我们重构吧",而是"我们保证向后兼容"。前者好歹意味着你在正视问题,后者意味着你在透支未来。

向后兼容不是免费的。每多兼容一个旧版本,你就多了一条隐形的约束链。这条链不会出现在代码评审里,不会出现在架构文档里,但会在每一次你需要修改字段含义、调整返回结构的时候跳出来,像一个极其礼貌的绑匪——"您不能动这个,还有调用方在用。"

会议是技术团队最体面的避难所

博客分类: 

周一早上打开日历,发现这一周已经被各种颜色的色块填满了。晨会、周会、评审会、对齐会、架构讨论会、需求评审会……还没坐下来写一行代码,一天就过去了。剩下的时间再打开 Slack 回几条消息,真正专注的时间碎片化到不可能做任何需要深度思考的事情。

这不是某个人的问题,是整个技术团队的通病。几乎所有技术管理者都抱怨过"会议太多",但有意思的是,抱怨完之后,大多数人还是继续安排会议、接受会议,甚至主动拉会。这种言行矛盾本身就值得琢磨。

 

信息同步?别自欺了

 

会议最常用的理由是"信息同步"或者"对齐"。但仔细想想,你上一次在会议中真正获得关键信息是什么时候?绝大多数会议的信息流向是单向的:一个人讲,其他人听。这叫广播,不叫同步。

更常见的情况是,会议的前十分钟在等人,中间二十分钟在重复上周说过的话,最后十分钟草草收场。真正有信息量的部分,可能五分钟就能说完。但五分钟的会议不符合惯例,也显得不够重视。

我曾经观察过一个现象:技术团队里信息传递效率最高的时刻,往往不是在会议中,而是在工位上随口问一句、在 IM 里发一条消息、或者在 MR 的 comment 里写一段话。这些非正式的信息交换反而更精准、更及时,因为它们是在具体问题的上下文中发生的,不是为了开会而开会。

缓存的一致性幻觉:为什么缓存越多数据越不可信

每个做过高并发系统的人,大概都经历过这样的时刻:线上出了个数据不一致的bug,排查一圈发现是缓存没更新。修完之后加个主动失效,觉得踏实了。过几天又出现,这次是另一个缓存层级。再修。再过段时间,用户反馈看到的金额对不上——你一查,三个缓存层级,两个过期时间不一样,一个还挂着CDN缓存头。

这不是段子,这是每天都在生产环境里上演的日常。

团队解决性能问题的第一反应永远是加缓存。页面慢?加个Redis。接口慢?加个本地缓存。前端渲染慢?加个HTTP Cache-Control。数据库扛不住?加个查询缓存。每一层缓存都在解决一个真实的问题,但每一层缓存也在制造一个你暂时看不见的新问题——等到它浮现的时候,往往已经是你最不想看到的形态。

 

缓存是性能的银弹,也是一致性的地雷

 

这里有个反直觉的事实:缓存从不制造bug,它只是把bug从"现在就暴露"延迟到"不知道什么时候暴露"。

一个没有缓存的系统,数据读出来就是最新的,哪怕慢一点,至少不会错。但当你开始在链路上堆缓存,你事实上引入了一个隐含假设:旧数据在一段时间内是可以接受的。这个假设在大部分场景下成立,但"在大部分场景下成立"和"在你的场景下成立"是两回事。

你的错误处理只是安慰剂

打开任何一个前端项目,全局搜索 `catch`,你会看到什么?一大片空荡荡的 catch 块,偶尔跳出几个 `console.error`,再偶尔冒出一行 `message.error('系统异常')`。后端项目也好不到哪去,try-catch 包着业务逻辑,catch 里面的处理方式跟前端如出一辙——记个日志,打个错误码,然后呢?然后什么都没有。

我把这种写法叫"安慰剂错误处理"。它的作用不是解决问题,而是让写代码的人觉得自己处理了问题。就好像感冒的时候吃维C,你做了点什么,但那点什么跟治愈没有关系。

更残酷的事实是:大部分错误处理非但没用,还在制造新的问题。

 

try-catch 不是错误处理,是错误藏匿

 

先说一个很多人不愿意承认的事:try-catch 是所有错误处理手段里最廉价的那一种。它的成本最低,所以它的价值也最低。

我见过太多这种代码:一个函数内部包了三层 try-catch,每一层都把异常吞掉,最外层返回一个 `{ success: false, message: '操作失败' }`。调用方拿到这个返回值,判断 `success` 为 false,然后弹个 toast —— "操作失败"。用户看到这四个字,内心毫无波澜,因为他已经见过一千次了。

你的技术深度,正是你的成长陷阱

博客分类: 

你有没有过这种感觉:技术上你比团队里任何人都强,但升上去的总是别人?

不是因为你不够好。恰恰相反,是因为你太好了——好到所有人都觉得你留在现在的位置是天经地义的事。

这个问题困扰过我也困扰过我身边几乎所有做了七八年以上的工程师。不管在哪个团队,总有一两个技术最硬的人,卡在资深或者专家的位置上,上不去也不愿下来。他们不是没有机会,而是每一次机会来临的时候,他们自己把路堵死了。

 

技术深度会制造一种虚假的安全感

 

做了很多年工程之后,你会形成一种本能:遇到问题,先想技术方案。系统慢了就优化性能,接口不稳就加重试,代码乱就重构。这套反应机制在过去无数次证明是有效的,所以你会越来越依赖它。

这本身没错。问题在于,当技术能力成为你唯一的响应模式,你就开始用技术思维处理一切问题了。

产品提出一个你觉得不合理的需求,你的第一反应是"这个方案技术上不优雅"。老板问你怎么缩短交付周期,你想到的是优化构建流程和CI。团队成员离职了,你认为是代码评审标准下降导致代码质量差,进而影响工作体验。每个问题你都给出了一个技术上站得住脚的答案,但每个答案都只触及了问题的表层。

重构冲动:写下代码的那一刻才是你最清醒的时候

每个工程师都有过这种时刻——翻开一段半年前写的代码,眉头一皱,手指开始痒。"这什么写法?""这命名谁看得懂?""这段逻辑完全可以抽象成三个函数。"然后一个"重构"的念头冒出来,越来越强烈,像夏天傍晚的蚊子,赶不走。

这种冲动太普遍了,以至于我们很少质疑它。代码审查时建议重构,结对编程时讨论重构,技术债清单里排满了重构项,季度规划里少不了"代码优化"的工时。重构似乎天然正确,像一种工程美德。

但我的观察恰恰相反:大部分重构是错误的决定。不是重构这件事有问题,是驱动重构的动机和时机几乎总是错的。

 

丑代码不等于坏代码

 

先说一个多数人不愿意接受的事实:代码的审美和代码的质量,经常是两回事。

一段看起来笨拙的代码,可能在一个微妙的时间窗口里正确处理了并发竞争。一段到处是硬编码的代码,可能正是因为硬编码才避免了配置出错引发线上故障。一段三百行的函数,可能是经过六次需求变更后唯一还hold得住的逻辑形态——你把它拆成六个"优雅"的函数之后,任何一个需求的变更都会涉及三个函数的修改,追踪数据流变成了噩梦。

"丑"是主观判断,"坏"是客观后果。两者之间没有必然因果关系,但在重构决策里,它们经常被混为一谈。工程师看到不美的代码就判定它需要重构,这个推理本身就是逻辑谬误。

绩效主义正在杀死工程团队

OpenAI前不久宣布,SWE-bench Verified已经不再适合用来评估前沿编码能力。一个专门为衡量代码能力设计的基准,被AI冲破上限之后反而失去了意义。这件事本身不意外——当某个维度变得可优化,它迟早会被优化到偏离初衷。真正有意思的是,这个逻辑不只适用于AI,它几乎完美地映射了工程团队考核的困局。

我们比历史上任何时期都拥有更多的工程度量手段——代码量、PR数、故事点、交付率、代码覆盖率、线上故障数、响应时长……每个团队都在量,每个管理者都在看,但几乎没有谁敢说,自己的考核体系真的反映了团队的工程能力。

原因很简单:你量的全是噪音。

 

当度量变成目标

 

古德哈特定律讲了两百年了,但工程团队似乎永远不会吸取教训。或者更准确地说,不是不懂,是没得选。

复盘救不了你的团队

博客分类: 

又一起线上事故。凌晨三点的告警,值班同学爬起来处理,应急响应、回滚、止血,一气呵成。第二天上午,事故复盘会准时召开。PPT做得很漂亮,时间线梳理得清清楚楚,根因分析写得明明白白,action items列了七八条。所有人都认真地点了点头,表示"以后一定注意"。

三个月后,同一个系统,同一类事故,再次发生。

这个场景太熟悉了。每个经历过线上事故的技术人,大概都对这种循环不陌生。复盘会开了一轮又一轮,文档写了一篇又一篇,"改进措施"列了一条又一条,但该来的事故还是会来,该踩的坑还是会踩。

问题到底出在哪?

 

复盘变成了一场仪式

 

大部分复盘会的真实目的,早就不是"从错误中学习"了。它承担的职能更接近一种组织仪式——事故发生了,总得有个交代。复盘会就是一种交代:你看,我们重视了,我们分析了,我们有改进计划了。

这种仪式感会带来一个隐蔽的副作用:它让所有人都觉得事情正在被处理。管理者觉得团队在反思,团队觉得管理者在推动改进,大家心里都松了一口气。但"感觉在改进"和"真实在改进"之间,隔着一道巨大的鸿沟。

技术债务的真正代价不是重写,是决策瘫痪

博客分类: 

每次技术债务的讨论,最后都会落在同一个分歧点上:要不要重写?

主张重写的人说,这堆代码已经烂到没法维护了,缝缝补补不如推倒重来。反对重写的人说,重写的失败率太高了,你重写的时候业务还在跑,最后往往是新系统写了一半、老系统还在加功能,两个系统一起维护到崩溃。

两边都有道理。但两边都忽略了一个更根本的问题:这个讨论本身,可能才是技术债务最贵的那部分。

 

技术债务的隐性利息

 

技术债务这个比喻本身就有误导性。金融债务是清晰的——你借了多少钱,利率多少,每月还多少,到期日是哪天,一目了然。但技术债务不是。没人能准确告诉你当前系统积累了多少"债务",也没人能说清楚这些债务的"利率"是多少。

我见过一个场景:团队花了三个月讨论要不要重写核心模块。三个月里,有人写方案,有人做技术调研,有人估算工作量,有人在会上争论渐进式重构还是大爆炸重写。这三个月的产出是什么?一个PPT,一份技术方案,和N次没有结论的会议。

而这三个月里,那个"债务模块"依然在跑,业务需求依然在堆,新加的代码依然在旧架构上打补丁。讨论结束的时候,技术债务又多积了三个月的利息。

"能者多劳"是技术团队最隐蔽的陷阱

团队里总有那么一两个人,什么活都能干,什么锅都能扛。需求来了找他,线上出问题找他,新技术调研找他,代码评审也想拉他。他的日历永远排得最满,他的PR永远是别人等Review最久的那个。

大家都觉得这是"能力强、受重视"的体现。管理者也觉得,把重要的活交给靠谱的人理所当然——毕竟谁能放心把核心模块交给新人?

但我要说一个可能得罪人的判断:"能者多劳"不是在重用人才,是在消耗人才。而且这种消耗有一个特别迷惑人的地方——它看起来像是信任,感觉起来像是被需要,唯独结果不是被成全。

 

看起来是信任,实际上是惩罚

 

考虑一个很常见的场景。

项目要赶排期,核心功能必须本周上线。你会交给谁?当然是那个靠谱的资深工程师,因为交给别人你不放心,延期了你担不起。

线上出了Bug,排查要理解完整链路。你会交给谁?还是那个人,因为只有他能从数据库一路追到前端渲染。

新人入职需要导师,你会选谁?又是他,因为他技术好、有耐心、能讲清楚。

看起来这个人得到了最大的信任,承担了最重要的工作。但仔细想想,他得到了什么?更多的工作。更满的日历。更少的写代码时间。更晚的下班时间。而那些干得一般的人呢?他们的工作量反而更少,因为没人找他们。

页面