新来的同事翻代码仓库时,指着合并请求列表问我:“CR 过了为什么还要 SR?CE 和 PE 到底哪个先部署?”我愣了一下——这些问题单独拎出来谁都懂,但凑在一起确实容易绕晕。AR、BR、CR、SR 和 CE、PE、P 这八个缩写,在研发交付链路里其实来自三套不同的标记体系:有的代表“质量门禁”,有的代表“发布形态”,还有的代表“部署环境”。把它们混在一起背定义,越背越乱;但如果还原到一条真实的交付链路里,每个缩写的作用和顺序就非常清晰。这篇文章就按这个思路整理一次,给团队做术语表和新人培训时也能直接用。
1. 这组缩写到底在说什么:交付链路里的三套标记体系
先给结论:AR、BR、CR、SR 和 CE、PE、P 并不在同一个坐标轴上。前四个偏向“流程动作”,后三个偏向“环境位置”。如果把一次功能上线比作一次搬家,前四个是搬家前的检查步骤(物品清点、邻居确认、路线审查、安全排查),后三个是搬家过程中途经的房间(临时仓库、演练房间、最终客厅)。步骤和环境混在一起说,自然容易绕。
我按团队里最常用的口径,整理了一张总表。这里必须说明:大写缩写在不同公司和不同资料里可能指代不同全称,我在第五章会专门讲歧义问题,本章先按“研发交付链路中最常见、最容易形成共识”的用法来列:
| 缩写 | 优先解读 | 所属维度 | 一句话作用 |
|---|---|---|---|
| AR | Author Review,作者自审 | 质量门禁 | 提交代码前,作者自己先按清单过一遍 |
| BR | Before Review,预评审 | 质量门禁 | 正式评审之前,由资深同事快速把关方向 |
| CR | Code Review,代码评审 | 质量门禁 | 同行逐行审查代码,确认设计、逻辑与规范 |
| SR | Security Review,安全评审 | 质量门禁 | 上线前最后确认无高危漏洞、无敏感信息泄露 |
| CE | Common Environment,公共/联调环境 | 部署环境 | 多个模块共同联调,数据允许脏、允许重置 |
| PE | Performance Environment,性能测试环境 | 部署环境 | 模拟生产流量,验证容量、并发与稳定性 |
| P | Production,生产环境 | 部署环境 | 承载真实用户流量的最终环境 |
这个组合不是唯一的正确答案,但它能完整覆盖标题里所有缩写的核心用途。后面每一章的展开,都围绕一个核心问题:这套标记在什么阶段出现、卡的是哪道关、出了问题谁负责。
在继续之前,我把关键结论说透:AR/BR/CR/SR 是“人做动作”的节点,而 CE/PE/P 是“代码待着”的位置。人和位置一旦搞混,就会出现“在 CE 上做了 SR”这种没有意义的描述——安全评审的对象是一段准备发布的代码,而不是某个环境本身。后面的交付链路图会再验证这一点。
2. AR、BR、CR、SR:从提交到上线的质量门禁顺序
这四个缩写拼在一起,是一条从作者到上线的审查链。注意它们不是四个并列的选项,而是有先后顺序的关卡:先自己查(AR)→ 再提前把关(BR)→ 再同行审(CR)→ 最后安全查(SR)。每一关都有不同的责任人和不同的关注点。
2.1 AR:作者自审,提交之前最后的自我救赎
AR 全称 Author Review,指开发者在把代码推送到远程分支、发起合并请求之前,自己先做一轮系统性的检查。很多团队没有把 AR 当成一个正式流程,只把它当“提交前扫一眼”。实际上,AR 才是成本最低、效率最高的质量关卡——因为此时代码只有你自己碰过,发现问题只需要本地改,不需要打扰任何人。
我在团队里落地 AR 的方式,是维护一份提交前自检清单,不需要额外工具,直接放在仓库根目录的CONTRIBUTING.md里:
- 本地构建和单测是否通过?
- 是否顺手提交了临时代码、调试打印、本地配置文件?
- 是否包含敏感信息(密码、Token、内网地址、个人手机号)?
- 是否更新了关联的接口文档、部署说明?
- 代码格式是否符合团队约定(lint 是否通过)?
- 是否为这次改动补充了必要的单测或回归用例?
这份清单看起来琐碎,但能拦住大概三成无意义的 CR 往返。我印象最深的一次:某个同事提交了一大段逻辑,他自认为“完全没问题”,结果第一行就有一个复制粘贴留下的调试断点。如果 AR 阶段认真跑一遍自检清单,这种问题根本不会出现在 CR 里。
2.2 BR:预评审,在大动干戈之前先探路
BR 有两种常见全称:Before Review 和 Branch Review。无论哪种,本质都是在完整同行评审前,先用最小的成本确认方向没有跑偏。
什么场景需要 BR?主要两类:
- 超大功能或重构:改动面涉及十几个文件、跨三四个模块。这时候直接发 CR,评审人会看得头大,讨论也会发散。更合理的做法是先整理一份改动方案或分支说明,由技术 lead 或资深同事快速过一遍,确认模块划分、接口设计、兼容性思路都成立,再进入逐行 CR。
- 高风险区域:涉及支付、鉴权、数据迁移、公共基础库的改动。哪怕是几十行的小改动,也值得 BR 先看一眼。因为这类改动的风险不在代码行数,而在影响面。
BR 的产物一般不是“代码评论列表”,而是一段清晰的描述:这次准备改什么、为什么这么改、预计影响哪些模块、有没有备选方案。有的团队把 BR 简化为合并请求描述区的固定模板,我倒觉得更有价值的做法是拉一次语音评审——二十分钟聊完方向,比写一堆文字再等评论高效得多。
2.3 CR:代码评审,交付链路的“主裁判”
CR 是 Code Review,也是这套缩写里大家最熟悉的一个。CR 的核心不是找 bug,而是用第二双眼睛验证三件事:
- 设计合理性:这个方案是不是过度设计?有没有更简单的实现路径?
- 一致性:代码是否与现有模块的风格、约定、数据流保持一致?
- 可维护性:半年后的维护者看到这段代码,能不能看懂?有没有注释说明“为什么”?
很多团队把 CR 做成“形式主义”——合并请求一发,自动分配给两个人,一个人点了“Approve”,另一个人看了一眼就通过。我见过最极端的案例:一个 800 行的合并请求,从发起到合入只花了二十分钟,评论数零。这种 CR 不仅没价值,还会给团队一种“代码已经被审过了”的虚假安全感。
比较健康的 CR 节奏是:小步提交、即时评审。一次合并请求尽量控制在一个功能点、几百行以内;提交后四小时内完成第一轮评审;评审人至少要留一条实质性的技术评论或问题讨论,而不是只点按钮。如果改动确实很大,就强行拆成多个小合并请求,让每一段都能被真正读懂。
2.4 SR:安全评审,上线前最后一道刹车
SR 常见全称是 Security Review,指的是在发布到生产环境之前,对这次版本做一次集中的安全确认。它在时序上排在 CR 之后,常常是发布流水线里的一个前置卡点。
不同体量的团队执行方式差别很大:
- 小团队:SR 可以由后端负责人兼任,重点检查:
- 是否引入了有已知漏洞的依赖?
- 是否有越权访问、未做鉴权的接口?
- 日志里是否可能打印敏感信息(手机号、身份证号、Token)?
- 数据库/缓存的连接配置是否都是环境变量,没有硬编码密钥?
- 中大型团队:SR 由专职安全工程师执行,往往结合自动化扫描工具,对合并请求的代码 diff、依赖清单、镜像扫描结果做统一评估。
需要注意,有些团队里 SR 也被解读为 Service Release,即“服务发布”动作本身。这个歧义我会在第五章展开。但在我推荐的交付链路模型里,SR 放在环境部署之前、CR 之后,作为质量门禁体系里的最后一个动作,逻辑上更顺——因为“发布”不是一个评审动作,而是一个执行动作,它更适合出现在流水线阶段,而不是缩写清单里。
这里有个很实用的经验:给 SR 设置单独的合并请求标签或流水线检查项,而不是让安全评审“口头通过”。我之前所在的团队给合并请求配置了“安全评审”标签,代码没打标签就不允许合入发布分支。一开始大家觉得麻烦,后来有人排查线上故障,靠标签记录快速定位到“某版本临时跳过了安全评审”,问题立刻一目了然。
2.5 把四个门禁串成一条链
综合来看,AR、BR、CR、SR 的完整顺序和责任人如下:
| 阶段 | 全称 | 责任人 | 卡点动作 | 典型失败信号 |
|---|---|---|---|---|
| AR | Author Review | 开发者本人 | 提交前跑自检、本地构建、自查清单 | 提交代码带调试日志、编译不过 |
| BR | Before Review | 技术 lead 或资深同事 | 确认方案方向、模块边界、风险评估 | 评审时发现整体设计跑偏 |
| CR | Code Review | 两名以上同事 | 逐行审代码、讨论设计、留评论 | 无实质评论、合入速度异常快 |
| SR | Security Review | 安全负责人或后端负责人 | 检查依赖、敏感信息、越权风险 | 上线后怀疑数据泄露、密钥暴露 |
这四个阶段环环相扣,每一环的目的都是把问题挡在更早的阶段。AR 挡住低级错误,BR 挡住方向错误,CR 挡住实现错误,SR 挡住安全风险。层层递减,互不替代——不能说“CR 都过了,SR 不用查了”,因为 CR 的评审人关注的是代码逻辑,不一定具备安全审计的视角。
3. CE、PE、P:环境维度的职责划分与部署边界
看完了流程,再看“代码待着的地方”。CE、PE、P 是三个环境标记,但它俩不像 AR/BR/CR/SR 那样线性递进,而是并行的、服务于不同目的的部署目标。一个版本从构建完成到最终上线,通常会按顺序经历这些环境,但它们承载的任务完全不同。
3.1 CE:公共联调环境,大家共享的“公共沙盘”
CE 是 Common Environment 的缩写,最直接的翻译是“公共环境”,在大多数团队里它同时承担“联调环境”的角色。它是多个服务、多个前端、多个客户端模块共同部署和调试的地方。
CE 环境的核心特征是:所有参与方都往这里部署,数据可以反复造脏、反复重置。
由于是多团队共用,CE 容易出现两类问题:
- 互踩:A 团队部署了新版本,B 团队还在调试旧接口,两边一起协作时就可能发现接口对不上。解决思路是给 CE 增加“部署广播”机制——每次有新版本部署到 CE,自动往协作群里推一条消息,写明“xxx 服务 v1.2.0 已部署,接口变化见文档 xxx”。
- 数据污染:某个团队在 CE 里跑了破坏性的测试数据,导致其他团队联调时拿到一堆脏数据。常见的对策是每天凌晨定时重建 CE 数据库,保证工作日开始时环境是干净的。
CE 环境的安全边界要放宽,一般允许访问 Debug 接口、允许查看详细日志、允许动态修改配置。因为它的目的就是暴露问题,而不是隐藏问题。但要注意:CE 环境往往直连某些开发用中间件,不要把生产环境的账号信息带入 CE 配置。
3.2 PE:性能测试环境,贴着生产环境做“压力预演”
PE 是 Performance Environment 或 Pre-production Environment 的缩写(又是一个双全称,详见第五章歧义表)。在本文的模型里,我按性能测试环境展开,因为它在一个发布链路中的职责非常具体:在接近生产的软硬件条件下,验证版本在预期流量下的表现。
PE 环境和 CE 最大的区别在于数据规模和隔离策略:
- CE 的数据是手工造的、小规模的;
- PE 的数据是生产数据脱敏后的副本,或者按生产流量比例生成的合成数据,量级接近生产。
这样做是为了让容量测试、压测结果有参考价值。如果 PE 里只有几百条数据,测出来的 QPS 和响应时间拿到生产上毫无意义。
在工程实践上,PE 环境还要注意几点:
- 独立部署,不与其他团队混用。PE 一旦被多个团队同时压测,测试结果互相干扰,谁的数据都不准。
- 流量开关要可控。通常 PE 环境不会接入真实流量,而是通过压测工具模拟;如果想做灰度验证,需要单独的入口和权限控制。
- 监控和日志必须齐全。PE 的价值就是“提前暴露问题”,如果监控缺失,等于白压测一场。
我之前在某个压测项目里吃过亏:PE 环境复用了 CE 的数据库实例,只是换了一个库名。结果压测到一半,数据库 IO 被打爆,直接拖垮了正在联调的 CE 服务。后来规范成“PE 必须使用独立的数据库实例或至少独立的物理机/容器资源”,才彻底解决环境间相互影响的问题。
3.3 P:生产环境,唯一的“正式舞台”
P 就是 Production,生产环境。它不需要太多解释——这是承载真实用户请求、直连真实数据的最终环境。所有前面的门禁和环境验证,最终都是为了安全地到达这里。
生产环境的运维边界,应该是最严格的:
| 项目 | CE | PE | P |
|---|---|---|---|
| 用途 | 多团队联调 | 压测/预发布验证 | 承载真实流量 |
| 数据 | 可脏、可重置 | 脱敏副本、规模接近生产 | 真实数据 |
| 部署权限 | 开发+QA | 开发+QA+运维 | 仅运维/发布系统 |
| 变更要求 | 随意、频繁 | 按发布节奏 | 走变更流程、可回滚 |
| 监控要求 | 基础日志 | 完整指标+链路追踪 | 全链路监控+告警 |
| 访问控制 | 项目组内 | 项目组+压测专项 | 严格权限、操作留痕 |
这里我想特别强调一点:生产环境的价值不在于“不能出错”,而在于“出错之后能快速恢复”。所以部署 P 的时候,永远要把回滚方案、版本标记、灰度策略一起准备好。很多团队把 P 看成终点,实际上 P 每次上线都是一次新的开始。
3.4 环境选择的一句话决策法
理解了 CE、PE、P 各自的职责,做环境决策时就很简单:
- 要验证多个模块能不能配合工作?选CE。
- 要验证这个版本扛不扛得住真实流量?选PE。
- 要交付给真实用户使用?选P。
不需要背复杂表格,想清楚“当前这个测试动作想回答什么问题”,环境就自然选对了。
4. 把两套体系拼成一张可落地的交付分支图
很多概念文写到上一章就停了,但实际工程里最缺的不是“名词解释”,而是一套能把 AR/BR/CR/SR 和 CE/PE/P 串起来的可执行流程。这一章我用一个模拟项目 X 来演示完整的交付链路,这套流程在我们团队已经跑了一年多,非常稳定。
4.1 从一个提交到生产环境,完整走一遍
假设模拟项目 X 负责的是一个用户中心服务,包含接口、数据库脚本、前端页面三部分。一位开发者完成了“用户资料页增加电子签名功能”的开发,下面是完整流转路径:
- AR 阶段:开发者在本地跑通单测,按自检清单确认没有调试代码、没有密钥硬编码,然后推送分支,创建合并请求,合并请求描述里写明改动背景、测试方法、影响范围。
- BR 阶段:技术 lead 看到合并请求描述后,发现这次改动涉及数据库字段变更,于是先做一次 BR——确认新增的字段有默认值兼容旧数据,排序规则和现有列表一致,没有破坏已有接口的返回结构。确认可行后回复“方向 OK,可以走 CR”。
- CR 阶段:两个评审人先后 review 代码,其中一个人发现电子签名图片的 Base64 上传方式在大图场景下可能导致接口超时,建议改成上传到对象存储再传 URL。开发者按建议修改,重新提交,再次 review 通过。
- SR 阶段:安全负责人检查依赖和代码 diff,确认没有引入有已知漏洞的库,图片上传逻辑做了文件类型校验,没有路径穿越风险。标记“安全评审通过”。
- 构建与部署到 CE:合入开发主干后,流水线自动构建,并部署到 CE 环境。前端同事、测试同事在 CE 上完成功能联调和回归测试。
- 验证 PE:版本通过 CE 的功能验证后,手动触发 PE 部署。运维在 PE 上跑一轮接口压测——模拟 500 并发、持续十分钟,观察平均响应时间和错误率,确认没有内存泄漏和慢 SQL。
- 发布到 P(生产环境):所有门禁变绿后,版本进入发布窗口。先在生产环境的小流量分组上灰度,观察核心指标 30 分钟,再逐步放开至全量。
这条链路里,AR/BR/CR/SR 全部发生在“代码合入主干之前”;而 CE/PE/P 发生在“代码合入主干之后”。前者决定“代码能不能合”,后者决定“版本能不能上”。两套缩写天然分属两个阶段,这也解释了为什么单独把它们拆开背定义容易混乱。
4.2 用流水线配置把门禁固化成代码
只写在文档里的流程是纸面流程,真正可落地的是写在流水线配置里的硬性卡点。下面是一段简化版的流水线脚本示意(以常见 GitLab CI 风格为例),关键不在于语法,而在于每个 stage 与 AR/BR/CR/SR 的对应关系:
stages: - verify # 对应 AR:提交前自检的自动化部分 - review # 对应 BR + CR:合并请求上下文中的评审动作 - security # 对应 SR:安全扫描 - deploy-ce # 部署到 CE - deploy-pe # 部署到 PE - deploy-prod # 部署到 P verify-job: stage: verify script: - echo "运行单测与静态检查" - make test - make lint security-job: stage: security only: - master script: - echo "依赖漏洞扫描与敏感信息检查" - security-scanner scan . - secret-detect --path . deploy-ce-job: stage: deploy-ce environment: name: CE script: - echo "部署到公共联调环境" deploy-pe-job: stage: deploy-pe environment: name: PE when: manual # PE 部署需要手动确认 script: - echo "部署到性能测试环境" deploy-prod-job: stage: deploy-prod environment: name: P when: manual # 生产部署必须手动触发 script: - echo "生产发布,执行灰度策略与回滚准备"流水线的 stage 名称和顺序,本质上就是在把 AR/BR/CR/SR、CE/PE/P 这些概念“翻译”成机器可执行的步骤。新员工看一遍流水线定义,比读十页规范文档都直观。
有一个很实用的补充:把 BR 设计成一个“非阻塞但必须完成”的步骤。可以用计划任务自动提醒:合并请求创建 24 小时内没有 BR 评论,就自动在群里触发提醒。有些团队把 BR 变成阻塞卡点,反而拖慢了正常的小改动节奏。我的建议是 BR 只针对“大改动或高风险改动”,通过标签或文件变更规模自动识别是否需要 BR,而不是对所有合并请求一刀切。
4.3 一次真实的卡点案例:CR、SR 都过了,部署却失败了
光讲正向流程不够,我再分享一次反向排查经历。某次模拟项目 X 准备发布“订单列表批量导出”功能,CR 和 SR 都通过了,CE 联调也正常,结果在 PE 压测阶段失败——接口在 200 并发时就出现大量超时。
排查过程很有意思:
- 先看代码,没有明显的慢查询;
- 再看 PE 环境配置,发现 PE 环境连的数据库是 CE 的只读副本,而代码里用了事务锁;
- 只读副本没有开启行级锁支持,导出逻辑一执行就阻塞等待;
- 最终定位到是环境配置漂移——PE 环境的数据库连接串写成了 CE 的地址。
这个问题的根因不在代码,而在“环境标记和实际部署内容不一致”。我们的流水线虽然给每个 job 都设置了environment.name,但数据库连接串是通过环境变量注入的,有维护人员手动改漏了。从那以后,我们在流水线里加了一步环境自检:部署完成后,自动读取当前环境的配置指纹,与预期值比对,不一致就直接失败并告警。
这个案例说明:概念整理得再清楚,如果在工程上没有把“环境”和“配置”绑定起来,缩写就只是纸面上的记号。每一层环境除了名字,还应该有独立的配置来源和校验机制。
5. 最容易踩的坑:同名缩写、跨团队口径与文档沉淀
概念整理到最后,必须正视一件事:这三个缩写并不是全世界统一的,实际工程里到处是一词多义。不把这个讲清楚,读者拿着这套解释去跟其他团队对需求,可能立刻就对不上。
5.1 CR 既是 Code Review,又是 Candidate Release
我在第 2 章把 CR 解释为 Code Review,但在很多发布管理场景里,CR 是 Candidate Release,意思是“候选发布版本”。“候选”又通常是发布流水线里的一个版本命名:RC(Release Candidate)大家更熟悉,但有些团队简写为 CR。
区分方法很简单:看语境里强调的是“人审代码”还是“版本产物”。如果句子是“CR 需要两个人 approve”,那一定是 Code Review;如果是“当前最新版本是 v1.3.0-CR2”,那一定是 Candidate Release。
PE 也是重灾区:它可以指 Performance Environment(性能环境),也可以指 Production Environment(生产环境)。后面这种用法常见于缩写习惯把 Production 写成 Prod 或 PE 的团队。同一篇文章里作者前两段说“PE 不用接真实流量”,后一段又说“PE 挂了影响所有用户”,那你就能判断出他说的不是同一个环境——真到这种时候不要硬猜,直接问对方“你这个 PE 是压测环境还是生产环境”。我吃过不少这种亏,后来在跨团队开会时养成了习惯:但凡听到一个缩写有歧义,立刻用一句话确认全称。
5.2 团队内部如何定规范:术语表是第一份技术债
解决歧义最有效的办法不是靠默契,而是把术语表沉淀到仓库里。我们团队内部的做法是:
- 在
docs/glossary.md里维护一份“术语与缩写表”,每条记录包含缩写、全称、在本团队的唯一语义、废弃需要替换的旧叫法。 - 所有合并请求、会话评论、工单描述里禁止出现裸缩写,第一次出现必须带上全称,例如“CR(Code Review)”。
- 每次跨团队对接前,主持人先把术语表发到会议文档,约法三章:“本文档里 CR 都指 Code Review,PE 都指 Performance Environment,如有歧义,以文档为准。”
这个方法看起来有点笨,实际上能省掉大量沟通成本。有一次新来的实习生把 CR 理解成“Candidate Release”,在发布文档里写了“v1.4.0-CR 待发布”,被流水线前的安全检查拦下来,大家对着看板争论了十分钟才搞清楚。之后按规范走了几十个版本,再也没有因为缩写歧义阻塞过流程。
5.3 概念整理的最终目的:不是背单词,而是理顺决策
如果把这套缩写当成英语单词来背,过两周一定忘。真正有用的整理方式,是把每个缩写还原成一次决策:
- AR 是一次“我准备好了吗”的自查决策;
- BR 是一次“方向对不对”的方向决策;
- CR 是一次“代码好不好维护”的质量决策;
- SR 是一次“上线安不安全”的风险决策;
- CE 是一次“大家一起验功能”的协作决策;
- PE 是一次“扛不扛得住压力”的容量决策;
- P 是一次“可以交给真实用户了”的发布决策。
每次决策都有明确的责任人和明确的产出物。理解了这层关系,你不需要死记任何一个缩写——只要站在某个节点问自己“此刻我需要做什么决策”,对应的缩写自然浮出来。
到了这里,这组缩写背后的完整脉络已经打通:先靠四个门禁把代码打磨合格,再按三个环境逐步验证、最终推送生产。每个做研发交付的人,都应该在自己的项目里画一张类似的分支图,把流程固化下来,而不是靠记忆和口头约定。