最近一位在头部互联网公司做技术研究的前辈在一次内部分享里说了一句让我印象很深的话“学好语文才能写好代码。”当时台下一半人当段子笑了我也跟着乐了。但回去之后我翻出自己过去几年写过的代码、写过的文档、参加过的评审记录越想越觉得这句话不是玩笑而是一句被低估的实话。今天不写教程也不做鸡汤就着这句话聊聊我自己的理解。顺便给那些同样被变量命名、注释、技术文档、代码评审折磨过的工程师一些确实能落地的建议。如果你刚入行两三年正在困惑“为什么我功能做完了代码还是被人说难维护”这篇文章应该对你有用。1. 一句“学好语文”戳中的是程序员的基本盘1.1 代码最大的读者从来不是机器很多人对写代码有一个误解觉得代码是写给编译器看的语法正确、能跑就行。这个想法在写脚本、做一次性任务的时候勉强成立一旦进入多人协作、长期维护的业务系统就会变成灾难。代码真正的第一读者是下一位维护它的工程师。编译器和运行环境只关心“语法是否正确”而人阅读代码时依赖的是语义理解。这就意味着你需要用代码准确地表达“你想干什么”“为什么这么干”“边界条件是什么”。这些表达能力的底层恰好就是语文。我带过不少同学遇到过一个很典型的对比同一个需求A工程师和B工程师的技术能力其实差不多但A写的代码评审一遍过大家还能顺手聊聊业务上的坑B写的代码一进评审会就变成考古现场光“这个变量到底代表什么”就能讨论二十分钟。两个人的代码都能运行但可维护性完全不在一个量级上差距不在技术在表达。那位前辈说得很直白他见过太多技术方案设计上没有任何错误但写出来别人看不懂最后没人敢改只能变成“祖传代码”。代码一旦走到没人敢动的地步技术债就不是重构能解决的问题了而是整个团队的认知负担。1.2 “语文不好”的代码长什么样写代码写得“语文不好”是有明显特征的。我整理了这几年评审里反复见到的几类变量名和函数名信息量为零data1、data2、list、temp、deal()。注释等于复读机代码下面写// 创建订单下一行就是createOrder()。一个函数三百行八层 if-else 嵌套读的时候需要在大脑里维护一个调用栈。提交信息全是fix、update、aaa三个月后看 git log 根本不知道当时干了什么。文档和代码严重脱节README 还停留在两年前的架构代码里已经换了三套方案。这些问题的共同点是机器层面没有任何错误但人在理解层面持续消耗精力。写代码本质上是在写一份说明书读者是同事、是三个月后的自己、是接手你模块的陌生人。说明书质量差机器照样跑但每一个后来者都要多花时间团队的效率就这样被一点点拉低。我印象很深的是有位前辈用一句话点破过这件事“代码是写给人看的顺便让机器能跑。”这句话和“学好语文才能写好代码”其实说的是同一件事。2. 遣词造句变量命名和注释都是语文题2.1 命名是每天做得最多的语文练习程序员大概是成年人里每天做“遣词造句”最多的一类人。变量名、函数名、类名、文件名只要在写代码就在做词汇选择和语义表达。一个词选得准不准直接影响读代码的人能不能一次理解到位。举个最简单的例子同样是代表“金额”的字段amount、totalAmount、payableAmount、actualPaidAmount在业务里是有严格区别的。写的人心里清楚但读的人不知道。如果你命名的时候偷懒写个money或者干脆a别人就只能在上下文的蛛丝马迹里猜。猜来猜去bug 就藏在这种模糊地带。我整理过一张反面对照表每次带新人时都会发给他们反面典型问题推荐写法理由temp没有任何语义pendingRefundAmt/retryCount说明清楚是什么data1/data2数字编号无含义refundList/orderItemList表达业务语义doSomeThing()动词太泛executeRefund()动词加对象flag布尔变量含义不明isPaySuccess明确判断主体和结果命名这件事有点像中文里的用词同样表示“拿”拿、取、拾、捞、端动作细节完全不一样英文里也有get、fetch、query、load的区别。用词的精度本质上就是词汇量和语义分辨能力的体现。实操层面我有一个建议每个团队都应该维护一份“业务命名词典”。同样是订单相关概念order、paymentOrder、refundOrder全组统一说法不许一会儿叫order一会儿叫bill一会儿又冒出个trade。定好之后代码里、文档里、评审群里都用同一套词沟通摩擦会肉眼可见地减少。2.2 注释是“为什么”的文体练习注释大概是代码里被骂得最多、也最该被认真对待的东西。新手容易写一堆“是什么”注释老手则知道注释的价值在于解释“为什么”。我拿一个片段说明问题# 坏注释复述代码纯属浪费 # 获取订单金额 amount get_order_amount(order_id) # 好注释解释代码之外的决策原因 # 订单金额必须从订单快照读取而不是实时计算 # 因为历史订单可能已调整价格实时金额会和当时的账单不一致 amount get_snapshot_amount(order_id)这个例子里的“坏注释”就是语文里说的“车轱辘话”——说了一堆信息量是零。而真正有价值的注释解释的是代码本身无法表达的信息为什么不用另一个方案、为什么这个边界条件必须处理、为什么这里可以延迟三天。还有一个容易被忽略的点中国工程师写代码用英文思考用中文注释大概率也用中文。这其实是一个两套语言体系之间的“转译”过程。你脑子里有一个复杂的业务背景需要一个词变成变量名用一句话解释这个决策再用一段注释留下来给后来人看。转译得准确、简洁、不产生歧义这靠的确实是语文能力。我见过最好的注释都遵循了同一个结构“背景 — 原因 — 影响”。先说这里为什么特殊再说选择当前做法的理由最后说如果不这样做会怎样。三句话以内讲完比长篇大论有用得多。3. 谋篇布局写代码和写文章是一套结构功夫3.1 从“流水账代码”到“分段落写作”如果把代码比作文章那函数就是句子模块就是段落服务就是章节。写文章要分段因为一整页内容堆在一起没人读得下去代码也一样一个函数几百行不做拆分再简单的逻辑也会把人读崩溃。我见过最典型的“流水账代码”是把所有步骤塞进一个函数里先校验库存再算优惠再创建订单再扣减库存全程平铺直叙。阅读的时候你需要自己在心里画出“这段是干什么的”相当于文章没有分段全靠读者自己归纳中心思想。用代码来说大概是这种感觉# 流水账式所有逻辑堆在一个函数里 def submit_order(user_id, items, coupon_id): # 检查库存 for item in items: stock check_stock(item.sku_id) if stock item.num: raise Exception(库存不足) # 计算金额 amount 0 for item in items: amount item.price * item.num if coupon_id: amount apply_coupon(coupon_id, amount) # 创建订单 order create_order(user_id, amount) # 扣减库存 for item in items: reduce_stock(item.sku_id, item.num) return order代码本身没有错但读起来难受。如果换成“先写提纲再展开细节”的方式# 结构化写法入口像提纲细节另有段落 def submit_order(user_id, items, coupon_id): validate_stock(items) amount calc_amount(items, coupon_id) order create_order(user_id, amount) lock_stock(items) return order后者的优越之处在于入口函数只负责“编排步骤”每一句话都是对读者的提示如果你想了解库存怎么校验去看validate_stock如果你想看金额怎么算去看calc_amount。这就叫“详略得当”写文章的基本要求放在代码里同样成立。3.2 逻辑链与抽象层级对应写作里的思维框架写作讲究总分结构、递进关系、因果关系写代码讲究分层抽象、时序逻辑、状态流转。表面上是两套东西底层是同一个能力把复杂的念头组织成别人能一次理解的结构。一个很常见的现象是代码写不清楚不是写的时候出了问题而是想的时候就没理顺。我自己有段时间负责一个跨系统对账模块每次都写得很痛苦拆了写、写了拆总觉得哪里不对。后来被逼着先写了一份“这个模块到底怎么工作”的纯文字说明写着写着发现原来我把“系统A的事件”和“系统B的流水”两个概念混在一起了。问题压根不在代码在概念。这就是费曼技巧在编程里的应用如果你讲不清楚一件事说明你自己也没搞懂对应到工程里如果你写不清楚一段逻辑说明设计本身就藏着隐含假设。还有一个值得刻意练习的动作写技术方案文档。方案文档不是给领导看的作业而是逼自己把脑中的模糊想法变成清晰结构的过程。背景、目标、约束、方案对比、风险、回滚方案这套框架本质上是议论文写作要有论点、论据、正反对比、结论。能把方案文档写清楚的工程师代码抽象能力通常都不会差。4. 代码之外的“语文”评审、文档与跨角色沟通4.1 代码评审本质上是一次阅读理解测试代码评审最耗时间的环节永远不是“找问题”而是“看懂对方在做什么”。一次评审的效率高低很大程度上取决于提交者有没有把自己的意图表达清楚。我经常看到这样的提交说明修改下单流程修复金额计算问题。没了。评审者看完这行字必须自己打开 diff逐行对比猜测这次改了什么、为什么要改、影响了哪些场景。这个过程不仅慢而且容易猜错。如果提交说明换成这样背景优惠券叠加时金额计算顺序有误导致部分订单少收运费。 改动将优惠计算移到运费计算之前并补充叠加场景用例。 影响仅影响使用满减券且同时产生运费的订单。 测试本地构造三类叠加场景全部通过。四句话四件事背景、改动、影响、测试全覆盖。评审者带着上下文去看 diff一眼就能判断改动是否合理剩下的时间全用来讨论真正有价值的问题。关于阅读理解的另一个观察来自我自己的一个经历有次团队评审一个改造方案半小时没人说话最后发现大家并不是在反对方案而是对方案里反复出现的“金额”一词理解不一致——有人理解成下单金额有人理解成实付金额。方案本身没有任何分歧却因为表达不精确耗掉了小半天。这种浪费在团队协作里几乎每周都会发生根源绝大多数时候就是语文问题。4.2 技术文档和面对非技术角色时的“翻译”能力代码评审是写在代码里的表达技术文档则是更正式的表达场景。我发现很多工程师写文档有个通病把文档当成代码的堆砌贴一大堆代码块却不说清楚“为什么这么做”。结果文档倒成了代码的复读机。好的技术文档应该像一篇议论文先给结论再说背景然后对比方案最后明确代价和步骤。读者可以只读前三百字就决定自己要不要继续深入想看细节也有清晰的路径。这种“分层给信息”的结构是语文里“主次分明”在技术写作上的直接应用。还有一类容易被忽略的语文能力是面对非技术角色时的“翻译”能力。你对接的产品经理不关心接口幂等性但他在意“用户快速点了两次提交按钮会不会生成两个订单”。把一个技术概念用生活化的类比讲清楚让不懂编程的人也能做判断这是打比方的能力、举例子讲人话的能力本质上也来自语文功底。事故复盘也同理。好的复盘文字应该是先说现象再说影响范围然后时间线然后根因最后改进动作。很多人把复盘写成检讨书或者写成代码式的流水账看起来写了很长信息密度却低得惊人。同样的材料换一种表达结构可读性天差地别。5. 工程师补“语文”的五个可落地动作说完道理说点能直接上手做的。下面这五个动作我自己试了大半年效果比想象中好也不需要额外挤出大块时间。5.1 建一个命名黑名单给团队做“词典”先给团队定一份命名黑名单比如temp、data、info、doWork、handle不是绝对禁用但使用的时候必须写注释解释清楚。再从现有代码里挑出最典型的二十个坏命名拉上团队集体讨论一轮投票改成什么顺便沉淀出一份团队内部的“命名词典”把高频业务概念统一掉。这个动作见效很快。改完一轮之后新代码的命名质量会明显上一个台阶因为“随手写一个 temp”的习惯被硬性打破了。5.2 每天一条“为什么”式技术笔记不用写长文每天一百五十字就够。模板很简单今天做了一个什么改动为什么这样改有没有考虑过其它方案为什么没选。写的时候坚持用大白话不要堆术语不要列流水账。时间久了你会发现自己梳理思路的速度变快了很多问题在写笔记的阶段就被暴露出来。我自己的体会是比起专门读写作书这种“输出式练习”对工程师的表达能力提升更直接。5.3 选一个你负责的模块用纯文字重写它挑一个你最有发言权的模块把它解决什么问题、数据从哪里来到哪里去、哪些分支最容易出问题写成一篇纯文字说明文。目标读者设置为“完全没看过代码的同事”读完能用自己的话复述出五成以上就算过关。这个练习最有意思的地方在于当你把代码翻译成文字时常常会发现自己代码里的隐含假设和逻辑漏洞。文字思维和代码思维互相校验比单纯对着代码审查效果好得多。5.4 在代码评审里刻意练习“先说结论”每次提 PR强制要求描述的前三行写完三件事改了什么、为什么改、影响范围。然后删掉所有形容词只留事实。写完之后你会觉得有点干但评审者反而会觉得舒服。日常沟通也适用群消息先说结论再说背景例会上先说行动项再说细节。这种表达习惯一旦养成会直接改变别人对你专业度的感知因为它反映的是“你已经想清楚了”。5.5 读开源项目怎么写文档、怎么写 changelog、怎么写 issue找一两个你常用的开源项目认真看它的 README 结构、changelog 用词、维护者是怎么回复 issue 的。这些内容是质量非常高的“技术写作样本”比大多数写作教程都实在。看得多了你大概会总结出它们的共性一句话讲清动机分层给信息先结论后细节术语第一次出现时有解释。把这些顺手用在你的文档和评审描述里很快就能看到反馈。我自己按这些方法调整了大半年最大变化不是代码变得多优美而是沟通摩擦明显变少。以前需要开半小时会才能说清楚的改动现在一个描述充分的 PR 就能推进。“学好语文才能写好代码”这句话初听像段子细想全是基本功。它说的并不是要你去成为文学家而是希望我们能把一个模糊的念头组织成一套别人也能理解的结构。这件事对于工程师确实比想象中重要得多。