代码写得多了,我越发认同一个看起来有点“反直觉”的说法:学好语文,才能写好代码。我第一次听到这话,是在某家大型支付平台的一次内部技术分享上,讲这句话的是一位研究员级别的老前辈。当时台下不少人当段子听,我倒是越想越觉得有道理。
事情得从一段让我刻骨铭心的代码说起。我转正之后接手的第一个模块,来自一位已经离职的同事。代码里大量出现a1、b2、tmp、data_list这类命名,注释只有三行,其中两行写着“这里有问题”和“别动”。我花了整整两周,才勉强搞清楚里面一个核心函数到底在干什么。那段时间我每天都在问同一个问题:代码写出来,到底是给机器看的,还是给人看的?
这个问题想通了,很多东西就顺了。
1. 代码的第一读者不是机器,是人——聊聊“学好语文”和“写好代码”的关系
1.1 编译器只检查语法,人检查“语义”
很多新手有一个误解,觉得代码只要编译通过、运行结果正确,就算写完了。这句话对了一半。机器只关心语法和逻辑,但代码真正的第一读者,永远是人。
一个函数,机器会老老实实执行它;但团队里的其他人、三个月后的你自己,需要读懂它、维护它、扩展它。编译器能抓出括号不匹配、类型错误,却抓不出“这个函数叫do_sth,它到底做了什么”这种致命问题。
工程代码的平均寿命远远超出很多人的预期。一段业务代码往往要经历需求迭代、bug 修复、人员交接、线上复盘,读它的次数可能比写它的次数多几十倍。代码的维护成本几乎全部发生在“人读代码”这个环节,而“人读代码”靠的不是语法分析,是阅读理解能力。
我后来带团队时经常跟新人说一句话:写代码要当心三个月后的自己。那时候你会像一个陌生人一样,对你的代码一无所知。如果你自己都看不懂自己写的代码,你要么是在写代码,要么是在制造灾难。
1.2 “语文”到底指的是什么能力
这里说的“语文”,不是背古诗词、写议论文那个语文。它在编程语境下,拆开来是四种能力:
- 精确理解他人表达的能力:看不懂需求文档、听不懂产品经理的话,代码写得再花哨也跑偏。
- 清晰组织文字的能力:变量名、注释、文档、提交信息,全是文字表达。
- 抽象概括的能力:把复杂逻辑提炼成几句话,把散乱的模块梳理出清晰的边界。
- 用类比把复杂变简单的能力:给非技术背景的人讲清楚技术方案,靠的正是语文里的比喻和类比。
写代码本质上是一连串“翻译”动作:把业务需求翻译成技术语言,把技术语言翻译成机器指令,再把代码意图翻译回文字,告诉下一个读者“这段代码为什么存在、为什么这么写”。任何一个翻译环节没过关,信息就会折损,折损的代价就是返工和加班。
有个生活化的类比我一直很喜欢:代码就像是菜谱。菜谱只是给厨师看的,机器会做的只是把菜谱“流式执行”一遍,但其他厨师能不能照着你写的菜谱做出同款菜,完全取决于你的措辞是否清楚。食材清单都列全了、步骤都对,但“适量盐”“小火慢炖”这种模糊表达,就足够让厨房翻车。
1.3 技术债里最隐蔽的一种是“表达债”
技术圈爱谈技术债,什么设计债、测试债、架构债,都有人研究。但有一种债几乎没人提,我管它叫表达债——代码逻辑其实不算乱,但你根本看不懂它想表达什么。
逻辑混乱的代码好歹还能靠重构救一救,表达债却会让你连重构的勇气都没有。我见过一个典型的场景:某模块的旧负责人离职后,留下一仓库的缩写命名,函数职责边界模糊,核心逻辑散落在一个 500 行的函数里。新团队花了整整两周审阅,才敢对一行代码动手——不是因为那行代码多难,而是因为没人看得懂它的依赖关系和前置条件。
后来团队做了一个很朴素的调整:重命名变量、拆分函数、补上“为什么”注释。代码逻辑几乎没有任何变化,但可维护性完全不一样了。那次之后,我是彻底信了这句话:语文能力的短板,会在长期维护里以“隐性工时”的方式反复向你收费。
2. 命名、注释与提交信息——程序员每天“写作文”的三个考场
2.1 变量命名的灾难现场与自救
先列几个我在 code review 里经常看到的命名,每个都是活生生的反面教材:
| 常见反例 | 问题出在哪 | 推荐改法 |
|---|---|---|
a1、a2 | 完全没有语义,只能靠上下文猜 | expire_time、create_time |
tmp | 指代不明,不同位置的tmp含义完全不同 | reduced_price、pending_count |
do_sth、deal | 动词含糊,看不出职责 | apply_discount、calculate_total_amount |
res | 到底是 result 还是 resource?前后矛盾 | query_result、order_response |
data2 | 比data多了个 2,但这根本不是版本号 | need_billing_data或filtered_rows |
命名的基本原则其实就一句话:变量名是职责的缩写,函数名是行为的说明书。一个名字如果说不清楚,往往不是因为你的词汇量不够,而是因为你在动手前就没想清楚它到底负责什么。
我印象很深的一次重构,发生在上面提到的那个“离职同事遗留模块”上。当时我硬着头皮花了一周,把a1、b2、tmp全部替换成可读的名字,原代码一行逻辑都没改。奇迹发生了:原本团队讨论要三小时才能理清的流程,重命名后只花了一小时十分钟。代码还是那段代码,只是“人话”变多了。
这个操作后来成了我给新人做培训时的保留题目。我会随手选一段代码,让大家说说每个变量名表达了什么。说不上来的,基本都不是英语问题,是设计问题。
2.2 注释的三种层次:翻译、意图与契约
注释是“语文”在代码里最集中的体现,但也是最容易写烂的地方。我把注释分成三个层次:
第一层:翻译注释,彻底没用。比如:
# 把 a 和 b 加起来 result = a + b这种注释的唯一贡献是制造噪音。代码本身已经说了它要做什么,你加上去,反而干扰阅读。
第二层:意图注释,这才是注释的核心价值。代码能告诉你“做了什么”,但通常说不清“为什么这么做”。比如:
# 缓存不能加在这里,因为订单过期时间由下游配置决定, # 一旦缓存会读到脏数据,读穿透的成本在可接受范围内 order = query_order(order_id)如果没有这段注释,后来的人很可能会“好心”地在这里加一个 Redis 缓存,然后踩进一个线上事故里。意图注释回答的是:这段代码为什么长这样,为什么不那样写。
第三层:契约注释,用来交代代码之外的约束条件。比如:
# 调用前必须持有全局锁,且并发线程数不允许超过 4,否则会触发死锁 def sync_xxx(): pass这种信息藏在代码里完全看不出来,但写出来就能救你一命。
我自己给团队定的注释纪律就一条:注释里只写代码自己不能表达的信息。能用代码说清楚的,不要用注释;注释要优先写“为什么”,而不是“是什么”。
2.3 commit message 和 PR 描述:被忽略的“职场作文”
如果说命名和注释是代码内部的语文,那么 commit message 和 PR 描述就是代码外部的语文。说实话,我见过不少代码写得挺干净的程序员,一到写提交信息就原形毕露。
最常见的反例就是这几个:
fix bugupdate修改代码优化
这种提交信息等于没写。三个月后你翻 git log,看到的是一堆“fix bug”“update”,完全无法恢复当时的上下文。更可怕的是,线上出故障时,值班同事只能对着这种历史空转。
我后来在团队里强制要求提交信息必须说清楚三件事:改了什么、为什么改、影响范围。主题行写成“动词 + 对象 + 原因”的格式,正文可以短,但必须给出背景。
比如这样:
修复订单导出时间窗口问题 背景:业务方反馈跨时区订单经常漏导。 原因:之前按自然日 0-24 点筛选,而业务实际需要按 “从当前时刻往前滚动 24 小时”计算。 改动:将筛选逻辑替换为滚动时间窗口。 影响:仅影响导出查询;不涉及其他模块。 验证:已用跨时区模拟数据跑通,抽样比对无漏单。这个习惯一开始确实痛苦,大家会发现很多提交根本写不出“为什么”四个字。但只要坚持两周,所有人都会发现一个神奇的变化:查 bug 比以前快多了。因为 git log 真的变成了一本可以翻阅的项目史,而不是一堆无法解码的暗号。
3. 从需求到落地的“翻译链”——语文不好,代码从一开始就跑偏
3.1 需求一句话,代码跑偏一百里
程序员每天干的第一件事,其实是“阅读”。读需求文档、读原型图、开会听产品经理说话。这一关没过,后面全是白干。
业务语言天然充满歧义。同一个词,在业务侧、技术侧、用户侧可能有完全不同的含义。我经历过一次典型的翻车:需求方提了一句“导出今天的订单”,开发人员很自然地按“自然日 0 点到 24 点”筛选数据。结果开发完了,业务方说不对,他们真正要的是“从当前时刻往前推 24 小时”的滚动窗口,因为运营同学会在凌晨导出前一天的订单,按自然日算会漏单。
一句话的歧义,浪费了一整个迭代的开发量。
语文能力强的人,会本能地做两件事:复述和反问。接到需求时多嘴问一句:“你的意思是,以支付成功时间为准,从昨天上午 10 点到今天上午 10 点,对吗?”这种习惯能帮你挡掉八成以上的返工。它不是需求分析师的专利,是每个会写代码的人都应该具备的基本功。
3.2 技术方案的本质是“书面沟通”
写技术方案这件事,很多程序员把它当成交差:背景复制粘贴,核心设计写一大段晦涩的架构图,收尾来个“综上所述”。这种方案发出来,评审者要么看不懂被退回,要么没人真看懂却直接通过了——后者更危险。
方案的本质是书面沟通,它是让评审者在最短时间内对你建立的系统建立正确心智模型的工具。表达混乱的方案,大概率意味着思路本身还不清晰。
我建议方案至少包含四件套:
- 背景:为什么要做这件事,不做会怎样。
- 方案:你打算怎么做,核心链路是什么。
- 取舍:为什么不用其他备选方案,这个方案牺牲了什么。
- 风险:哪里可能翻车,怎么兜底。
大部分人只写第二件,导致评审变成答疑大会,一个个来回盘问。如果你在写方案时发现“取舍”和“风险”写不出来,那不是文笔问题,是设计还没有闭环。写不清楚就是没想清楚,这个判断标准比任何 review 都严格。
3.3 评审和跨部门沟通中的“语言组织能力”
代码评审里有一种熟悉的味道:讲的人很激动,听的人很迷茫。问题的核心往往出在组织语言时,完全没考虑听众。
有一次我们跨部门评审一个订单状态机的设计,一个同事准备得非常充分,时序图画了五张,讲得满头大汗,但对面业务方全程表情空白。后来另一个同事站起来说:“你就把订单状态当成洗衣机。待机、进水、洗涤、排水、脱水、完成,任何一个环节出问题就进故障检修,修好了继续跑,修不好就走报废流程。”
全场三秒就懂了。
这个例子说明的是语文里的“类比”能力。在写代码时,类比能力的价值同样是核弹级的:设计系统时,如果你能用一句话比喻讲清楚核心数据流,说明你已经先在心里验证了一遍这个设计。讲不清楚,大概率说明某些环节自己也没完全捋直。
这种能力是可以练的。每次给自己两分钟,把刚做完的模块用一句话讲给一个非技术背景的人听。讲得通,你才真正理解了它;讲不通,回去再看一遍自己的代码。
4. 把“练语文”落到日常:我拆出的四个可执行动作
光谈观念没用,我会说一些真正落地的动作。这些年我靠这几个习惯,明显感觉自己写代码和带团队都顺了不少。
4.1 动作一:给每个命名写“一句话理由”
这个动作来自一次 code review。当时我盯着一行代码的变量data看了半天,问作者:“你这个 data 到底存的是什么?”他想了半天,说“就是数据啊”。这句话一出口,我们俩都愣住了——一个连作者都说不清语义的变量名,放在线上跑了三个月。
从那以后我给自己定了个规矩:任何命名,如果能用一句话解释它在系统里的职责,就成立;解释不了,就说明它不该存在,或者名字该换。具体操作是定期对自己负责的模块做一次“命名复盘”,把模糊的命名列出来,逐一替换。
你试试就知道,给名字找理由的过程,往往会倒逼出重构。因为有些函数和变量你压根解释不清,它们就是历史包袱,不删留着过年。
4.2 动作二:注释先写“为什么”,再写“是什么”
写注释的正确姿势,不是对着代码逐行翻译,而是先回答一个问题:如果这段代码没有任何注释,三个月后的我会在哪个环节卡住?
把答案写下来,这就是一条合格的意图注释。
比如:
# 这里不用 Redis 缓存:订单过期时间由 MQ 异步更新, # 缓存一致性维护成本高于收益,当前读 QPS 完全扛得住 def get_order(order_id): ...它回答了三个问题:为什么不用更“聪明”的方案、有什么代价、为什么接受这个代价。后来的人看到这段注释,就不太可能手贱改掉你的设计。
团队纪律就一条:禁止翻译注释,禁止“这里有问题”“别动”这种没有信息量的废话。注释是为了填补代码表达的盲区,不是为了凑行数。
4.3 动作三:把 commit message 当“工作日志”写
前面已经讲了 commit message 的格式,这里补充一个操作层面的建议:主题行一定不要超过一句话,正文用列表列清楚关键信息。
我个人的习惯模板是这样的:
- 第一行:
动词 + 对象 + 原因,比如“缩短订单查询超时时间,消除上游慢查询阻塞” - 空一行后写正文:
- 背景:为什么要做这个改动
- 改动:改了什么模块、什么核心逻辑
- 影响:会影响哪些调用方、需要注意什么
- 验证:怎么自测的、结果如何
这个格式并不复杂,但它能让你在三个月后线上出问题时,只需要一条git log就能恢复所有上下文。有一次我们在追查一个生产环境的数据异常,靠的是一条半年前写的 commit message,里面恰好备注了“此逻辑依赖上游的时区配置,改动前务必确认”。这句话帮我们排除了一个重大嫌疑方向,节省了至少两小时排查时间。
这就是语文能力在“运维”场景里的真金白银。
4.4 动作四:每周输出一篇“人话版”技术笔记
最后一个动作跟代码无关,但效果极其显著:每周选一个本周解决过的问题,用不超过五百字的篇幅,把它讲给一个完全没有技术背景的人听。
这不是为了传播,而是为了倒逼自己梳理模糊地带。你会发现一个很有意思的现象:好多事你觉得自己懂了,一写就卡壳。真到“写出来”这一步,那些含糊的、没想透的地方全都会浮现出来。
我坚持写技术笔记已经很多年。最大的受益根本不在“分享出去”,而在“写之前被迫把脑子里那团模糊的毛线团拆成一根根清晰的线头”。如果你觉得“明明会做,但讲不出来”,那九成不是表达天赋问题,而是你对这件事的理解还停留在“能做”的层面,没到“能讲清”的层面。
我这些年面试过不少人,也带过不少新人,发现一个很朴素的规律:代码命名清楚、注释准确、提交信息可读的人,写技术文档、发协作消息、讲方案,通常也不差。反过来,代码乱成一团的人,让他口头讲讲自己的设计,往往也讲不出完整的因果链。
这不是玄学,而是同一种能力的两面——能不能在脑子里把事想清楚,并通过语言精确地传递出去。“学好语文,才能写好代码”这句话看似鸡汤,其实是一条被反复验证过的工程经验。如果你也正在被“写得出来却说不清楚”困扰,不妨从给一个变量起好名字开始试试。