
1. 从“能跑就行”到“敢让人看”工程师成长的第一个分水岭刚入行那几年我特别迷恋一种状态代码能跑起来功能能演示领导看了点头我就觉得这事儿成了。直到有一次我把自己写的一个数据处理脚本交给同事去维护他打开文件后沉默了半分钟问了我一句“你这个变量名data1、data2、temp到底哪个是原始数据哪个是清洗后的”那一刻我才意识到工程师和“写代码的人”之间隔着一道叫“可维护性”的墙。“我的工程师之路”这个标题看起来很大但落到每一天的工作里其实就是一连串具体的选择变量怎么命名、函数怎么拆分、日志怎么打、异常怎么处理、文档写不写、测试做不做。这些选择单独看都不起眼但累积起来就决定了一个人是三年经验重复用了十年还是真正在三年里长出了别人五年的功力。这篇文章我想聊的不是什么高深架构而是那些没人会专门教你、但每个工程师迟早都要自己补上的基本功。如果你刚入行一两年正处在“能干活但心里没底”的阶段或者你带过新人、发现他们总在同样的地方卡住那下面的内容应该能帮你省下不少试错时间。我会从代码组织、调试思维、工具链、协作习惯、持续学习这几个维度把“工程师之路”拆成可以落地的动作而不是空泛的鸡汤。2. 代码组织让三个月后的自己还能看懂2.1 命名不是小事它是你思维的投影我见过太多项目里充斥着a、b、tmp、flag、list1这样的命名。写的时候觉得省事读的时候就是灾难。变量名和函数名是你留给未来读者的路标如果路标全是“往前走”那跟没有路标有什么区别。一个实用的判断标准如果你把变量名遮住只看它的赋值和使用方式能不能猜出它是什么比如const d new Date()和const createdAt new Date()后者一眼就知道是创建时间。再比如function handle(data)和function validateUserInput(input)后者直接告诉你这个函数负责校验用户输入。我自己的习惯是命名时多花五秒钟想一个准确的词比后面花五分钟跟同事解释要划算得多。具体做法上我遵循几条规则布尔值用is、has、can开头数组用复数名词函数名用动词开头避免用data、info、manager这种万能词除非实在找不到更具体的。这些规则不新鲜但真正坚持下来的人不多。2.2 函数拆分的“一屏原则”与“单一职责”关于函数该多长网上有各种说法有人说不超过 50 行有人说不超过 20 行。我的经验是一个函数最好能在一屏内看完不需要滚动。这不是硬性指标而是一个信号——如果你写一个函数写到需要来回滚动才能理解它的逻辑那它大概率承担了太多事情。更本质的原则是“单一职责”。我刚开始写代码时喜欢在一个函数里把“读取数据、清洗数据、计算指标、写回数据库”全干完觉得这样调用方便。后来发现只要其中任何一步需要调整整个函数都要重新测试而且很难复用其中的某一段逻辑。拆成readData()、cleanData()、computeMetrics()、saveResults()之后每个函数只做一件事测试和复用都变得简单。这里有个常见的误区为了拆分而拆分。我见过有人把一个简单的三行逻辑硬拆成三个函数结果调用链变得很长读代码要跳来跳去。拆分的目的是降低理解成本如果拆分后反而更难懂了那就不要拆。判断标准很简单拆出来的函数你能不能给它起一个准确的名字如果只能叫processStep1、processStep2那说明拆得不对。2.3 注释写“为什么”而不是“做什么”很多教程教人写注释会说“给每行代码加注释”。我强烈反对这种做法。i // i 加一这种注释除了占地方没有任何价值。真正有价值的注释是解释“为什么这样做”尤其是那些看起来有点奇怪、但其实是刻意为之的地方。举个例子你写了一个setTimeout(fn, 0)如果只写// 延迟 0 毫秒执行读者会困惑为什么要这么做。但如果你写// 用 setTimeout 0 把回调推到下一个事件循环避免阻塞当前渲染读者就明白了。再比如你写了一个看起来多余的边界判断注释说明“上游数据在极端情况下会返回空数组这里提前返回避免后续报错”这就帮读者省去了猜测的时间。我自己的习惯是代码本身能说清楚的不写注释代码说不清楚但必须这么写的一定写注释。另外注释要跟着代码一起更新过时的注释比没有注释更危险因为它会误导人。3. 调试思维从“瞎试”到“有章法地缩小范围”3.1 先复现再定位最后修复我见过不少新人遇到 bug 的第一反应是“改一行试试看”改完发现没好再改另一行。这种“瞎试”的方式运气好可能碰对但大多数时候是在浪费时间而且可能引入新的问题。正确的调试顺序是稳定复现 → 缩小范围 → 定位根因 → 修复验证。第一步“稳定复现”经常被忽略但它极其重要。如果一个 bug 时有时无你连它什么时候出现都不知道那修复就无从谈起。我通常会先记录下复现步骤什么输入、什么操作、什么环境、什么时间点然后尝试用最小化的方式重复触发它。第二步“缩小范围”是核心。比如一个接口返回了错误数据你可以先确认是前端展示问题还是后端返回问题——打开浏览器开发者工具看网络请求如果返回的数据本身就是错的那问题在后端如果返回是对的但页面显示不对那问题在前端。再往后端细分是数据库查询错了还是业务逻辑处理错了还是序列化错了每缩小一次范围你就离根因近一步。3.2 日志不是越多越好而是要“有信息量”打日志是调试的基本功但我见过两种极端一种是不打日志出了问题只能靠猜另一种是日志满天飞关键信息被淹没在噪音里。好的日志应该让你在出问题时能快速还原当时的上下文。我通常会在几个关键位置打日志函数入口记录输入参数函数出口记录返回结果异常捕获处记录错误堆栈和上下文变量关键分支记录走了哪条路径。日志内容要包含足够的标识信息比如请求 ID、用户 ID、订单号这样你才能把一次请求的多个日志串联起来。另外日志级别要合理使用。DEBUG用于开发阶段的详细信息INFO用于正常的业务流程节点WARN用于可恢复的异常情况ERROR用于需要立即关注的错误。我见过有人把所有日志都打成INFO结果线上出问题时真正的错误信息被淹没在大量正常日志里找起来非常痛苦。3.3 二分法排查快速定位问题引入点当你不确定问题是哪个版本引入的或者哪段代码导致的二分法是非常高效的策略。比如你发现某个功能在最新版本坏了但不确定是哪个提交引入的可以用git bisect在提交历史中二分查找通常几次就能定位到问题提交。代码层面也一样。如果一个大函数出了问题你可以在中间某个位置加一个临时日志或断言看执行到那里时数据是否正常。如果正常说明问题在后半段如果不正常说明问题在前半段。然后继续对半切分直到找到具体出问题的那几行。这种方法比从头到尾逐行检查要快得多尤其是在处理几百行的复杂逻辑时。4. 工具链把重复劳动交给机器4.1 版本控制不只是“备份代码”很多人把 Git 当成一个“上传代码的地方”只在写完一个功能后提交一次提交信息写个“update”就完事。这其实浪费了版本控制最大的价值——它记录了你思考的过程。我自己的习惯是小步提交每次提交只做一件事。比如“修复用户登录时手机号校验失败的问题”和“调整登录页面按钮样式”应该是两个提交而不是混在一起。这样后面如果要回滚某个改动或者查找某行代码的修改原因都会清晰很多。提交信息我通常写三部分做了什么、为什么做、影响范围。比如“修复登录校验手机号正则漏掉了 17 号段影响所有 17 开头用户登录”。这样的提交信息半年后回看依然能快速理解。分支策略上小团队用简单的main feature就够了。每个功能或修复开一个分支完成后合并回主干。合并前先拉取最新主干代码解决冲突后再提交避免把冲突带到主干上。4.2 自动化测试写一次省一百次“写测试太浪费时间了”——这是我听过最多的借口。但实际情况是不写测试才会浪费更多时间。每次改完代码手动点一遍所有功能改一个 bug 要回归测试十个地方这种重复劳动才是真正的浪费。测试不需要一开始就追求高覆盖率。我的建议是先给核心业务逻辑写测试。比如订单金额计算、权限判断、数据清洗规则这些逻辑一旦出错影响很大而且经常需要调整有测试保护会安心很多。测试用例不用多每个分支覆盖到就行。比如一个折扣计算函数正常折扣、无折扣、折扣超过上限、折扣为负数这几种情况各写一个用例基本就够了。写测试还有一个隐藏好处它会逼你把代码写得更可测。可测的代码通常意味着函数职责单一、依赖清晰、副作用可控这本身就是好代码的特征。我见过不少人在写测试的过程中主动把原来耦合在一起的逻辑拆开了因为不拆就没法单独测试。4.3 编辑器与命令行打造顺手的“工作台”工欲善其事必先利其器。我不推荐花太多时间折腾配置但基本的效率工具还是要会用。编辑器方面熟练使用快捷键、多光标编辑、全局搜索替换、代码跳转这些操作每天能帮你省下大量时间。命令行方面至少掌握grep、find、awk、sed这几个文本处理工具处理日志和批量文件时非常有用。我自己的经验是遇到重复操作超过三次就想想能不能自动化。比如每次新建项目都要手动创建一堆目录和配置文件那就写个脚本一键生成每次部署都要手动执行五条命令那就写个部署脚本。这些脚本本身不复杂但长期积累下来能省下大量时间而且减少人为操作失误。5. 协作习惯代码是写给同事看的5.1 代码评审既当作者也当读者代码评审是工程师协作中最重要的环节之一但很多人把它当成走过场。我见过两种极端一种是评审时只说“LGTM”Looks Good To Me什么意见都不提另一种是揪着格式问题不放对真正的逻辑问题视而不见。好的评审应该关注三件事正确性、可维护性、边界情况。正确性是指代码逻辑是否实现了预期功能有没有明显的 bug可维护性是指命名是否清晰、结构是否合理、有没有难以理解的“魔法数字”边界情况是指空值、极值、并发、异常路径有没有考虑到。作为作者提交评审前先自己过一遍把明显的格式问题、调试代码、无用注释清理掉别让评审者把精力浪费在这些上面。作为评审者提意见时尽量具体不要只说“这里不好”而是说“这里如果输入为空数组会报错建议加一个判空”。另外评审意见要区分“必须改”和“建议改”别把个人偏好当成硬性要求。5.2 文档写给人看不是写给搜索引擎看文档的重要性不用多说但很多人不知道该怎么写。我的经验是文档要回答“为什么”和“怎么用”而不是“有什么”。比如一个模块的文档不要只列出它有哪些函数而要说明这个模块解决什么问题、什么场景下应该用它、调用时需要注意什么。README 文件是项目的门面至少要包含项目是做什么的、怎么安装、怎么运行、怎么测试、目录结构说明。如果是内部项目还要加上负责人、相关链接、常见问题。这些内容看起来简单但真正写全的项目不多。我见过不少项目新人接手后要花一整天才能把环境跑起来就是因为 README 里缺了关键步骤。另外文档要跟着代码更新。我自己的做法是修改代码时如果发现相关文档已经过时顺手更新掉。不要想着“等以后有空再统一整理”因为那个“以后”通常不会来。5.3 沟通把技术问题翻译成业务语言工程师经常需要和非技术同事沟通比如产品经理、运营、客户。这时候能把技术问题翻译成业务语言是一项非常值钱的能力。比如“数据库索引失效导致全表扫描”这种话产品经理听不懂但如果你说“这个查询没走索引数据量大了之后会越来越慢影响用户打开页面的速度”他就能理解为什么要花时间优化。反过来当产品经理提出一个需求时你也要能判断它的技术影响。比如“这个功能能不能明天上线”你要能快速评估涉及哪些模块、需要改多少代码、有没有技术风险、测试需要多久。这种评估能力是随着经验积累慢慢培养起来的但前提是你平时就关注自己负责的模块在整个系统中的位置和依赖关系。6. 持续学习在信息过载中找到自己的节奏6.1 别追热点追问题技术圈每天都有新框架、新工具、新概念如果什么都追很容易陷入焦虑。我的经验是让问题驱动学习而不是让热点驱动学习。工作中遇到性能瓶颈就去学性能分析和优化遇到并发问题就去学并发模型和锁机制遇到部署麻烦就去学容器化和自动化部署。这样学到的知识马上就能用上而且理解得更深。反过来如果只是看了一篇“2024 年最值得学的十个技术”就冲动去学大概率学个皮毛就放下了因为工作中用不到没有实践机会。我并不是说不要了解新技术而是说优先级要清楚先解决手头的问题再拓展视野。6.2 建立自己的知识库学到的东西如果不整理很快就会忘。我自己的做法是每解决一个有价值的问题就写一篇简短的笔记。笔记不用很长关键是记录清楚问题是什么、怎么排查的、根因是什么、怎么修复的、以后怎么避免。这些笔记积累下来就是自己的“错题本”下次遇到类似问题翻一下就能找到思路。笔记工具用什么不重要重要的是坚持记录和方便检索。我用过纯文本、Markdown 文件、在线笔记软件最后发现最简单的 Markdown 文件加全局搜索就够用了。关键不是工具多高级而是你愿不愿意花那几分钟把经验沉淀下来。6.3 定期复盘从“做了”到“学到了”很多人工作很忙但成长很慢原因就是只做事不复盘。我建议每完成一个项目或一个阶段花半小时问自己几个问题这个项目里我做得好的地方是什么做得不好的地方是什么如果重来一次我会怎么做有没有什么经验可以复用到下一个项目这些问题不需要写成长篇大论几句话就行。但就是这几句话能帮你把零散的经验提炼成可复用的方法。我自己的习惯是每个季度做一次复盘看看这三个月里哪些能力提升了、哪些问题反复出现、下一个季度重点补什么。这种定期回顾比漫无目的地“努力”要有效得多。7. 一些没人明说但很重要的软技能7.1 学会说“我不知道”工程师容易有一种“必须什么都懂”的包袱遇到不懂的问题硬撑结果浪费了更多时间。承认“我不知道”并不可怕可怕的是不懂装懂导致项目出问题。我自己的做法是遇到不确定的问题先快速查资料或做小实验如果十分钟内没有头绪就果断问同事或搜索更专业的资料。问问题不丢人耽误项目进度才丢人。当然问问题也有技巧。不要直接问“这个怎么做”而是说“我遇到了什么问题已经尝试了哪些方法现在卡在哪里你能给我一些方向吗”。这样对方能快速理解你的处境给出有针对性的建议而不是从头给你讲一遍基础知识。7.2 管理预期别做“老好人”工程师经常面临多方需求产品要加功能、运营要改配置、客户要修 bug。如果每个需求都答应“马上做”最后要么加班到崩溃要么交付质量下降。学会管理预期是保护自己也是保护项目。我的做法是接到需求先评估工作量和优先级然后明确告诉对方“这个需求大概需要多久我手头还有哪些事情你希望我优先做哪个”。把选择权交给对方而不是自己默默扛下所有。如果确实做不完提前说比到期再说要好得多。提前说对方还有时间调整计划到期再说对方就被动了。7.3 保持对代码的敬畏心最后这一点听起来有点虚但我觉得很重要。代码一旦上线就会影响真实的人——可能是用户的登录、可能是商家的订单、可能是医生的诊断参考。写代码时多想一想“如果这里出错了会有什么后果”很多低级错误就能避免。我自己的习惯是提交代码前问自己三个问题——这段代码在异常情况下会怎样边界条件处理了吗如果线上出问题我能快速定位吗这三个问题花不了几分钟但能拦住不少潜在事故。工程师之路很长技术会更新工具会迭代但这种对代码负责的态度是走到哪里都适用的底子。