ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

写出让同事纷纷上门的“省心代码”:从命名、结构到评审的全套实践

写出让同事纷纷上门的“省心代码”:从命名、结构到评审的全套实践 我先说个真事。前几天我们组一个小伙子把一段接口返回的字段统一成了下划线转驼峰每个布尔变量都起了isXxx这种一眼能看懂的名字原来二十多行的 if 嵌套也被他拆成了三个独立小函数。结果评审一过邻组同事居然专门跑来问他要代码片段当参考连测试同事都多看了两眼他的提测说明。标题说“同事纷纷上门祝贺”真没夸张。这件事我琢磨了几天发现大家夸他代码好绕来绕去就几个字省心。今天就把这种“省心代码”怎么练出来的拆一遍。1. 先想清楚同事眼里的“好代码”到底长什么样1.1 能跑只是底线能改才是本事很多程序员入行时以为“写得好”指算法够妙、API 冷门、一行代码把功能算完。但真在团队里待过的人都知道同事不会因为你用了位运算、lambda 嵌套怪招来给你鼓掌真正让他们上门的理由往往是你的代码让人少猜了十分钟。代码要同时给机器和同事看。机器看语法同事看语义。如果一段代码机器能跑但是别人接手时完全不知道哪一步在干什么那它只是“写出来了”离“写好了”还有差距。换句话说写完代码以后你能闭上眼睛把数据流转讲一遍讲得清楚代码基本就及格了讲不清楚的地方就是留给大家踩坑的地方。踩过的坑多了就会发现团队里最受欢迎的人不是技术名词说得最多的那个人而是交出来的代码让别人接手成本最低的那个人。所谓功底好表现在结果上就是“别人愿意动你的代码”。1.2 三个硬指标可读、可查、可拆我给“好代码”定了三个特别朴素的指标不是学术定义就是日常评审的实用标准。第一是可读。拿到一段代码不需要来回翻上下文就能大致知道它处理了什么、先做什么后做什么。实现手段主要靠命名和结构而不是注释。第二个是可查。线上或者测试环境出了问题能从日志和报错信息比较快地定位到对应代码位置。这就要求函数职责分离一个函数干一件事查起来才不会像大海捞针。第三个是可拆。改需求时能在局部调整而不是牵一发动全身。模块边界清楚、依赖方向明确才能拆得开。这三个指标不需要什么高深理论但对开发习惯要求不低。养成好习惯前先得把“写完运行通过”这种思维升级成“写完后同事能在三分钟内看懂”。1.3 你的同事里有一个叫“未来的自己”还有一种同事最容易被忽略就是三个月后的自己。当时清爽的代码三个月后回来看也可能一脸懵。我有过这种经历为了赶版本写了一段非常“聪明”的缓存刷新逻辑当时觉得天下无敌后来线上问题定位自己都要在草稿纸上画半天才想起来意图。后来我就给自己留了个规矩凡是当时想了一会儿才想明白的地方必须留一句注释说明思路哪怕只是半行。这句话不是写给别人的是写给未来我的。“好代码”的时间维度也要拉长不是上线那一刻算结束而是维护周期里一直能被人理解。2. 命名与结构第一个让同事点头的细节2.1 变量命名不是玄学是信息传递命名是代码评审里最容易被看到的部分。变量名这件事我常用的判断标准是把代码里所有变量名换成a、b、tmp再看一遍如果仍然能猜到大致逻辑说明结构还行如果完全看不懂那问题通常不在名字而在变量太多、函数太长。举一个真实场景。我负责过一个订单模块早期代码里有大量flag开头的写法user get_user() flag user.is_vip if flag: fee calc_vip_price() else: fee calc_normal_price()这段代码本身没错但flag完全没表达业务含义。后来改成了is_vip_member一眼就能看出判断的是会员状态。同样的逻辑信息量差得非常远。再比如快速排序里常见的双指针教材里爱写i和j但换到工程代码里写成left和right就不用在脑子里反复映射。# 推荐left/right 是上下文自明的 def quicksort_range(arr, lo, hi): if lo hi: return left, right lo, hi pivot arr[(lo hi) // 2] while left right: while arr[left] pivot: left 1 while arr[right] pivot: right - 1 if left right: arr[left], arr[right] arr[right], arr[left] left 1 right - 1 quicksort_range(arr, lo, right) quicksort_range(arr, left, hi)临时变量不是不能用但要有“生命周期意识”如果一个变量的作用范围超过五六行最好给它一个能讲清楚用途的名字。给同事省下的每一次思考最后都会变成你代码口碑的积累。2.2 函数边界一个函数最好只说一件事“一个函数只干一件事”这句话听上去像废话真正写起来最难。一种常见情况是开发时需求明确顺手把校验、鉴权、业务计算、日志、通知全塞进一个handleOrder()开始还行等功能一多这个函数会膨胀到几百行。我会先把大函数拆开拆法的核心不是“少写几行”而是“让每个片段有名字”。比如一个下单服务可以拆成checkOrder、lockStock、calcPayment、flushEvent这几步每一步对应一个函数名。主流程读下来相当于在看一张流程清单def handle_order(order_id): check_order(order_id) lock_stock(order_id) fee calc_payment(order_id) flush_event(order_id, fee)这样拆完之后挂在哪一步就去哪个函数里看不需要从头到尾读一遍所有实现细节。同事看到这种主流程通常都能很快进入状态这也是“上门祝贺”的关键原因之一他们不用花半小时才能看懂你要干什么。一个很实用的经验是如果函数超过 30 行就逼自己写一行注释说明函数干了什么。写不出来或者要写两行才能说清楚那它逻辑上很可能不是一个动作。2.3 风格统一把“看不顺眼”从评审里拿掉团队里经常有这种吵架有人喜欢双引号有人喜欢单引号有人缩进两格有人缩进四格。争这个没有意义因为它是纯偏好问题。解决办法很简单上一个自动格式化的工具比如 Python 的 Black、JavaScript 的 Prettier、Go 的 gofmt让工具来做决定然后众人闭嘴。格式化工具的好处不只是漂亮。它最大的价值是把 diff 变小代码评审时只看到业务改动不会因为某个人顺手把别的文件格式改了一遍导致一堆无关变动混进来。这个是很多新人没意识到的点一个几百行的格式化 diff足以让评审同事失去耐心。我在团队里的要求是提交前必须跑一次格式化本地配置好保存自动格式化最好在提交钩子里也挂上。宁可让工具花半秒钟整理一下也不要让同事在评审页面上满屏找改动点。3. 注释与文档别让名字出现在“返工写注释”名单上3.1 注释是给“半年后的同事”写的不是给编译器写的很多人对注释的态度是两个极端要么一点不写要么写满整屏。这两种都不可取。注释的读者是人而且多半是对这段代码缺少上下文的后来人。你要解决的是“他为什么看不懂”而不是“把代码翻译成中文”。最近那个段子说“公司要求前程序员回公司写注释”听起来好笑背后是血泪项目上线后没人知道那段正则表达式匹配的是什么也没人敢动那几百行无人区代码。与其等被资源遣返不如在写的时候顺手留两句。一个补丁的注释成本可能只有一分钟但能避免后来人几天的大冒险。3.2 什么位置值得写注释根据我的经验下面几类是性价比最高的注释位置。第一类是业务规则复杂的计算。比如订单满减、库存扣减、优惠叠加这种逻辑靠看代码推理太慢注释直接写明规则来源和边界情况能救很多人命。第二类是特殊决策点。为什么用消息队列而不是直接调接口为什么这里要 sleep 三秒再重试这类“为什么”是代码本身回答不了的。第三类是一个函数的入口文档。对外被多处调用的函数至少要写清楚参数、返回值和抛出异常。举个例子一个查询订单费用的函数如果有这么一段 docstring接手的人会非常舒服def calc_shop_fee(order: Order, use_coupon: bool True) - Decimal: 计算订单实付金额。 规则 1. 平台券和店铺券互斥取优惠力度更大的那张 2. 若订单命中满减活动满减金额在券后计算 3. 结果保留两位小数调用方不要自行四舍五入。 Raises: OrderCalcError: 订单金额状态非法或优惠金额超出订单金额。 这种注释没有一句废话也没有复述代码它补充的是上下文和约束。同事拿到手就知道该怎么调用、该注意什么。3.3 注释不是越多越好也有垃圾注释我见过不少代码注释全是这种# 设置用户姓名 user.name name这就是典型的“替代编译器阅读”式注释没有任何新信息。垃圾注释的危害在于它会占据注意力真正有价值的注释会被淹没在里面。所以最重要的不是写多少而是挑对位置。凡是“为什么”值得写凡是“是什么”代码自己会说话。另外要养成习惯改逻辑的时候同步改注释否则注释反而是误导。有一次我看到一行注释说“暂存用户地址”实际上代码已经改成从配置中心读取地址这种过期注释比没有注释还坑人。后来我就规定注释跟着需求走需求变了注释必须一起更新。4. 能少吵十次架的工程链格式化、静态检查与代码补全4.1 格式化工具让“风格问题”永远退出评审前面在风格统一里已经说了格式化工具的重要性这里再往深讲一层。真正的工程团队不是靠纪律或者约定维持统一风格而是靠自动化的钩子。比如提交前执行pre-commit钩子跑一遍格式化不通过就禁止提交。这样一来任何人的代码进入仓库时都是同一个风格你在评审里再也不用看到“这行怎么多了一个空格”这种评论。我个人配置过一个前端项目用了 Prettier 加 ESLint。效果非常明显以前 Reviewer 经常因为引号、分号、缩进问题打口水战配置之后这类讨论彻底消失。团队时间被大量解放出来讨论逻辑和设计而不是在标点符号上。4.2 静态检查把低级错误挡在同事看到之前除了格式化静态检查工具是第二道保险。现在各语言都有比较成熟的方案Python 有 Ruff、Flake8、pylintJavaScript 有 ESLintJava 有 Checkstyle/SpotBugsC/C 有 cppcheck。它们的价值不单是检查风格还会发现一些潜在的 bug比如变量未定义、类型问题、明显的死代码、逻辑冲突。我每次提交前都会先跑一遍本地静态检查所有报错都清零再发 Merge Request。这么做的好处是CI 不会因为低级问题挂掉同事也不会在评审时揪着“这里有个拼写错误”不放。整体来看静态检查是防线上移越早发现问题成本越低。4.3 代码补全时代人的判断更值钱最近两年 AI 代码补全工具用得挺多大家误以为“代码写得好”这件事被工具接管了。其实相反工具越强人的判断越重要。我见过有人让 AI 生成了一大段看起来很全但根本不符合团队规范的代码反而让评审同事花更多时间去纠偏。用 AI 补全的正确姿势是把它当成“快一点的联想输入”而不是“需求翻译机”。先生成命名良好的函数骨架由人来确认边界和业务规则再让补全去填充局部实现。关键的业务逻辑、权限校验、支付计算我一般不会让 AI 直接写完整因为一旦出错责任还在人身上。同事真正欣赏的是那种能分清楚“工具能帮什么、人需要负责什么”的工程师。5. 让协作记录也“闪闪发光”提交信息、分支管理与 Code Review 话术5.1 提交信息是把改动讲给未来的同事很多人觉得git commit -m update已经够了但等你想在 git log 里找一段历史时会非常痛苦。好的提交信息应当像日记能让人不用看代码就知道这次改了什么以及为什么改。一个我常用的模板是feat(order): 增加店铺券与平台券互斥逻辑 店铺券和平台券不能叠加使用取优惠金额最大的一张。 补充了 order_calc_test 中的边界用例本地全量测试通过。第一行是类型加简述类型用feat表示新功能fix表示修 bugdocs表示文档refactor表示重构。主体部分解释“为什么”和“怎么验证”。别小看这两行字几个月后排查问题git log 里能直接看到这段描述会省下大量考古时间。5.2 分支策略小步提交别让 diff 变成灾难另一个容易让同事“叹气”的场景是一个 Merge Request 里塞了几百个文件、几十个提交里面还混着依赖升级、配置文件改动和业务代码。这种 MR 别说评审了连 diff 都看不下去。我的经验是一个 MR 只解决一个需求或者一个 bug。每次提交尽量小一点比如“增加一个接口字段”“修复一个空指针”而不是攒了一周的工作一次性上来。这样既能减少冲突也方便回溯。如果代码仓库是 Gitee 或者 GitHub还要注意.gitignore把node_modules、dist、.env、密钥文件这些排除掉不要把本地垃圾和敏感信息传到仓库里。曾经有人把数据库密码提交进 Git 历史后面清记录非常麻烦这个坑希望大家都不要再踩。5.3 Code Review用提问代替指责效果翻倍代码好不好最终要过评审这一关。很多开发者在评审时习惯直接说“这样不对”但对方听起来就像在被人审视。我比较推荐的说法是“这块逻辑我有点绕能不能解释一下为什么用这个方案”“这里要不要抽个函数看起来更直观”用提问的方式帮对方自己意识到问题接受度会高很多。反过来如果你是被评审的那一个也要明白评审不是找茬。有人质疑你的写法先别急着反驳把上下文和取舍讲清楚。如果对方建议合理就大方接受如果不合理拿出业务规则说明白。一个成熟工程师的标志就是能把技术争论停留在代码里不上升到人身。这样几个版本之后大家就知道你的代码值得细看也愿意来帮你 review这本身就是一种“祝贺”。6. 实战中的典型翻车现场和排查技巧6.1 排查问题先复现、再改码别让同事替你探险我处理过一个线上问题用户的请求偶尔超时后来发现 Nginx 的 worker 进程 CPU 突然跑满。当时第一反应不是改代码而是用top找到 CPU 高的进程再strace -p pid看它在忙什么最后查 access log 发现某个接口参数异常导致缓存穿透回到业务代码里定位到了一个死循环的遍历逻辑。这个过程完全可以写进提交说明和评审记录让同事知道排查思路是什么。真正的“好代码”不只是静态的可读还包括动态的可诊断。你留下日志、链路追踪 ID、错误码同事遇到线上问题就能顺着线索查下去不至于全组人一起抓瞎。6.2 重构先补测试再动剪刀碰过乱代码谁都有“推倒重来”的冲动。我的经验是重构一定不能裸奔。先把当前行为用测试固定下来再开始拆分和重命名。哪怕是个小函数也先写两三个用例把它钉住再动手。重构完成后跑测试绿了才算安全。重构还有个技巧一次只做一类动作。要么只改命名要么只拆函数要么只调整目录。把它们揉在一起出问题很难定位。小步骤前进每步都有测试护航这种节奏看起来很慢但实际交付速度反而快因为你不需要反复救火。6.3 常见问题速查表问题典型表现排查思路预防方法命名模糊变量名叫 a、b、tmp、flag全局搜索临时变量名数量评审时把命名列为必查项注释缺失业务逻辑只能靠猜让新同事读一遍代码计时复杂规则写 why注释废话注释和代码逐行重复注释改为只保留有增量信息的部分写注释前问一句“代码自己能说明吗”大面积改动MR diff 几百个文件审查改动范围是否单一小步提交MR 聚焦提交信息空洞git log 全是 update查看 git log 的阅读成本按 type summary 写静态检查不过CI 红灯在格式或类型上报错本地先跑 lint 和 test提交钩子强制检查风格争执评审争论引号缩进配置统一工具后禁止偏好评判格式化工具自动处理6.4 想被同事“祝贺”从今晚的提交开始这东西不需要等一个大会战。手头随便找个函数把三个变量名改清楚拆一个五十行的大块头补上两行关键注释提交信息写完整明天同事打开你的 MR 就会觉得变舒服了。我见过最让人刮目相看的工程师不是突然写了个炫酷模块而是连续三个月每份提交都保持这种水准口碑慢慢就出来了。代码写得清楚不一定会被当众表扬但一定会在每个关键节点帮你积累信任。等哪天邻组同事带着问题来敲你的工位你就知道那种感觉确实跟“上门祝贺”差不多。
返回列表