题干与适用场景
给你 30 分钟审查一份陌生的 Pull Request。请说明你会如何理解改动意图、安排检查顺序、区分阻塞问题与非阻塞建议、写出作者能够执行的评论,并最终决定批准、仅评论还是要求修改。
这是一道通用软件工程面试题,适合后端、前端、移动端、基础设施和技术管理岗位。题目可能只要求口述,也可能给出一段 diff 让候选人现场评论。两种形式都考察同一件事:能否在有限时间内找到对用户和系统最重要的问题,而不是尽可能多地挑出格式瑕疵。
下面假设可以看到 PR 描述、关联需求、改动文件和测试结果,但对代码库不熟悉,也不能随时询问作者。若面试官给出不同条件,应先调整风险判断和审查范围。
面试官考察点
第一个信号是能否先重建“改动应该做什么”。没有需求、接口契约或失败边界,评论只能表达个人偏好。强回答会先读 PR 说明和相邻代码,确认行为变化,再进入逐行检查。
第二个信号是优先级。正确性、数据损坏、安全、权限、并发和兼容性问题通常比命名或排版更值得先看。面试官会观察候选人是否能把潜在后果说清楚,并把时间花在风险最高的路径上。
第三个信号是证据。指出“这里可能有问题”只是起点。高质量评论会给出触发条件、可观察后果和验证方法;如果缺少上下文,则写成精确问题,不把猜测伪装成阻塞结论。
最后看审查决定与沟通。候选人要区分必须修复、可选建议、澄清问题和纯粹的 nit,并在总结中说明审查了哪些范围、哪些风险仍未验证、为何批准或要求修改。评论针对代码和影响,不评价作者能力。
回答前需要澄清的问题
- 这是口述流程,还是需要现场审查真实 diff? 前者应给出可复用框架;后者先用几十秒说明框架,再把它应用到具体行,不能只背清单。
- 能看到哪些上下文? 有需求、接口契约和相邻代码时,可以验证行为;只有孤立函数时,应明确假设,把无法确认的契约写成问题。
- 改动是否涉及高风险领域? 支付、鉴权、隐私、数据迁移或公共 API 会提高证据门槛,并可能需要领域专家;内部低风险工具可以更偏向快速改进。
- 面试官希望得到什么输出? 逐行评论、总体总结、是否批准和测试建议是不同交付物。先确认后,才能合理分配 30 分钟。
- 这是常规改动还是紧急修复? 紧急情况可以接受更窄的修复和后续任务,但不能用“紧急”跳过已知的数据损坏或安全风险。
- 你是否拥有所需领域知识? 不熟悉密码学、隐私、数据库迁移等内容时,应审查自己能确认的部分,并明确请求合格审查者,而不是凭信心批准。
30 秒回答框架
“我先确认 PR 的目标、行为变化和失败影响,再做两遍审查。第一遍看改动边界、数据流和高风险路径,优先找正确性、安全、数据、并发与兼容性问题;第二遍检查边界条件、错误处理、测试、可观测性、性能和可维护性。每条评论都写明严重级别、触发条件、后果和期望结果。存在可复现的阻塞问题就要求修改;只有建议和 nit 时可以批准并注明。最后总结已审范围、未验证风险和决定依据。”
分步骤深入解答
先建立基线。阅读 PR 标题、描述、关联需求、接口或数据模型变化,以及现有测试。用一句话复述:“这项改动让谁在什么条件下获得什么新行为,同时必须保持哪些旧契约。”如果这句话写不出来,应先补上下文,逐行评论还没有判断标准。
接着标出改动边界。查看入口、状态变化、外部副作用和返回路径,而不只看高亮行。新增参数会影响哪些调用方?数据库写入和消息发送是否处于同一失败边界?公共响应、事件格式或配置默认值是否改变?这一遍的目标是画出“输入从哪里来、经过什么信任边界、改变什么、怎样失败”。
在 30 分钟的示例时间盒中,可以用 3 分钟理解意图、7 分钟看边界与高风险路径、12 分钟逐段检查、5 分钟核对测试和运行保障、3 分钟整理评论与决定。这是练习用分配,需按 diff 大小和风险调整;它的价值是避免前 20 分钟都花在命名上。
第一遍采用风险优先顺序:
- 行为与正确性: 主路径是否满足契约?空输入、重复请求、部分失败和重试会怎样?
- 安全与数据: 权限检查位于可信边界之前吗?敏感数据是否被暴露?失败能否造成丢失、重复或不可逆状态?
- 并发与兼容性: 两个请求同时执行是否破坏不变量?旧客户端、旧数据和滚动部署期间的混合版本是否仍可工作?
- 架构边界: 改动是否把职责放在正确组件,还是绕开已有约束并复制状态?
第二遍再看更细的实现:控制流和错误传播、资源释放、查询或循环规模、日志与指标、测试是否会在代码出错时真正失败、命名和注释是否帮助未来读者。格式和可自动修复的样式放在最后,避免自动化工具能够发现的 nit 挤掉人工判断时间。
发现问题后先验证它属于本次改动。若新代码让允许为空的 items 直接访问 items[0],这是具体回归;若同一文件原本就有一段无关的复杂逻辑,应在总结中标为既有债务或另开任务,除非它与本次改动组合后造成安全或正确性风险。审查范围不能无限扩张。
评论使用四种意图:
- Blocker: 已有证据表明会违反契约,造成错误结果、安全问题、数据损坏或无法接受的兼容性风险;合并前必须处理。
- Question: 缺少一项会改变结论的上下文。作者回答后,它可能消失,也可能升级为 Blocker。
- Suggestion: 能改善设计、可维护性或运行保障,但当前实现仍达到合并标准。
- Nit: 非阻塞的细小可读性或一致性问题,最好由格式化和静态检查承担。
一条可执行评论包含“级别 + 条件 + 后果 + 期望结果”,必要时再给一种修复方向。例如:
Blocker: 当请求允许items=[]时,这里读取items[0].id会抛错,批量接口返回 500。请在进入循环前处理空数组并补一个回归测试;返回空结果还是 400 由接口契约决定。
如果不知道空数组是否合法,应改写为问题:“接口契约是否允许空数组?当前路径会返回 500;若允许,需要显式处理并测试。”这样既指出证据,也没有伪造需求。
最后作出审查决定。存在未解决 Blocker 时选择“要求修改”;需要上下文但尚不能判断时提交“评论”,不要用批准掩盖未知风险;只有非阻塞建议时可以批准,同时明确建议并非合并条件。总结至少包含:审查范围、关键发现、运行或测试证据、未覆盖领域和最终状态。
绿色 CI 不是审查完成的证明。测试可能漏掉关键分支,静态工具也不知道业务契约;反过来,人工评审也不应替代可执行测试。强回答会把两者连接起来:评论指出可失败条件,并要求能在修复前失败、修复后通过的验证。
高质量示范回答
“我不会一上来逐行找问题。我会先读 PR 描述、关联需求和接口变化,用一句话复述目标与必须保持的旧行为。拿不到上下文时,我会列出假设,不会把猜测写成 blocker。
在 30 分钟里,我会做两遍。第一遍沿入口、状态变化、外部副作用和返回路径走一遍,先看正确性、安全、数据损坏、并发和兼容性。第二遍再检查边界条件、错误处理、性能、日志、测试和可维护性。样式放最后,而且只指出工具没有覆盖、确实影响理解的部分。
每个发现都要能回答三个问题:什么条件触发、后果是什么、怎样验证。我会把必须修复的问题标成 Blocker;信息不足但会改变结论的写成 Question;非阻塞改进写成 Suggestion;纯细节写成 Nit。比如空数组路径会访问第一项,而接口允许空输入,我会说明它导致 500,要求显式处理并增加回归测试,而不会只写‘这里有空指针风险’。
提交审查前,我会再检查评论是否由本次 diff 引入、是否把个人偏好当成标准,以及是否遗漏领域专家。若有可复现的安全、数据或正确性问题,我选择要求修改;只有建议时可以批准。总结里写清已审文件、测试证据、尚未验证的部分和决定理由,让作者知道下一步,也让后续审查者知道我实际覆盖了什么。”
常见错误
- 打开 diff 就逐行挑错 → 没有目标和契约,容易把合法取舍误判为问题 → 先复述意图、行为变化和失败边界。
- 按发现顺序发表评论 → 命名细节可能淹没数据损坏或权限风险 → 先做风险遍历,再处理实现细节。
- 只写“这里可能有 bug” → 作者不知道触发条件,也无法验证修复 → 写明条件、后果、证据和期望结果。
- 所有评论都要求修改 → 作者无法区分合并条件与个人偏好 → 使用 Blocker、Question、Suggestion 和 Nit 表明意图。
- 为了显得全面列出长清单 → 清单没有应用到数据流,也不能证明判断力 → 沿一条关键路径展示实际推导,再说明剩余覆盖。
- 要求顺手修复所有旧问题 → PR 范围不断扩大,风险和验证成本失控 → 区分本次回归与既有债务,安全或正确性耦合问题除外。
- 把绿色 CI 当成批准依据 → 测试可能没有覆盖错误契约 → 检查测试是否会在实现错误时失败,并补最关键反例。
- 评论作者而不是代码 → 产生防御情绪,也没有提供技术依据 → 描述代码、条件和影响,假设对方善意。
- 不懂领域仍直接批准 → 审查状态会制造虚假的安全感 → 标明自己覆盖的部分并请求领域审查者。
追问及应对
追问一:作者不同意你的 Blocker,怎么办?
回到可验证的契约和后果。确认双方是否对输入、风险或发布条件有不同理解;能运行最小复现就用证据解决。仍无法达成一致时,请代码所有者或领域负责人裁决,并把口头结论记录回 PR,不能让分歧无限挂起。
追问二:PR 太大,30 分钟看不完怎么办?
不要假装完成全量审查。先按风险选关键入口、数据变化和公共契约,明确哪些文件已逐行检查、哪些只扫描、哪些未覆盖,并请求拆分或增加领域审查者。批准状态必须与实际覆盖一致。
追问三:发现一个严重的既有问题,但它不在 diff 中呢?
先判断本次改动是否放大或触发它。若会造成当前发布的安全、数据或正确性风险,应阻塞并说明关联;若完全独立,记录证据、创建高优先级后续任务并通知负责人,不把无边界重构塞进当前 PR。
追问四:紧急修复没有完整测试,可以批准吗?
先确认不修复的即时损失、改动是否足够窄、是否有回滚或开关,以及至少能运行哪项针对性验证。可以在明确的紧急流程下接受后续补测,但已知安全、数据损坏或不可回滚风险仍需要更高审批,不因时钟压力自动放行。
追问五:你不熟悉改动涉及的领域,怎样审查?
继续检查通用的控制流、错误处理、测试和接口变化,同时标记未具备资格判断的部分。对密码学、隐私、数据库迁移或复杂并发,请求相应负责人加入。部分审查有价值,前提是没有把它描述成全量批准。
追问六:测试很多,还需要逐行看代码吗?
需要,但关注点不同。测试提供已编码场景的执行证据,评审还要检查需求是否正确、是否漏掉风险、设计是否增加不必要复杂度、日志和兼容性是否合理。反过来,评审发现的关键反例应转成测试,避免只靠未来审查者记住它。