找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

5043

积分

0

好友

685

主题
发表于 2 小时前 | 查看: 5| 回复: 0

代码审查中指导与困惑的对比

看完这篇文章,你会发现自己过去很多 Code Review 可能都在浪费时间。

开篇:一个扎心的场景

你花两天精心实现了一个功能,提交 PR 后满心期待审核。结果等来 20 条评论:

  • “这个变量名建议改成 userData
  • “这里用三元运算符更简洁”
  • “建议把这段提取成独立函数”

逐条改完再提交,代码顺利上线。然后生产环境炸了——因为并发场景下的 race condition 没人发现。

这就是大部分团队 Code Review 的现状:把精力耗在不重要的细枝末节上,真正致命的问题反而视而不见。

作为一个前端老兵,我见过太多类似的情形。今天想聊聊高级开发者到底怎么做 Code Review,以及为什么他们的方法和你想的截然不同。

第一个颠覆认知:先问“该不该存在”,别管“写得好不好”

大部分人的审查顺序是错的

打开一个 PR,99% 的人第一反应是看实现细节:

  • 变量命名规范吗?
  • 函数拆分合理吗?
  • 有没有遵循最佳实践?

但高级开发者的第一个问题是:这段代码应该存在吗?

我见过一个经典案例。某同学提交了一个完美的虚拟滚动实现,处理了边界情况,写了完整测试,代码优雅得让人想点赞:

// PR标题: 为表格添加虚拟滚动优化性能
function VirtualizedTable({ data, rowHeight = 50 }) {
  const [scrollTop, setScrollTop] = useState(0);
  const containerHeight = 600;

  // 计算可见区域
  const visibleStart = Math.floor(scrollTop / rowHeight);
  const visibleEnd = Math.ceil((scrollTop + containerHeight) / rowHeight);
  const visibleData = data.slice(visibleStart, visibleEnd);

  return (
    <div
      style={{ height: containerHeight, overflow: 'auto' }}
      onScroll={e => setScrollTop(e.target.scrollTop)}
    >
      <div style={{ height: data.length * rowHeight }}>
        <div style={{ transform: `translateY(${visibleStart * rowHeight}px)` }}>
          {visibleData.map(row => <TableRow key={row.id} data={row} />)}
        </div>
      </div>
    </div>
  );
}

代码挑不出毛病。但问题在于:为什么要一次性加载 10000 条数据?后端 API 明明支持分页,为什么不用?

虚拟滚动解决的是“如何高效渲染大量 DOM”,可真正的问题是“为什么要在前端渲染这么多数据”。好比家里着火,你却去买个更好的灭火器,而不是找出起火原因。

真正的 Code Review 应该质疑解决方案本身,而不是解决方案的实现质量。

这需要跳出代码看问题:

  • 理解业务背景和技术架构
  • 思考有没有更简单的方案
  • 判断这个 PR 是在解决症状还是病根

这种思维方式,才是高级开发者和普通开发者的分水岭。

第二个颠覆认知:懂得什么不重要,比懂得什么重要更重要

注意力是有限资源,别浪费在无关紧要的地方

我有个可能引起争议的观点:大部分代码细节根本不重要。

  • 变量叫 userData 还是 user?无所谓
  • if-else 还是三元运算符?无所谓
  • 10 个元素用 .map() 还是 for 循环?也无所谓

看到这里可能有人要跳起来:“代码质量很重要!细节决定成败!”

我没说代码质量不重要,我是说:注意力是稀缺资源,应该花在刀刃上。

建筑学里有个概念很有启发——承重墙和非承重墙。承重墙支撑整个结构,拆了房子就塌;非承重墙只是分隔空间,改了无伤大雅。

代码也一样。有些决策是“承重”的:

  • 影响性能的算法选择
  • 关系到安全的权限设计
  • 决定可维护性的架构方案

这些值得死磕。但更多的是“非承重”的偏好:

// 这三种写法值得你留 10 条评论吗?

// 写法1
const isValid = data.name && data.email;

// 写法2  
const isValid = Boolean(data.name && data.email);

// 写法3
const hasName = !!data.name;
const hasEmail = !!data.email;
const isValid = hasName && hasEmail;

写法 3 可读性可能好一点点。但值得让作者改代码、推新 commit、延迟功能上线吗?

高级开发者知道什么时候该闭嘴。 不是妥协代码质量,而是清楚:在无关紧要的问题上消耗团队精力,就没时间关注真正重要的事了。

第三个核心技能:预见什么会炸

线上事故都是可以预防的,如果你知道该看哪里

经历过几次生产事故后,你就会形成一种直觉——知道哪些代码模式容易出问题。

最容易被忽视的就是错误处理。 不是“有没有 try-catch”,而是“出错了会怎样”?

async function getUserProfile(userId) {
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();
  return data;
}

这代码开发环境跑得好好的。但上线后:

  • API 返回 500 怎么办?
  • 响应是 HTML 错误页而不是 JSON 怎么办?
  • 网络超时了怎么办?
  • 用户看到的是什么?白屏?报错?还是无限 loading?

初级 Review 可能会说:“加个 try-catch 吧。”

高级 Review 会问:“用户体验应该是什么?显示错误提示?重试?用缓存数据?哪种失败模式对用户最友好?”

这才是关键差异——不是检查代码在理想情况下能不能跑,而是思考在最糟糕的情况下用户会遇到什么

所有系统都会失败。API 会挂,网络会断,数据库会超时,第三方服务会返回垃圾数据,用户会做你意想不到的操作。真正的代码审查,是在代码进入生产前,就预见到这些失败场景。

第四个维度:关注 PR 里没有的东西

最隐蔽的 bug 往往藏在“没写的代码”里

优秀的 Code Review 不只看写了什么,更要问:没写什么?

function useUserData(userId) {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchUser(userId).then(setData);
  }, [userId]);

  return data;
}

这代码看起来没问题。但高级开发者会问:

“如果 userId 变化了,但上一个请求还没返回呢?组件会先设置旧数据,再被新数据覆盖吗?”

这就是经典的 race condition。用户快速切换 tab,页面显示错乱,没人知道为什么。

修复其实很简单,但前提是你要想到去找这个问题:

function useUserData(userId) {
  const [data, setData] = useState(null);

  useEffect(() => {
    let cancelled = false;

    fetchUser(userId).then(user => {
      if (!cancelled) setData(user);
    });

    return () => { cancelled = true };
  }, [userId]);

  return data;
}

这类问题需要你在脑子里“运行”代码:

  • 组件卸载时会发生什么?
  • 函数同时执行多次会怎样?
  • 异步操作完成前用户跳转了呢?

线性阅读代码是不够的,你需要在多个维度上模拟执行。 这是经验积累的结果,也是高级开发者最值钱的技能之一。

第五个判断标准:知道什么时候该 Block

不是所有问题都值得阻止合并

这可能是 Code Review 里最微妙的技能:判断什么问题必须现在解决,什么可以后续优化。

必须 Block 的:

  • ✋ 会导致数据丢失的问题
  • ✋ 有安全风险的代码
  • ✋ 影响用户体验的性能问题
  • ✋ 会造成内存泄漏的逻辑
  • ✋ 有 race condition 的并发处理
  • ✋ 未来难以修改的架构决策

可以放行的:

  • ✅ 可读性改进
  • ✅ 不常执行路径的小性能问题
  • ✅ 非核心功能的边界测试
  • ✅ 代码风格偏好

对比这两段代码:

// 这个必须 Block - 会留下一堆脏数据
async function deleteUser(userId) {
  await db.users.delete(userId);
  // 等等,用户的帖子、评论、关系怎么办?
  // 删不干净会导致数据一致性问题
}

// 这个可以放行 - 只是风格问题
function getUserDisplayName(user) {
  return user.firstName + ' ' + user.lastName;
  // 可以用模板字符串,但这样也能用
}

第一个会在生产环境造成数据混乱,第二个只是写法偏好。

判断标准很简单:这会导致生产事故吗?会让代码库显著难维护吗? 如果答案是否,建议改进但别阻止合并。

第六个沟通技巧:解释“为什么”,而不只是“怎么做”

好的 Review 是在教育,而不只是审查

对比这两条 Review 评论:

弱评论:“这里应该用 useMemo

强评论:“这个计算每次渲染都会跑,处理 1000+ 项目。用 useMemo 包裹,依赖项设为 [items],可以避免其他状态变化时重复计算。我本地 profiling 显示渲染时间从 80ms 降到 5ms。”

差异在哪?

第二条评论不只说了做什么,还解释了:

  • 问题是什么:每次渲染都重复计算
  • 为什么重要:处理大量数据,影响性能
  • 怎么解决:用 useMemo 缓存结果
  • 效果如何:实际性能提升数据

当你解释“为什么”时,你不是在给指令,而是在传授思维方式。 开发者学到的不是规则,而是原则。下次遇到类似场景,他们就能自己判断该怎么做。

这在反对某种方案时尤其重要:

❌ “这个方法不对”
✅ “这个方法在下个 sprint 加入多用户功能时会出问题,因为它假设只有一个活跃用户。我们需要重构成支持多用户的结构,现在改比上线后改容易得多。”

第二种方式提供了上下文,让开发者理解更大的图景。这样的反馈引导理解,而不只是强制服从。

第七个长远视角:为未来的维护者写代码

今天写的代码,明天就会变成别人的噩梦

代码的生命周期远比你想象的长。今天你花 2 小时写的功能,可能半年后需要改动。那时候:

  • 你可能已经忘了细节
  • 可能是新同事在改
  • 可能是凌晨 3 点线上出 bug 你在查

高级开发者审查代码时,想的是:“这段代码能不能脱离作者独立存在?”

// 缺少上下文 - 半年后没人知道 0.88 是什么
function calculatePrice(item) {
  return item.basePrice * 0.88;
}

// 有清晰上下文 - 未来维护者能理解
function calculatePrice(item) {
  // 扣除 12% 平台手续费 (0.88 = 1 - 0.12)
  // 详见定价文档: https://docs.company.com/pricing
  return item.basePrice * 0.88;
}

魔法数字 0.88 让人一头雾水。注释解释了它的含义和出处,未来的开发者不用到处找文档或问人。

同样重要的是识别“技术上正确但让人困惑”的代码:

// 能跑,但看着累
const isValid = !!data && !!data.name && data.name.length > 0;

// 同样逻辑,意图清晰
const hasValidName = data?.name && data.name.length > 0;

第二种写法更容易理解。有人快速浏览代码时,不用解析布尔逻辑就能明白在检查什么。

可维护性不是奢侈品,而是必需品。 每次 Review 都是在为未来投资,减少技术债务,让代码库保持健康。

第八个判断力:知道什么时候该当面聊

有些问题靠评论解决不了

当一个 PR 方向根本就错了,你写再多评论也没用:

  • 架构选择有根本问题
  • 对需求理解有偏差
  • 技术方案从起点就跑偏

这时候别写长篇大论的评论,直接拉个会聊。

“嘿,我觉得咱们应该聊一下这个 PR。我对整体方案有些担忧,实时讨论会比来回留言快。”

当面讨论的好处:

  • 作者避免走弯路浪费时间
  • 审查者不用打字打到手抽筋
  • 双方能快速达成共识
  • 可以即时澄清误解

Code Review 评论适合处理具体的、局部的修改建议。对于需要重新思考整体方案的情况,它就是错误的沟通方式。

知道什么时候切换沟通渠道,这是高级开发者的关键软技能。

第九个平衡术:信任与教导之间的微妙关系

Code Review 最难的不是技术,是人

做 Code Review 最难的部分不是技术判断,而是社交平衡

你需要:

  • 信任作者经过了思考,有他们的理由
  • 怀疑他们可能没看到某些坑
  • 教导他们不知道的模式和风险
  • 尊重他们的决策,即使和你的不同

这种平衡很难把握。太严格会打击积极性,太宽松会放过问题。

一个技巧是从好奇开始,而不是纠正

❌ “你应该用方案 Y 而不是 X”
✅ “我好奇为什么选择方案 X 而不是 Y?我之前遇到过 Y 在这种场景下更合适的情况”

第二种说法:

  • 假设作者有理由(也许他们试过 Y 不行)
  • 表达了你的经验,但不强加观点
  • 开启了对话而不是命令
  • 也给你机会学习(也许他们知道你不知道的事)

同时,该教的时候别藏着掖着。初级开发者不知道自己不知道什么。你发现的模式和坑,如果不解释清楚,他们下次还会犯。

这种社交技能——在信任、教导和共情之间找平衡——才是区分高级审查者和一般审查者的关键。

最后的底线:完美不是目标,出货才是

Code Review 不是追求完美,而是在保证质量的前提下让代码尽快上线

每条评论都有成本:

  • 作者要花时间修改
  • 功能上线被延迟
  • 可能产生团队摩擦

所以问题不是“什么地方可以更好?”——因为代码永远可以更好。

真正的问题是:“什么必须在上线前改变?”

这个列表要短得多:

  • ✋ 会造成生产事故的问题
  • ✋ 会让代码库难以维护的决策
  • ✋ 有明显安全隐患的实现

其他的:

  • ✅ 作为下次的改进建议
  • ✅ 写在文档里给未来参考
  • ✅ 作为个人偏好就别提了

最好的 Code Review 是那些发现了微妙的 race condition 或架构问题,避免了几周痛苦的 Review。 不是那些纠结变量命名和格式的 Review。

知道区别。审查真正重要的东西。

写在最后

如果你读到这里,我希望你能重新思考 Code Review 的目的。

它不是展示你知道多少规则的舞台,不是刷存在感的机会,更不是追求完美代码的工具。

Code Review 的真正价值在于:在代码进入生产前,用经验和判断力筛选出真正重要的问题。

下次做 Review 时,问问自己:

  • 这个问题会导致生产事故吗?
  • 这个建议会显著提升代码质量吗?
  • 这条评论值得占用团队的时间吗?

如果答案是否,就让它过。把精力留给真正重要的战斗。

因为注意力是有限的,leverage 才是无限的。




上一篇:Go Delve 调试实战:Go 1.26 条件断点精准定位偶发 Bug 指南
下一篇:换IDE容易,换终端难?2026年8款命令行工具横向对比与场景化推荐
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-5-7 22:09 , Processed in 0.980920 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表