ARTICLE DETAIL

资讯详情

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

如何写出让同事主动来问你要的代码:可读性与工程实践指南

如何写出让同事主动来问你要的代码:可读性与工程实践指南 1. 代码为什么会“获赞”先把好看的定义搞清楚我自己刚工作那两年一直有个特别大的误区觉得同事之间互相夸“代码写得好”指的是一个人技术多牛、算法多聪明、用了多少高级特性。后来发现根本不是这么回事。有一次我写了一个本地文件批量重命名的小工具处理公司素材目录里几百个混乱命名的文件。代码其实特别简单就是遍历目录、按规则拼接新文件名、再调一下系统接口做重命名总共一百来行。结果当天下午就有四五个同事跑过来问我要代码还有同事直接把那段函数复制到自己项目里改了改就用上了。那是我第一次意识到“代码写得好”在真实职场里的定义根本不是“炫技”而是别人愿意用、敢用、能看懂。这个定义听起来平平无奇但真正做到的人少之又少。作为一个天天在代码堆里摸爬滚打的程序员我后来总结出一个很朴素的结论能让同事“上门祝贺”的代码通常同时满足三个条件——第一读起来不费力。同事从拿到你的代码到看懂它在干什么不需要反复追问“这逻辑是怎么串起来的”。他们打开文件扫几眼函数名、变量名、注释一配合心里就有数了。第二改起来不害怕。有经验的程序员看到一段好代码第一反应不是“这里能不能再优化”而是“如果产品提了新需求我知道该在哪里动手而且改完不用担心别的地方炸掉”。这种边界感带来的安全感比代码本身的价值更让人安心。第三拆下来就能用。要么是一段可以直接粘贴复制的工具函数要么是一个边界清楚的业务模块。同事拿来就能跑跑完效果符合预期这种“即插即用”的体验是他们在内心深处给你点赞的真正原因。后面我在带团队、做代码评审的时候越来越确定所谓“同事纷纷上门祝贺”本质上不是因为你的代码做了什么惊天动地的事而是因为你把代码写成了别人最想看到的样子。这篇文章我就从几个维度把“让同事愿意主动来问你要代码”的写法一层一层拆开讲清楚。2. 命名与注释决定同事读你代码时的血压2.1 变量命名先把“字典”级别的命名做好很多程序员对命名的理解停留在“不要用a、b、c这种没意义的字母”但真实情况是即使不用a、b、c命名也不一定到位。我见过大量“看似规范、读起来依然难受”的代码典型例子是这样的// 坏味道示例 ListString list1 getData(); ListString list2 new ArrayList(); for (String s : list1) { if (s.length() 5 !s.startsWith(test)) { list2.add(s); } }这段代码没有任何语法问题变量名看起来也不算乱用但同事拿到手必须先读一遍循环体里的逻辑才能猜出list2到底是什么。更好的写法是ListString rawKeywords getRawKeywords(); ListString validKeywords new ArrayList(); for (String keyword : rawKeywords) { if (isValidKeyword(keyword)) { validKeywords.add(keyword); } }区别在哪里区别在于我通过命名先告诉读者“这是原始关键词这是过滤后的有效关键词”再结合一个语义化的小函数isValidKeyword把判断逻辑包起来。同事不需要钻进循环体里逐行推敲扫一眼就能建立整体认知。这里我特别想强调一点命名不只是“起名字”而是给同事铺理解路径。变量名承载的是“这段数据的业务含义”不是“这段数据的技术类型”。同样是存字符串的List在业务里可能是“用户ID列表”也可能是“配置项名称列表”命名不同同事读代码时消耗的心力完全不同。我自己在命名这件事上踩过很多坑后来固定了几个习惯布尔变量用一个动词开头isReady、hasPermission、canRetry不要用flag、status这种含义模糊的词。局部变量允许稍微长一点只要语义清楚比如filteredOrders比orders2强太多。避免把类型塞进变量名stringList、intArr这种命名说明你对这份数据的业务含义还没想清楚。命名粒度跟作用域挂钩临时循环里的索引用i、j完全没问题但一个方法级别的核心变量必须有完整的业务语义。2.2 函数命名的“动词开头”原则函数名是同事理解代码的另一个关键入口。我见过最让人崩溃的命名是那种含糊的handleData()、processInfo()、doThing()这类函数名写了等于没写同事根本不知道函数里发生了什么只能一层层往里点。好的函数命名遵循一个特别朴素的规则动词开头说清楚“做了什么事”。// 不推荐 function handleFile(input) { ... } // 推荐 function parseConfigFile(filePath) { ... } function normalizeFileName(rawName) { ... } function extractKeyFromLine(line) { ... }一个有意思的现象是当你发现自己给函数起名字很费劲大概率不是表达问题而是函数本身的职责没有拆干净。如果一个函数既要做参数校验、又要调外部接口、还要处理返回结果拼装数据你很难用一个动词短语把它的职责说清楚。所以函数命名困难往往是重构的信号而不是词汇量的问题。我之前在评审一个同事的代码时看到他写了一个dealData的函数里面六十多行既处理了Excel解析、又做了数据清洗、还插了数据库。我给他的建议是不要想着怎么给这个函数起个好名字先把它拆成parseExcel、cleanInvalidRows、saveToDatabase三步每一个动作都有明确的对象命名自然就出来了。2.3 注释的正确姿势写“为什么”少写“是什么”关于注释程序员社区里有两派观点吵了很多年一派说代码应该自解释能不加注释就不加另一派说注释是美德多写总比少写好。我的真实感受是两派都不完全对关键在于注释写的是什么内容。如果注释写的是“这段代码在做什么”那大概率是废话因为读代码的人自己看得见// 遍历用户列表把用户名拼接到列表里 for (User user : userList) { nameList.add(user.getName()); }这种注释纯属噪音。真正有价值的注释解释的是“为什么这么做”也就是代码无法直接表达的背景信息。举一个很典型的例子// 注意这里必须在事务提交前发送通知 // 否则监听服务可能在事务回滚时读到脏数据 notificationService.sendOrderCreated(order); transactionManager.commit(order);如果没这条注释后来接手的同事很可能觉得“这个通知放前面是什么奇怪顺序”顺手就调到了commit之后然后线上出了一个偶发性脏读bug排查好几个小时。这种“为什么”级别的注释才是同事看了之后会发自内心感谢你的东西。另外还有一种注释是“警示型”的专门给后来接手的人提示危险区域。比如某个代码分支覆盖了一个特别隐蔽的历史逻辑直接改可能会出问题我会在那边写清楚“这个分支是为了兼容旧版本数据里xx字段为空的场景不能删删了老数据全部读不出来”。这种注释是真正的财富远比教科书里写的那种“代码注释可以提高可读性”要实在得多。3. 模块化与函数边界决定了同事“敢不敢”改你的代码3.1 函数体不要大到让人失去耐心说一个我早期写代码的真实黑历史。有一次我写了一个方法处理订单状态流转里面有七层if嵌套、三个try-catch、两个for循环总行数上百行。当时写完自己跑测试都过了还觉得挺得意。结果两周之后产品说要加一个新的订单状态我打开那个方法盯着自己写的代码看了整整五分钟愣是没敢动手。后来我被迫重构那个方法是这么拆的def update_order_status(order, new_status): _validate_transition(order.status, new_status) _handle_special_status(order, new_status) _persist_status_change(order, new_status) _notify_related_services(order, new_status)拆完之后每个子函数控制在十五行以内各自关注一个独立的小步骤。后来再改状态逻辑我只需要看是哪一小块受影响了改动范围能明确收敛到一个函数里出问题的概率大幅下降。这里我想说一个重要的判断标准一个函数如果超过三十行就需要认真考虑拆分。不是说超过三十行的一定差而是这个长度大概率意味着函数里混入了太多层级不同的逻辑。拆函数的核心思路不是“把大段代码切成小段”而是“把不同抽象层级的东西分开”。比如在业务方法里从数据库查订单、计算折扣、保存订单本身就是三个不同层级的事应该各自独立成函数。真正职责单一的函数同事打开之后很容易建立“输入→处理→输出”的完整心智模型改起来自然有底气。3.2 警惕“隐式耦合”最隐蔽的代码地雷在我做代码评审的经验里隐式耦合是让同事最不敢改代码的头号原因。什么是隐式耦合就是两个看起来毫不相干的代码片段通过某种不明显的隐含约定绑在一起。一个特别常见的场景是“字段字符顺序约定”。比如一段代码里用userName _ userId拼接了一个字符串存到缓存里另一处代码解析时就按这个约定拆分。从代码本身看这两处完全没有依赖关系但一旦某个同事改了拼接规则另一处的解析直接炸掉。而且因为没有显式的依赖关系排查问题的时候不到线上出bug根本没人会意识到这里有关联。针对这种问题我的建议是遇到这种跨模块共享的约定要么封装成独立函数统一管理和调用要么把这种约定写成显式的注释放在两处代码附近同时在代码评审阶段专门问一句“这个约定有没有其他地方在用”。每次评审我都会格外关注这种“看似独立、实则耦合”的代码因为它们才是同事只敢看、不敢改的根源。还有一个很常见的隐式耦合是依赖修改全局状态。比如某个函数不传参直接改了一个模块级别的缓存map表面上看调用方很简洁但实际上这个函数的行为完全依赖于调用之前的程序状态同事想复用它的时候根本不敢动因为他们不确定当前程序状态是否满足前置条件。真正舒服的代码函数依赖的状态越显式越好参数传进去返回值出来中间不偷偷改全局的东西。3.3 功能内聚让代码模块具备更清晰的“业务角色”有一次我做代码评审看到一个工具类里什么都有字符串处理、日期格式化、文件读写、发邮件全放一起类名干脆叫CommonUtil——这基本等于告诉同事“这里就是垃圾桶”。不是不能这样但一旦类里混的东西太多同事想找“日期格式化”的函数得翻完整个文件才能确定有没有。后来我们定了个习惯一个类或者模块只负责一个业务角色。做时间处理就归时间处理类做文件解析就归文件解析类不要在工具类里塞不相关的逻辑。这个改动看起来没什么技术含量但对阅读体验的提升是巨大的。同事拿到一个新项目先看目录结构就知道去哪里找对应功能的代码心里不慌自然愿意用你写的模块。4. 什么代码最让同事“眼前一亮”高光场景拆解4.1 工具型代码解决普适痛点拿来就用前面提到的文件批量重命名就是典型例子。这类代码的共同特点是能解决大量重复劳动且不依赖复杂的业务环境。同事们看到这样的代码会第一时间联想到自己的使用场景贴过来就能跑所以传播得格外快。写工具代码的时候有几个经验值得分享入参设计要灵活但默认值要合理。比如一个批量压缩图片的脚本文件目录可以不传、默认取当前目录这样同事拿来直接用是最省事的。输出信息要友好。脚本跑完打印一下“共处理xx个文件成功xx个失败xx个”比自己默默跑完强太多同事能立刻确认结果。尽量不依赖特殊环境。你用了一个第三方库同事那边没装使用门槛一下子就上去了。能用标准库实现就优先用标准库。4.2 算法中的“克制”还是用快速排序这个例子程序员社区里经常有人讨论算法题快速排序是绕不开的话题。但工作中真正让我觉得厉害的代码不是用了多冷门的算法而是在需要算法的地方写得克制、清晰、正确。比如让我写一个快速排序我绝不会追求一行流或者极致优化我会写这样一版def quick_sort(arr): if len(arr) 1: return arr pivot arr[len(arr) // 2] left [x for x in arr if x pivot] middle [x for x in arr if x pivot] right [x for x in arr if x pivot] return quick_sort(left) middle quick_sort(right)这版代码胜在什么地方胜在所有人都能一眼看明白。同事如果需要在项目里处理排序逻辑看到这种代码完全可以放心地用因为每一个分支的语义都清清楚楚。相比之下那种为了性能写了大量原地交换逻辑的快速排序虽然技术含量更高但肉眼验证正确性要难得多。真实项目里排序的性能瓶颈通常不在算法本身而在于数据怎么来、怎么存、怎么用。我并不是说大家不要学算法而是说算法落在工程代码里时表达清晰、边界明确比炫技更有价值。同事“眼前一亮”更多时候是看到你用一种干净利落的方式解决了一个原本容易被写复杂的问题而不是看到你写了一个复杂到没人敢碰的实现。4.3 修复一个“历史遗留”问题时顺便把周边整理干净还有一种代码特别让同事开心那就是修复历史bug时不只是打一个补丁而是把问题根因找出来连带着把周边逻辑整理清楚。我上个月修过一个FileInputStream相关的bug现象是程序运行时间长了文件句柄泄漏导致无法删除临时文件。如果只打补丁在某个地方加一行close就能解决当下面临的问题但同事后来再遇到类似问题还是不知道怎么排查。所以我额外做了一件事把项目中所有涉及文件读写的代码都过了一遍凡是手工打开流的地方统一改成try-with-resources写法并顺手给每个文件操作函数加上了清晰的注释。这个动作带来的效果是之后再有人看到文件操作相关的代码一眼就知道资源会被自动释放不会踩同样的坑。那种“一个同学修bug全组人受益”的感觉其实靠的就是这种顺手把周边整理干净的习惯。4.4 一些能让大家心情变好的“彩蛋”说点轻松的。程序员也是人代码里偶尔有一些小幽默确实能拉近距离。网上特别火的“爱心代码”就是典型例子——一个用Python或前端技术画动态爱心的代码本身没有什么业务价值但同事发现了会觉得你这个人有意思。举一个很简单用Python打印爱心图形的例子import numpy as np import matplotlib.pyplot as plt t np.linspace(0, 2 * np.pi, 1000) x 16 * np.sin(t) ** 3 y 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t) plt.plot(x, y, colorred) plt.axis(equal) plt.show()这种东西放工作代码里肯定不合适但如果你有一个内部小工具库或者个人维护的项目偶尔加一点这种小元素同事看到就会觉得“这哥们挺有意思”团队氛围瞬间轻松不少。所谓的“上门祝贺”很多时候也是从这种轻松的小互动开始的。5. 从“自测”到“评审”让同事放心接手的一套完整动作5.1 自测不只是跑通而是把“边界情况”写在明面上我自己被同事“祝贺”得最多的一次不是我写的代码功能多复杂而是我交付之前在代码注释和提测说明里把边界情况写得特别清楚。比如我写过一个批量导入的接口正常数据、超长字段、重复记录、空文件、编码混乱的文件这些情况我都会提前测一遍然后把对应行为整理成一份简短说明。同事拿到手心里不但知道“这条路通”还知道“哪些路会撞墙”他们用起来的时候会特别踏实。这个习惯还会反过来影响你的自测质量。当你有意识地整理“边界处理清单”时你会主动去想各种异常情况而不是只盯着正常流程跑一遍就提交。久而久之代码的健壮性会有一个明显的提升。5.2 代码评审中怎么给别人提建议最舒服写代码不只关乎自己还关乎整个团队协作的氛围。代码评审是每个程序员都要面对的场景。我见过很多水平不错的人在评审时直接一句“这里写得太烂了”把同事打击得不行后续配合也变差。我的做法是提问题的时候尽量把“问题”和“建议”绑在一起说。比如看到一段JAVA代码里大量使用List而不是ArrayList我会说“这里用ArrayList的话后续按索引获取元素会更快而且语义上也更明确。”这样同事听了是技术建议而不是对你个人价值的否定。代码评审的氛围一旦变好大家拿到你的代码时心态也会更开放更愿意认真读、认真提意见而不是一上来就挑刺互怼。5.3 commit信息与变更说明让同事一眼看懂改动意图有些程序员提交代码时的commit信息写的是“fix bug”或者“update”这种信息基本等于没写同事看到提交记录时完全不知道这个改动是为了什么。我比较推荐用一句话把“做了什么”以及“为什么”写清楚例如fix: 修复订单导出时金额精度丢失的问题 原因金额字段使用浮点数存储导出时乘法运算导致小数位截断。 处理改用 Decimal 进行金额计算并在导出前统一格式化为两位小数。这种commit信息无论是做代码评审还是将来回头排查问题都能让人快速定位改动背景。尤其当一个项目迭代几个月之后翻看提交记录就像翻看项目的历史日记写清楚了同事的兴趣和信任感都会明显不一样。6. 常见问题与排查技巧实录6.1 总觉得自己的代码“差点意思”但说不清差在哪这是很多初级程序员都有的困惑。我建议你用一个最简单的办法写完代码搁半小时假装自己是第一次看到这份代码的同事从头到尾读一遍。读的过程里凡是出现“这一段是干嘛的”“这个变量为什么存在”“这个函数能不能拆开”的疑问就说明这里有提升空间。如果你自己都读不顺同事就更不用说了。这个“角色扮演”的方法虽然土但比各种代码质量工具都好用因为它强迫你从作者视角切到读者视角。6.2 同事说“看不懂逻辑”怎么排查问题同事说看不懂你的代码大多数情况不是理解能力问题而是你的代码存在“逻辑跳跃”。什么叫逻辑跳跃就是你在写代码时大脑里默认了一些前置知识但读代码的人并不知道。排查方法很简单找一个没看过你这块代码的同事让他读五分钟然后把他的理解讲给你听。你会惊讶地发现他理解得不对的地方几乎都是你在代码里没有说清楚的地方。这比任何代码审查工具都有效。6.3 一个方法写得太长拆了又觉得更乱了怎么办这种问题特别常见。拆分的核心不是“把长函数切成多个短函数”而是先识别出“不同抽象层级”的处理过程。比如一个方法里既有业务规则判断又有数据组装还有外部接口调用那你就应该把这三件事分别抽成三个独立步骤。如果拆完之后发现代码更乱那大概率是你把子函数设计成了“代码片段搬家”而不是真正的职责拆分。每个子函数都应该有一个明确的动词短语能概括它做的事情做不到这一点说明你还没拆到点子上。6.4 格式化工具和Lint规则值得花时间配置我看到太多团队在代码规范上全靠“口口相传”结果每个人的风格都不一样。其实统一的格式化工具和Lint规则能解决很大一部分可读性问题。格式化工具负责解决“缩进、换行、空格”这类表面问题Lint规则负责拦截那些明显的坏味道。比较推荐的做法是在项目里强制接入格式化配置并设置提交前自动检查。这样同事之间看到对方的代码风格是一致的读起来天然就不累。这是投入产出比特别高的一件事。7. 写在后面这是我个人最深的体会做程序员越久我越发觉得“代码写得好”这件事本质上是一种对他人的体贴。你以为同事在祝贺你技术厉害其实他们心里想的是“这段代码我理解起来不费劲改起来不心惊胆战用起来顺手”——这才是“上门祝贺”的真正含义。我见过很多天赋极高的工程师写的代码精妙到让人叹为观止但周围的人都不敢碰。我也见过许多看起来“平平无奇”的同事他们写的每段代码都朴实、清晰、边界清楚所有人都愿意和他合作。如果让我选长期做项目的时候我会更信任后者因为他们的代码能让我睡得安稳。最后分享一个小技巧每次交付代码之前我做完自测后会额外花半小时做一次“同事视角通读”想象自己是第二天接手这个模块的人一边读一边记录那些让自己困惑的地方然后逐一改进。这个习惯可能不会让代码变“惊艳”但它能让同事打开你的代码时心里默默说一句“这代码靠谱。”而当靠谱的印象积累起来你在团队里的口碑就真正立住了。
返回列表