工程师的沟通成本为什么总是被低估

博客分类: 

每个项目复盘的时候,技术负责人都会提一个问题:为什么又延期了?

然后大家开始列原因:需求变更、技术方案调整、第三方接口不稳定、测试环境问题……很少有人提"沟通"。

因为沟通好像不属于正经的工作。你写了一千行代码,那是产出;你开了三小时会,那是浪费时间。但实际情况是,你开的那三小时会,可能比写一千行代码对项目的影响更大——不管是正面还是负面。

 

沟通不是耽误开发,沟通本身就是开发

 

很多工程师有一种根深蒂固的观念:写代码才是正事,沟通是不得不做的打断。所以他们会尽量减少沟通——需求文档能不读就不读,能用文字说清楚的绝不开会,能自己决定的绝不找人讨论。

这种做法在个人项目里没问题。一个人写代码,沟通成本为零。但团队项目不一样。

一个五人团队,如果每个人对需求的理解偏差10%,最终集成出来的东西可能偏差50%。这不是数学平均,这是偏差的累积——前端理解的交互逻辑和后端理解的数据结构不一样,测试理解的验收标准和产品理解的验收标准不一样,等大家把代码合到一起才发现问题,修复的成本是开发阶段的十倍。

你的技术债务可能不是债务,是遗产

博客分类: 

技术债务这个比喻太深入人心了,以至于所有旧代码都被贴上了"债"的标签。重构计划里排满了"还债"任务,架构升级的理由是"降低技术债务",新人入职第一件事是听老人抱怨欠了多少债。

但仔细想想,你管它叫债,它就真的欠了什么吗?

 

债务这个比喻哪里不对

 

债务意味着你有偿还义务。你借了钱,必须还。到期不还,利息越滚越多,最终破产。

代码不是这样的。

一段跑得好好的代码,即使写法丑陋、架构混乱、没有测试覆盖,只要它在生产环境稳定运行,就没有人强迫你"还债"。业务的每一个需求都可能让你动这段代码,但你可以选择不动它——包一层,绕过去,新功能走新路径。

真正的问题不是"不还债会怎样",而是"你还了债能得到什么"。

大部分技术债务偿还计划高估了收益。重构一个模块,你的预期收益是什么?代码更整洁了,开发者体验更好了,将来改起来更方便了。但这些收益几乎无法量化。你很难说服老板说:"我花两个礼拜重构这个模块,将来的需求开发速度会提升百分之多少。"

而你需要放弃的成本是实实在在的:两个礼拜的业务产出,可能还有引入 bug 的风险。

这不是债务,这是投资。投资就得算回报,而不是用"还债"的道德感来绑架决策。

 

造轮子的真正代价不是开发,是走不掉

博客分类: 

每个技术团队都会经历这样一个时刻:现有工具不太顺手,自己做一个吧。

理由总是充分得无法反驳——市面上的组件库不支持我们这种交互模式,开源的表单引擎处理不了我们的动态字段逻辑,第三方的配置中心满足不了我们的合规要求。每一条拿出来都是刚需,每一条都指向同一个结论:我们得自己造。

没人会在这个阶段反对。因为反对意味着你要证明"现有的东西够用",而提出造轮子的人已经把"不够用"的场景摆上了桌面。你没办法反驳一个正在发生的痛点。

于是轮子开始转。第一周,大家很兴奋。第三周,初版出来了,确实比之前好用。第二个月,几个业务接入了。半年后,团队里有一半人的KPI跟这个轮子挂钩。一年后,最初写轮子的人提了离职。

这就是造轮子的故事。但今天我不想聊"该不该造"——这个问题已经被讨论过太多次,而且答案永远是"看情况"。我想聊的是一个更少人关注、却更致命的问题:造轮子最大的代价不是花时间开发,而是你再也走不掉。

 

开发成本只是首付

 

大部分人评估"要不要自己造"的时候,脑子里算的是开发成本。两个人做两个月,能搞定,成本就是四个人月。和采购方案比一比,好像更划算。这个算术本身没错,但它只算了首付。

真正的问题是后面几十年的月供。

设计系统的尽头是没人用

每个前端团队最终都会走上同一条路:建组件库,搞设计系统,画设计规范文档。

然后看着它慢慢死去。

不是夸张。我见过太多团队花大力气搭了一套设计系统,上线时热热闹闹,半年后只剩一个人在维护,一年后连那个人也转岗了。业务线该自己封装的还是自己封装,该重复的还是重复,设计系统变成了一座精美的鬼城。

这个现象太普遍了,普遍到已经不值得用"执行力不够"来解释。问题不在人,在设计系统这个事情本身的逻辑里。

 

建设者的幻觉

 

设计系统的出发点通常很理想:统一视觉规范,提高开发效率,减少重复劳动。听起来无懈可击。

但这个逻辑有一个隐含前提——业务足够稳定,需求足够规范,变化足够可控。

现实呢?

金融产品恨不得每周换一轮视觉,大促活动页面跟日常页面完全是两套设计语言,某个业务线因为合规要求必须用特殊样式,另一个业务线因为老板拍板要用新风格。你精心设计的 Button 组件有十二个 variant,但业务要的是第十三个。

设计系统的建设者往往是架构团队或基础设施团队。他们离业务远,有时间做抽象。但离业务远恰恰是问题所在——你抽象出来的东西,天然跟不上业务的节奏。

Serverless:复杂度没有消失,只是换了地址

2018年前后,Serverless在国内技术圈火了一阵。各大云厂商轮番布道,"按需付费"、"零运维"、"自动扩缩容"——每个口号都精准戳中了团队的痛点。我当时也投入了不少精力研究,毕竟谁不想不用管服务器?

几年过去了,当初那些All in Serverless的团队,相当一部分已经在往回走。不是Serverless不好,而是很多人慢慢意识到:当初以为消灭的复杂度,其实只是搬了个家。

 

那些承诺里没写的小字

 

Serverless的核心承诺有三条,每一条都有对应的现实版本。

"不用管服务器"——开发者只需写业务逻辑,基础设施平台托管。这对前端团队尤其有吸引力,写完函数丢上去就能跑,不用折腾部署和运维。但现实是,你很快会发现自己需要关注函数的内存配置、超时时间、并发限制、冷启动策略、VPC配置、IAM权限……这些不叫"服务器运维",但本质上还是在管基础设施,只是换了个仪表盘。

"按需付费"——代码不运行就不收钱,空转成本消失。但下一句没人告诉你的是:按需付费的反面是成本不可预测。一个函数死循环触发重试,一个爬虫突然疯爬你的API,一个配置错误导致无限调用——这些异常场景下的账单,会让你深切怀念固定成本的日子。

AB实验的真相:你以为的数据驱动,其实是数据背书

博客分类: 

先说一个我在做推荐系统 AB 实验架构时观察到的现象。

某个业务团队跑了一个月的实验,结论是新的推荐策略效果显著,CTR 提升了 12%,上线。三个月后回头看,整体转化率没变——用户只是提前点了,没有多买。没人复盘这件事,因为所有人都已经在那个"12% CTR 提升"的实验报告上签了字。

这不是个例。在金融产品场景里,我见过太多 AB 实验的结果被当作结论使用,而实验本身的设计、指标选择、对照组偏差,几乎没人认真审视。大家需要的不是真相,是一个可以写进周报的数字。

 

数据驱动这个词已经被用烂了

 

每家公司都说自己是数据驱动的。但如果你仔细看他们做决策的过程,会发现一个很典型的模式:先有结论,再做实验。

产品经理已经有了想法,设计已经出了方案,开发已经排了迭代——实验只是最后的"验证"。注意,不是"验证假设",是"验证决策"。这两者有本质区别。验证假设的时候你真心想知道答案,验证决策的时候你只想要一个通过。

这种心态不是某个人有问题,是整个系统的激励结构决定的。一个做了三个月的项目,谁愿意在最后一步承认实验结果不支持上线?于是你会看到各种"实验结果不显著但趋势向好"、"主要指标未达显著但次要指标表现优异"的结论。翻译成人话就是:实验没通过,但我们还是要上。

技术选型的隐性成本:你选的不只是框架,是未来三年的约束

每个技术团队都经历过这种场景:选型评审会上,几个方案摆出来,有人做性能对比,有人秀Demo效果,有人贴GitHub Star数。讨论半天,拍板一个,然后散会。团队满怀期待地开始新项目,三个月后开始骂娘。

这事儿我见过太多次了。选型时看到的都是优点,上线后暴露的全是成本。而且这些成本绝大多数在选型评审时根本没人提——不是不想提,是没人知道该提什么。

技术选型最大的陷阱不是选错了框架,而是你以为自己只选了框架。

 

你看到的是Demo,你看不到的是深渊

 

选型评审时,我们评估什么?功能列表、性能基准、社区活跃度、文档完整度。这些都是对的,也是不够的。

它们属于"可见成本"——你能在两小时的评审会上完成对比的东西。但框架的真正成本,从来不在这张表上。

你选的 Git 工作流,暴露了你的发布能力

每次团队讨论 Git 工作流,场面都很热烈。有人搬出 Git Flow 的严谨分支模型,有人推荐 Trunk-Based Development 的极简主义,还有人觉得 GitHub Flow 够用就行。辩论往往持续一个下午,最后投票选一个,README 里写上规范,全员执行。

然后三个月后你会发现:分支还是乱成一锅粥,release 分支上堆了二十个 cherry-pick,hotfix 从 master 拉出来合不回去,有人偷偷开了 feature branch 三周没合,还有人在 master 上直接 commit——"就改了一行,没必要开分支"。

工作流选错了?不,工作流没选错。是大部分团队在选工作流的时候,选的是自己向往的工作方式,而不是自己实际具备的发布能力。

 

Git Flow:为发布窗口而生的妥协

 

Vincent Driessen 在 2010 年提出 Git Flow 的时候,他的假设非常明确:你的软件有固定的发布周期。develop 分支积累功能,release 分支冻结测试,master 分支只放生产代码,hotfix 从 master 拉出紧急修复。每一步都有清晰的语义。

知识孤岛的解法不在文档里

博客分类: 

每个技术团队都有这个痛点:核心开发离职了,系统没人敢动;某个服务只有一个人能改;新人来了三个月还在问"这个接口谁负责"。然后管理层拍板——写文档!搞知识库!做Wiki!

半年后,知识库变成了数字垃圾场。文档没人读,读了也过时,过时了更没人更新。循环往复。

我的判断可能比较尖锐:文档是知识孤岛的安慰剂,不是解药。真要打破知识孤岛,得从架构设计和组织设计入手,让知识天然不容易集中到某个单点上去。

 

知识孤岛是怎么长出来的

 

先得搞清楚,知识孤岛不是"文档没写好"的产物。它是系统架构和组织结构的影子。

当一个微服务只有一个人从设计到上线全程参与,那这个人就是这个服务的知识孤岛。不是因为他不愿意分享——他可能写了很详细的README,但你让另一个人接手,还是要花两周才能上手。因为README里写的是"怎么做",而脑子里装的是"为什么这么做"以及"哪里会踩坑"。

AI+机器人创业:软件思维才是最大的坑

过去一年,我接触了不少AI和机器人方向的创业团队。有做具身智能的,有做服务机器人的,有做工业检测的,也有做消费级陪伴机器人的。聊完之后一个很强烈的感受:大量的创业者——尤其是技术背景出身的——正在用软件的思维做硬件的生意,用SaaS的逻辑评估一个物理世界的项目。

这个错位,比大多数人以为的要严重得多。

 

Demo到产品的距离,在硬件世界里是光年

 

软件创业的核心叙事是MVP——最小可行产品。两周上线一个版本,收集用户反馈,快速迭代。这套方法论在过去十年被验证了无数次,几乎成了互联网创业的圣经。

但这个叙事在机器人领域完全不成立。

一个做餐厅配送机器人的团队跟我说,他们的软件系统三个月就搭好了,SLAM算法调了两个月效果不错,避障也过得去。但整机跑起来之后,发现轮子打滑导致里程计漂移,激光雷达在不同光照条件下噪点差异巨大,电池在冬天衰减了30%导致续航不达标,餐厅地面的油渍让轮子寿命从设计的一半都不到。每一个都是"小问题",但要解决任何其中一个,都需要改模具、换供应商、重新做可靠性测试——一个循环下来三个月起步。

软件出bug可以热修复,硬件出问题只能召回。软件改一行代码的成本是几分钟,硬件改一个结构件的成本是几万块起,加上开模周期至少四周。这不是量变,这是质变。

发布恐惧怪圈:你加的每道门禁都在让发布更危险

上周有个做支付系统的朋友跟我吐槽,他们团队每次发版要过六道门禁——代码审查、单元测试覆盖率、集成测试、安全扫描、性能基线、人工审批。听起来很严谨对吧?结果他们每次发布的变更量越来越大的同时,线上故障却没有减少。反而因为每次发布都是"大手术",一出问题就是大问题。

这不是个例。我观察过不少团队,尤其是金融、支付这类对稳定性要求极高的业务线,都有一个共同的倾向:发布越怕出错,就越加流程;流程越重,发布频率越低;频率越低,每次发布承载的变更越多;变更越多,出错概率越高——然后更怕出错,继续加流程。

这就是发布恐惧怪圈。而最反直觉的地方在于:你加的每一道门禁,都在让下一次发布更危险。

 

门禁越多,发布越重

 

这是最直接的后果,也是最容易被人忽视的。

假设一个团队每周发布一次,每次发布平均包含 15 个 commit 的变更。后来因为一次线上事故,决定加强管控:发布前必须经过安全扫描和性能基线验证,流程从一天变成了三天。发布频率自然从每周一次降到了每两周一次。但开发节奏没变,两周的变更量是 30 个 commit。

看到了吗?单次发布的爆炸半径翻倍了。

日志的谎言:为什么你的日志比没有日志更危险

凌晨两点,线上出了一个支付失败的问题。你登录日志平台,输入订单号,回车。三千条日志滚动出来。你加上时间过滤,缩小到五百条。再加 ERROR 级别,剩三十条。你一条一条看——全是超时告警,没有一条告诉你为什么超时。

你把级别放宽到 WARN,三百条。大多是重试成功的记录,看起来没啥用。你换了个关键词搜,又出来几百条。折腾了一个小时,你发现关键信息藏在一个 INFO 级别的日志里,存的是上游服务的返回值,但打印的时候没用 JSON 格式,正则匹配不到。

最后你靠着回忆和猜,定了位。第二天写故障报告,复盘建议第一条写着:"增加更多日志。"

这个场景大概不需要太多解释,每个值过班的工程师都经历过。但真正值得思考的不是"日志不够"这个表面结论,而是更深的问题:你有成千上万条日志,为什么还需要靠猜?如果日志真的有用,为什么越紧急的时刻,日志越帮不上忙?

 

日志的安全感是假的

 

很多团队对日志有一种近乎宗教性的信任——出了问题看日志,没问题也要看日志确认一下。代码评审的时候,一句"这里加个日志吧"几乎不会被拒绝,因为加日志没有成本,看起来还体现了严谨。

但日志给你的安全感是虚假的。

点头不是共识:需求评审为什么总是事后翻车

博客分类: 

每个做过需求评审的人大概都经历过这个场景——产品经理对着投影仪把PRD过了一遍,问了句"大家有什么问题吗?",沉默,没人说话。"好,那就这样。"散会。

然后写代码写到一半,问题冒出来了。前端说这个交互跟另外一个模块冲突,后端说这个数据结构改不动,测试说这个场景根本没法验证。所有人突然都有意见了,但不是在评审会上。

这不是个例。在金融业务这种需求复杂度高的场景里,我见过太多次"全员通过"的需求评审最终落地时面目全非。问题不在某个角色,不在某个人,问题出在需求评审这个仪式本身——它制造的共识感是假的。

 

全员点头到底说明了什么

 

先搞清楚一件事:评审会上大家为什么都在点头。

最表层的原因——大部分人根本没消化。产品经理30分钟讲完40页PRD,信息密度像子弹,你能记住的只有自己那块冰山一角。但没人举手说"我没听懂"。这跟技术评审会上没人敢说"这个架构我没看明白"是同一种心理。沉默被解读为同意,点头被当成认可。会议室比你想的更像剧场,每个人都在演"我在认真听"。

从Redux到Signal,状态管理为什么换了三代还是不满意

前端开发有一个有趣的规律:每隔两三年,社区就会宣布一个新的状态管理"最佳实践"。Redux曾是不可撼动的标准,然后MobX说响应式才是正道,Dva试图让Redux更人性化,Zustand说状态管理不该这么啰嗦,Jotai说应该是原子化的,现在Signal又告诉你要从原语层面重新思考这个问题。每一次更迭都伴随着"旧方案已死"的宣言,和一波迁移重构。

但有一个问题始终没人回答:如果每个新方案都比上一个好,为什么我们还是不满意?

经历过Redux到Dva再到Zustand的完整迁移,我发现每一次换库的时候,团队都觉得这次终于对了。每一次,半年后又会发现新的别扭。这种循环让人不得不怀疑——问题可能根本不在库里。

 

Redux的铁律和代价

 

Redux的核心思想并不复杂:单一数据源,纯函数更新,不可变数据。这三条铁律在2016年前后确实解决了一个真实痛点——复杂应用中状态变化的不可预测性。组件树里的状态到处飞,回调层层传递,谁能改、谁不能改、改了之后谁是新的,没人说得清。Redux用一套严格的规则堵住了这个口子。

你招的不是最优秀的人,是最像你的人

博客分类: 

每个技术团队都有个共同的幻觉:我们在招最优秀的人。

JD写的是"追求卓越",面试聊的是"技术深度",评级用的是"超出预期"。所有的信号都在告诉你,你们在建立一个精英团队。但你有没有认真看过你团队里的人——他们解决问题的思路是不是惊人地相似?他们认同的技术方案是不是高度一致?他们对"好代码"的定义是不是几乎一模一样?

这不是因为相似的人恰好都最优秀。这是因为你在招聘的第一步,就已经把"和我像"编码成了"优秀"。

 

你以为的标准,其实是你的倒影

 

技术面试有一个很少被质疑的前提:面试官能够客观地判断候选人的技术能力。但事实是,大部分技术面试的"客观标准",不过是面试官自身经验的主观投射。

举个很常见的场景:两个候选人,能力相当,但一个和你用了同样的技术栈、同样的架构思路,另一个用了不同的方案解决了同样的问题。你觉得谁更强?大概率你会觉得前者"更扎实"、"理解更深",后者"还行但细节不够"、"方向有点偏"。这不是你在有意偏袒——是你的大脑在用"熟悉度"作为"质量"的代理指标。

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

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

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

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

 

兼容性承诺是一种债务

 

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

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

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

博客分类: 

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

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

 

信息同步?别自欺了

 

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

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

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

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

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

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

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

 

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

 

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

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

1on1已死,只是通知还没到你

博客分类: 

每个技术管理者都有个执念:1on1。书上说1on1,培训教1on1,入职第一天HR就跟你说"你的1on1频率是每周一次"。于是你信了,每周往日历上塞一个30分钟的会议邀请,拎着笔记本走进小会议室,然后——聊了30分钟的进度。

你是不是觉得这也没什么?毕竟了解项目进展也挺重要的。但问题是,你有没有想过,这30分钟的进度同步,和你早会上问的那句"这个需求什么时候能提测",到底有什么本质区别?

1on1正在变成一场管理者自我感动的仪式。你去了,你聊了,你觉得自己在"关心人"。但如果你把过去半年1on1的聊天记录拉出来看一遍,大概率80%的内容是项目状态、deadline确认、阻塞项梳理。剩下20%是"最近压力大吗"——然后对方说"还好",你说"注意休息",对话结束。

这不是1on1,这是换了个场地的工作汇报。

 

它是怎么一步一步变成周报会的

 

没有人一开始就打算把1on1搞成这样。每个新晋管理者第一次和下属1on1的时候,都带着真诚来的——"我想了解你的想法"、"有什么困难尽管说"。但很快,现实就开始纠正你的浪漫。

你的错误处理只是安慰剂

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

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

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

 

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

 

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

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

页面