周一上午九点,林晨坐在工位上,盯着 Github 页面上那个刚刚创建的 pull Request(代码合并请求),心跳有些加速。这是他在新公司的第一个 pR。
过去一周,他几乎每天加班到晚上十点,周末也泡在代码里。任务不算复杂:为一个 deFi(去中心化金融)项目的前端添加一个质押收益计算器组件。他用了自己最熟悉的 React 方式完成了开发,自测了几遍,功能都正常。
鼠标在“create pull request”按钮上悬停了十几秒,林晨深吸一口气,点了下去。页面跳转,pR 编号 #347 出现在屏幕上。他顺手在 Slack 的技术频道里 @ 了 tech Lead 陈峰:“陈哥,质押计算器组件的 pR 已提,麻烦有空 review 一下”。
消息刚发出去,陈峰几乎秒回:“收到,上午处理”。
林晨松了口气,起身去接水。路过陈峰的工位时,看到对方已经点开了 pR 页面,眉头微皱,手指在键盘上快速敲击。回到座位,林晨强迫自己不去刷新页面,转而开始看项目里另一个模块的代码。但注意力总是不集中,每隔几分钟就忍不住瞥一眼屏幕右下角的 Slack 图标。
这种等待代码审查的感觉,比他预想的要焦虑。在上一家跨境电商公司,他的代码审查通常很顺利——十年老员工,对业务系统和代码库了如指掌,提交的 pR 往往只有些格式小调整。但在这里,一切都是新的:新技术栈、新编码规范、新的最佳实践,甚至新的思维方式。
上午十点半, Slack 图标终于闪动起来。
陈峰:“@林晨 看完你的 pR 了,问题比较多,我们快速过一下?现在方便的话来小会议室”。
林晨心里一沉,回复:“好的,马上来”。
拿起笔记本和笔,他走向走廊尽头的小玻璃会议室。推门进去时,陈峰已经坐在里面,面前的 macbook 屏幕上正是那个 pR 的页面,右侧评论栏里密密麻麻的红色标记。
“坐”。陈峰指了指对面的椅子,语气平静但严肃,“我们先整体说,然后你回去逐条修改”。
林晨坐下,打开笔记本准备记录。
“首先,功能实现没问题,计算逻辑是对的”。陈峰开门见山,“但问题出在代码质量、安全性和可维护性上。我一条条说,你记一下”。
他滚动页面到文件开头:“第一,typeScript 类型定义太松散。你看这里”,他指着屏幕,“userInput: any,这等于没定义类型。我们项目要求严格类型,所有变量、函数参数、返回值都必须明确定义。用 any 在审查中直接会被打回”。
林晨点头,快速记下:“明白,我改成具体类型”。
“第二,智能合约交互部分”。陈峰翻到另一段代码,“你直接调用了合约方法,但没有处理可能的失败情况和 Gas 费估算。这在 web3 前端是必须的——每一次链上交互都可能失败,可能耗光用户 Gas 费。你需要加上 try-catch,加上 loading 状态,加上预估 Gas 并提示用户确认”。
“这个我确实没考虑到……”林晨如实说。在传统 web 开发里, ApI 调用失败通常只是重试或报错,但涉及真金白银的 Gas 费,完全是另一个维度。
“第三,代码结构”。陈峰继续,“你把所有逻辑都写在一个组件里,计算函数、格式化函数、状态管理全混在一起。按照我们项目的规范,计算逻辑应该抽离成独立的纯函数,放在 utils 目录下;格式化函数也应该独立;状态管理考虑用 Zustand 而不是全用 useState,因为这部分状态可能在应用其他地方也需要访问”。
林晨看着自己那八百多行的组件文件,突然意识到问题所在——他用了最快速、最直白的方式实现功能,但没考虑后续维护和扩展。
“第四,安全问题”。陈峰的语气更严肃了些,“你这里用了 eval 吗”?
林晨一愣:“没有啊……”
陈峰指向一段动态计算公式的代码:“虽然不是 eval,但你用 new Function 动态执行用户输入的计算公式,这本质上和 eval 一样危险。如果用户输入恶意代码呢?即使在我们自己控制的前端,这种模式也绝对禁止。计算公式必须白名单化,只允许几种预设模式”。
林晨背后冒出冷汗。他确实为了方便,允许用户输入自定义计算公式,然后动态执行。这在传统 web 也许还行,但在涉及加密资产的环境里,简直是漏洞。
“第五,样式问题”。陈峰翻到最后,“你用了内联样式和大量 !important 覆盖。我们项目用 tailwind cSS,要求原子化样式类。另外,响应式设计考虑不足,在移动端布局会乱”。
他停下来,看向林晨:“暂时就这些主要问题。细节评论我都在代码行里加了,总共……四十七条评论”。
四十七条。林晨感觉脸上有些发烫。工作十年,他从未在一个 pR 里收到过这么多修改意见。
“陈哥,抱歉,是我没熟悉好规范”。他诚恳地说。
陈峰摆摆手:“不用道歉,新人第一个 pR 都这样。 web3 开发,尤其是金融相关,安全性和代码质量的要求比传统 web 高几个数量级。一笔错误交易可能造成用户永久资产损失,一个合约漏洞可能被黑客利用导致项目归零。所以审查严格是必须的”。
他顿了顿,语气缓和了些:“但你的优势也很明显——实现速度快,基础逻辑扎实,没有低级错误。只是需要适应我们的标准和思维方式”。
林晨点头:“我回去一条条改。大概需要多久”?
“给你两天时间”。陈峰说,“改完后重新提交,我会再审查。另外,建议你仔细读一下项目里的 coNtRIbUtING.md 贡献指南,还有 code-review-checklist.md 代码审查清单,里面写了所有要求和常见问题”。
“好的,我马上看”。
回到工位,林晨先点开陈峰说的那两个文档。贡献指南足足有三十多页,从 Git 工作流、提交信息规范、代码风格、测试要求,到安全注意事项、性能优化点,事无巨细。代码审查清单更是列了一百多项检查项。
他泡了杯浓茶,开始逐条处理评论。
第一条,类型定义。他把所有的 any 都替换成具体类型: string、 number、 bigNumber( web3 中处理大数字的库)、自定义接口……光是完善类型,就花了两个小时。
第二条,智能合约交互。他查阅项目里其他类似组件的实现,学习如何处理交易发送、等待确认、错误回退。添加了 Gas 费估算函数,在用户点击前弹出预估费用和确认框。这部分涉及对以太坊交易机制的深入理解,他边学边做,到下午三点才完成。
第三条,代码结构重构。这是最耗时的。他把那个庞大的组件拆解:计算逻辑抽成 calculateYield.ts,格式化函数抽成 formatters.ts,状态管理改用 Zustand 创建独立 store。拆解过程中,他发现了自己原来代码里的几个隐藏 bug ——状态更新时机不对导致渲染闪烁。如果不是重构,这些 bug 可能要到用户使用才会暴露。
第四条,安全问题。他彻底重写了计算公式部分。不再允许用户自由输入,而是提供几个预设计算模式(线性、指数、对数),用户只能选择模式和调整参数。虽然灵活性下降,但绝对安全。
第五条,样式重构。他强迫自己学习 tailwind cSS 的类名体系,把一堆内联样式替换成原子化类。调整响应式断点,确保从手机到桌面都能正常显示。
晚上八点,办公室只剩下寥寥几人。林晨终于处理完最后一条评论,重新运行测试,全部通过。他再次提交 pR ,这次在描述里详细列出了所有修改点,并 @ 陈峰。
几分钟后,陈峰回复:“收到,明天上午审查。”
林晨关掉电脑,揉了揉发酸的眼睛。走出写字楼时,南山科技园的夜空稀疏地亮着几颗星。晚风带着深圳湾的潮湿气息吹来,他深吸一口气,疲惫中却有种奇异的充实感。今天这四十七条评论,像一面镜子,照出了他过去十年在舒适区里形成的思维定式——只求功能实现,忽视安全质量;只顾眼前交付,不想长期维护。而 web3 这个领域,正在用最严格的标准逼迫他打破这些定式。
地铁上,他打开手机,看到苏婉发来的消息:“今天又加班?晚饭在锅里,记得热了吃”。
他回复:“刚下班,在回去路上。今天被陈哥批了代码,改了整整一天”。
苏婉很快回复:“批得狠吗?”
“四十七条修改意见”。
“啊……那确实不少。不过能学到东西就好,别太有压力”。
“嗯,确实学到很多。就是感觉……三十五岁重新当学徒,滋味挺复杂”。
“学徒不可怕,可怕的是连当学徒的机会都没有。你现在至少还有公司愿意花时间培养你,不是吗”?
林晨看着这句话,心里某个地方被触动了。是啊,如果是在原来那家公司,以他十年老员工的资历,谁敢给他的 pR 提四十七条意见?但在那里,他也温水煮青蛙般停止了成长。而现在,虽然被批评、被要求重写、像个新手一样从头学起,但每一天都能感觉到自己在突破旧边界。
回到家已经九点半。苏婉陪乐乐睡了,餐桌上留着饭菜和一张便签:“热两分钟就能吃,别凑合”。
林晨热了饭,坐在餐桌前安静吃完。然后打开自己的笔记本电脑,没有继续看公司代码,而是点开了那个他一直在悄悄开发的智能投资系统。这几天忙于适应新工作,系统的开发进度慢了下来。但此刻,看着自己完全掌控的代码库,他忽然有种奇妙的感觉——在公司的 pR 里,他是被审查者,要遵循别人的规范;但在这个自己的项目里,他是规则的制定者。而这两种角色,或许都需要。
他写了几行代码,又停下来思考。今天陈峰强调的安全性和健壮性,是不是也应该应用到自己的投资系统里?比如更严格的输入验证,更完善的错误处理,更模块化的架构……
手机震动了一下,是陈峰发来的 Slack 消息,在他提交的新 pR 下:“初步看了一下,修改方向都对。明天我会详细审查,今天早点休息”。
林晨回复:“谢谢陈哥,明天见”。
关掉电脑,他走到阳台上。夜色中的深圳,无数写字楼依然灯火通明。这座城市永远不缺加班的人,不缺学习的人,不缺在压力中试图抓住机会的人。
他想起下午修改代码时的一个瞬间——当他终于理解为什么 web3 前端必须预估 Gas 费并提示用户确认时,那种“啊,原来如此”的顿悟感。那种感觉,让他想起二十多岁刚入行时,第一次理解异步编程,第一次搞明白闭包,第一次成功调试一个复杂 bug 时的兴奋。三十五岁,重新找到那种学习新东西的兴奋感。这或许就是这次被迫转型,最珍贵的收获。