Java 代码质量自检清单:上线前必查的 10 个点
引言
"周三晚上 20:00 大促前的最后一次发布,测试全绿,发布完成后 22:30 用户量一上来,接口全部超时。排查发现是一个新的营销接口调下游风控服务没设超时——风控库抖了一下,接口线程全部挂在那里,Tomcat 200 个线程 10 分钟内耗尽,拖垮了整个应用。更讽刺的是,这个问题在测试环境根本暴露不出来,因为测试环境的下游永远秒回。"
复盘时我们把历次线上事故拉了个清单,发现一个扎心的规律:90% 的上线后事故,根源都不是"不会写",而是"忘了查"——超时没设、日志级别没改、缓存没过期、幂等没做。这些知识团队里人人都懂,但上线前的匆忙时刻,没人按住每一行代码问一遍。
所以这份清单的定位不是教程,是检查表(Checklist):每个点只回答三件事——查什么(检查标准)、怎么查(工具/命令)、中招长什么样(事故症状)。发布评审时照着走一遍,10 分钟能拦住绝大多数"低级但致命"的问题。这份清单也是对本系列前面事故复盘文章的收口:幂等对应《消息重复消费排查》《接口幂等 4 种方案》,SQL 对应《PG 性能调优实战》,缓存对应《Redis 大 Key 阻塞》,内存对应《堆外内存暴增排查》——每一条背后都有一次真实的"血泪"。
先给全景总表,后面逐项展开:
| # | 检查点 | 一句话标准 | 不查的代价 |
|---|---|---|---|
| 1 | 日志级别 | 生产只留 INFO/WARN,DEBUG 关闭,循环体禁打日志 | 磁盘打满、性能损耗、敏感信息外泄 |
| 2 | 敏感信息脱敏 | 手机号/卡号/token 出现在任何出口前必须变形 | 数据合规事故、用户投诉、监管处罚 |
| 3 | 异常兜底 | 全局兜底 + 事务 rollbackFor + 吞异常零容忍 | 半截数据、错误堆栈直透用户 |
| 4 | 线程池参数 | 手动创建、有界队列、命名线程、拒绝策略明确 | OOM、任务静默丢弃、拖垮宿主 |
| 5 | 超时时间 | 每一次远程调用都有 connect+read 超时,且小于上游剩余预算 | 线程耗尽雪崩(本次事故) |
| 6 | 幂等性 | 写接口/消费端有唯一业务键防重 | 重复扣款、重复发货 |
| 7 | SQL 走索引 | 新增 SQL 上线前 EXPLAIN 验证,禁全表扫 | 慢查询拖垮连接池 |
| 8 | 缓存过期 | 所有写入 Redis 的 key 必须有 TTL,防击穿/雪崩 | 大 Key、雪崩、缓存与库不一致 |
| 9 | 内存泄漏 | ThreadLocal remove、流关闭、静态集合受控 | 老年代持续增长,几天后 OOM |
| 10 | 降级预案 | 核心链路有开关可降级,且降级动作演练过 | 故障只能干瞪眼等恢复 |
一、日志级别:磁盘是被循环里的 log.info 打满的
查什么:
<!-- logback-spring.xml 生产基线 -->
<root level="INFO"> <!-- 生产禁用 DEBUG/TRACE -->
<appender-ref ref="ASYNC_FILE"/> <!-- 异步 Appender 防日志阻塞业务线程 -->
</root>
<logger name="com.xxx.thirdparty" level="WARN"/> <!-- 三方 SDK 单独降噪 -->
<logger name="org.hibernate.SQL" level="WARN"/> <!-- 开发期 SQL 日志上线前确认关闭 -->
检查标准:
- 全局级别 INFO;确认没有任何包配了 DEBUG/TRACE(开发期常忘改回)
- 循环体、流式处理、高频回调里禁止日志——20 万次循环每次打一行,10 分钟就是几十 GB
- 日志必须带上下文(traceId、业务单号),但不打整个对象(大对象 toString 可能触发懒加载/巨型 JSON)
- 用异步 Appender(
AsyncAppender/Log4j2AsyncLogger),logback 同步滚动磁盘高峰期会拖慢业务线程
怎么查:grep -rn "log\.\(debug\|info\)" --include="*.java" | grep -i "for\|while\|stream" 人工过一遍循环上下文;上线后盯磁盘增长率(本系列《可观测性》篇的磁盘告警规则)。
中招长什么样:磁盘使用率从 30% 半小时冲到 95%,随后所有写日志的接口 P99 抖动,df -h 看到 log 目录在疯涨。
二、敏感信息脱敏:每一个出口都要过一遍
查什么:数据有四个出口——日志、响应体、存储、三方调用,逐一核对:
- 日志:手机号、身份证、银行卡、token、密码字段绝不原样输出。优先在 toString/日志工具层统一处理,而不是靠人记住
- 响应:用户查询类接口返回前脱敏(138****5678);管理端展示脱敏,导出类走单独权限
- 存储:核心字段(卡号、证件号)落库前加密(AES + 独立密钥管理),不用 MD5 之类的可逆/弱哈希"假装"加密
可抄的工具层写法(一个工具防住日志出口的大头):
public final class MaskUtil {
/** 138****5678 */
public static String phone(String p) {
return p == null || p.length() != 11 ? p : p.substring(0, 3) + "****" + p.substring(7);
}
/** 银行卡:留前4后4 */
public static String bankCard(String c) {
return c == null || c.length() < 8 ? c
: c.substring(0, 4) + "****".repeat(Math.max(0, c.length() - 8)) + c.substring(c.length() - 4);
}
}
// 统一日志工具:任何字段输出前先问一句是否敏感
log.info("订单支付完成 orderNo={}, phone={}", orderNo, MaskUtil.phone(phone));
怎么查:grep -rn "log\.\(info\|warn\|error\)" --include="*.java" | grep -iE "phone|mobile|idCard|bankCard|token|password",命中处逐条确认;配合测试环境抓一次真实日志全量正则扫描(身份证 18 位正则、11 位手机号正则)做兜底。
中招长什么样:安全渗透测试报告里"日志泄露用户手机号",或者更糟——ES 里的用户行为日志被离职员工导出,公关和法务一起上线。
三、异常兜底:三种最典型的错误写法
查什么:
- 全局兜底存在且生效:
@RestControllerAdvice+@ExceptionHandler(Exception.class),任何异常都不能把堆栈直接怼给用户 - 事务方法
@Transactional(rollbackFor = Exception.class)——默认只回滚 RuntimeException,checked 异常会让"半截数据"落库 - 吞异常零容忍:
catch (Exception e) {}或 catch 后只 return null,异常被静默吃掉,问题延迟到不可解释的地方爆发 - 释放资源的正确姿势:
try-with-resources替代手动 close(呼应《连接池泄漏排查》里那个忘记 close 的事故)
前后对比:
// ✗ 事故写法三连
@Transactional // ✗ 默认不回滚 IOException
public void importData(File f) {
try {
parse(f);
} catch (Exception e) {
log.error("解析失败"); // ✗ 吞掉后继续执行,写入了半截数据
}
}
// ✓ 正确形态
@Transactional(rollbackFor = Exception.class)
public void importData(File f) throws IOException {
try (Reader r = Files.newBufferedReader(f)) { // ✓ try-with-resources
parse(r);
} catch (BizException e) {
log.error("解析失败 file={}", f.getName(), e); // ✓ 带堆栈,向上抛或转业务码
throw e;
}
}
中招长什么样:数据库里出现"一半成功一半失败"的脏数据;或者前端看到一个 500 页面带着完整堆栈和 SQL——后者同时还是渗透测试的送分题。
四、线程池参数:Executors 的四个快捷方法都别用
查什么(详细原理见 Alibaba Java 开发手册,这里只给检查动作):
- 禁用 Executors 快捷方法:
newFixedThreadPool/newSingleThreadExecutor的无界队列会 OOM,newCachedThreadPool的最大线程数是 Integer.MAX_VALUE - 手动
ThreadPoolExecutor四件套齐全:核心数、有界队列、拒绝策略、线程命名 - 队列满后的行为明确:拒绝策略选对场景——调用方执行(降级到主线程)适合可回退任务,丢弃+告警适合日志上报类
- 优雅关闭:
@PreDestroy里shutdown()+awaitTermination,发布时任务不丢 - 池参数与宿主隔离:独立线程池按下游分组,任何一个下游挂了不能拖死别的业务(与第 5 点超时联动)
private static final ThreadPoolExecutor EXPORT_POOL = new ThreadPoolExecutor(
4, 8, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), // ✓ 有界
new ThreadFactoryBuilder().setNameFormat("export-%d").build(), // ✓ 命名,dump 线程栈时能认出是谁
new ThreadPoolExecutor.CallerRunsPolicy()); // ✓ 反压给调用方
怎么查:grep -rn "Executors\." --include="*.java" 命中即整改;上线后观察线程数(jstack | grep "pool-" | wc -l)与队列水位(暴露到 Micrometer/Prometheus)。
中招长什么样:LinkedBlockingQueue 无界堆积后老年代稳步上涨(与第 9 点内存泄漏症状一致);或者 CallerRunsPolicy 用在支付链路,高峰期把 Web 线程拖去干导出活。
五、超时时间:全链路预算制,不是每个点各定各的
查什么:
- 每一次远程调用(HTTP/RPC/DB/MQ 发送)都有明确超时,默认值等于没设
- 预算制:下游超时之和 < 上游剩余时间。接口预算 3s,那它调用的每个下游各自 800ms~1s,不能每个都设 5s
- connect 超时与 read 超时分开设(connect 通常 1~2s,read 按下游 P99×2 估)
# Spring Cloud OpenFeign 基线
feign:
client:
config:
default:
connectTimeout: 1000
readTimeout: 2000 # 按下游P99×2定,写入代码评审记录
risk-service: # 慢下游单独收紧
readTimeout: 800
# MyBatis / JDBC 兜底
mybatis:
configuration:
default-statement-timeout: 10 # SQL 级兜底,防止慢SQL挂住连接
- MQ 消费者处理超时与幂等联动(详见《消息重复消费排查》:超时重投是重复消费四大来源之一)
- 流式/大文件接口按实际需要单独放宽,但要用异步线程承接,不占 Web 线程池
怎么查:grep -rn "RestTemplate\|WebClient\|FeignClient\|HttpClient" --include="*.java",逐个核对构造/配置处超时;架构图上给每层调用标预算(接口 3s → 服务 1s → DB 200ms)。
中招长什么样:就是引言那次事故——下游一抖,没有超时的调用把线程池挂满,故障从"下游局部慢"升级成"自己全挂",波及面扩大一个数量级。
六、幂等性:写接口和消费端都要有"业务唯一键"
查什么:
- 写接口:前端防抖只是体验优化,不是幂等。服务端必须有唯一业务键(订单号)+ 唯一索引/状态机/Redis SETNX 之一(方案细节见《接口幂等 4 种方案》)
- MQ 消费端:幂等键用业务单号而不是 messageId,SETNX 原子占位 + 失败释放键 + 数据库唯一索引兜底(《消息重复消费排查》的三重保障)
- 支付回调类:天然会被第三方重发,状态机 CAS(
UPDATE ... WHERE status='INIT')是最低成本的防线 - 幂等键的TTL 覆盖最大重试周期(MQ 16 次重试跨度数小时,Redis 键至少 24h)
-- 最低成本兜底:业务流水表唯一索引,重复插入直接 DuplicateKeyException
ALTER TABLE t_payment_flow ADD UNIQUE INDEX uk_biz_no (biz_no, type);
怎么查:Code Review 时对每个"会产生副作用"的入口(写接口、@RabbitListener/@RocketMQListener、定时任务)问一句:重复调用两次会发生什么? 答不上来的就是没做。
中招长什么样:用户一笔订单被扣两次款(《消息重复消费排查》的完整事故)、优惠券被领两次、库存扣成负数。
七、SQL 走索引:新 SQL 上线前必须 EXPLAIN 过
查什么(方法论文详见《PG 性能调优实战》《MySQL 索引实战》,这里给发布前动作):
- 新增/修改的 SQL 在等体量数据的环境 EXPLAIN 验证:禁 Seq Scan 扫大表、禁 filesort 大结果集
- 索引列保持裸列:WHERE 里禁函数包裹、隐式类型转换(
varchar = 123) - 分页防深翻:
LIMIT 1000000, 20改游标/延迟关联 - 批量写走批量语句,防 N+1 查询(循环里单条 select 是重灾区)
- 上线后慢日志/pg_stat_statements 观察新 SQL 的 mean_time
-- 发布评审附带物:新SQL + EXPLAIN(ANALYZE, BUFFERS) 截图
EXPLAIN (ANALYZE, BUFFERS) SELECT ... ;
-- 验收标准:无大表 Seq Scan;估算rows与actual偏差<10倍;Buffers 磁盘读可控
怎么查:发布单模板加一栏"本次涉及 SQL + EXPLAIN 截图",没有的不给过;上线后第一天盯 pg_stat_statements/慢日志 top10。
中招长什么样:上线几小时后连接池耗尽告警(《连接池泄漏排查》同款症状),慢 SQL 占满连接——索引问题从不敲锣打鼓地来,它借连接池的手杀你。
八、缓存过期:没有 TTL 的 key 都是定时炸弹
查什么:
- 所有写入 Redis 的 key 必须有 TTL(呼应《Redis 大 Key 阻塞》里那个 200MB 无 TTL Hash 拖垮集群的事故)。唯一例外要有评审记录
- TTL 加随机抖动(
base + random(0, 300s))防同一批 key 同时过期引发雪崩 - 热点 key 防击穿:互斥重建(单飞)或逻辑过期;空值防穿透(缓存空结果短 TTL)
- value 体积受控:单 key 建议 < 10KB、集合类分页/分片,禁止整个 List 一次塞入
- 缓存与 DB 的一致性策略明确:先更库后删缓存 + 延迟双删/订阅 binlog,说清楚容忍不一致的窗口
// 基线写法:TTL + 抖动
private static final long BASE_TTL_MINUTES = 30;
redisTemplate.opsForValue().set(key, value,
BASE_TTL_MINUTES * 60 + ThreadLocalRandom.current().nextLong(300), TimeUnit.SECONDS);
怎么查:grep -rn "opsForValue().set\|opsForHash().putAll\|opsForList" --include="*.java" 逐个确认带 TTL 参数;上线后用 redis-cli --bigkeys 和慢日志观察。
中招长什么样:凌晨批量任务写入 200 万个无 TTL key,三天后集群内存告警;或同一时刻缓存集体过期,DB 瞬间被打出洪峰。
九、内存泄漏:Java 不用手动释放,但泄漏点一点不少
查什么(排查方法详见《堆外内存暴增排查》,上线前重点防四类):
- ThreadLocal 用完 remove:线程池线程永不死亡,
set不remove就是永久泄漏,尤其 Web 应用里的用户上下文 - 流/连接必关:
InputStream/OutputStream/Connection/ByteBuf全部 try-with-resources 或 finally release(Netty 的ReferenceCountUtil.release) - 静态集合受控:
static Map做本地缓存的必须要有界(LRU/Caffeine + maximumSize),禁止"只进不出" - 大对象导出受控:Excel 导出用 SXSSFWorkbook 流式写,禁止把 50 万行全部读进内存
- 压测后对比堆:连续等量压测两轮,老年代不持续增长
// ✓ ThreadLocal 标准姿势:filter 里 set,finally 里 remove
try {
UserContext.set(user);
chain.doFilter(request, response);
} finally {
UserContext.clear(); // remove,线程归还池子前清干净
}
怎么查:压测期间 jstat -gcutil pid 5000 看老年代 FGC 后是否回落;堆外场景看 RSS 与 jmap -heap 的差值。
中招长什么样:每天 OOM 一次(老年代慢慢涨到阈值);或者容器没 OOM 但 RSS 一周涨满被 OOMKilled(堆外泄漏,《堆外内存暴增排查》同款)。
十、降级预案:故障来时你要有按钮可按
查什么:
- 核心链路识别清楚:什么是"砍了也不影响下单主流程"的(推荐、评价、积分),什么是"不能砍"的(支付、库存),砍掉的部分必须有开关
- 开关设计三原则:秒级生效(配置中心推送,不发版)、默认关闭、关闭路径有降级返回(返回缓存/默认值/友好提示,不是白屏)
- 下游熔断兜底:Sentinel/Resilience4j 配好慢调用比例/异常比例熔断规则,防止下游故障把自己打挂(与第 5 点超时联动)
- 降级动作演练过:上线前在预发把开关打开跑一遍,确认页面/接口表现符合预期——没演练过的开关等于没有
// 开关读取走配置中心,秒级生效
@RefreshScope
@RestController
public class RecommendController {
@Value("${switch.recommend-fallback:false}")
private boolean recommendFallback;
@GetMapping("/recommend")
public R<List<GoodsVO>> recommend(Long userId) {
if (recommendFallback) {
return R.ok(goodsService.hotSalesDefault()); // ✓ 降级返回兜底数据
}
return R.ok(recommendService.personal(userId)); // 正常链路
}
}
怎么查:发布评审对每个新依赖的外部服务问三句——它挂了会怎样?有没有开关?开关打开后用户看到什么?
中招长什么样:推荐服务故障,购物车页面跟着 500——因为没人给推荐降过级;凌晨故障只能干等下游恢复,或紧急发版。
十一、怎么用这份清单:嵌进发布流程才算真清单
清单的价值在于执行机制,不在于文档本身。推荐三层落法:
- 开发自检:发布单附带 Checklist 勾选记录(本文第八章速查卡即表格版),未勾选项写明"不适用原因"。
- CR 双检:Code Review 以此为底本,Reviewers 各分一半条目重点核查(幂等/超时/线程池给资深同学,日志/脱敏给所有人)。
- 发布评审:涉及支付/库存等核心链路的发布,逐条过会,SQL 附 EXPLAIN、开关附演练截图。
配套的 CI 静态检查能机器拦一部分:Sonar 规则覆盖吞异常/Executors/System.out;grep 类检查(TTL 缺失、超时缺失)写成 Git pre-push 钩子或流水线门禁。机器能拦的交给机器,人只查机器拦不住的"业务语义"问题。
十二、常见问题
12.1 十个点全查一遍太重了,能不能只查高风险的?
按"改动半径"裁剪:只查本次改到的东西及其直接下游。改了一个接口就查它链路上的超时/幂等/SQL;新建了缓存就查 TTL;加了线程池就查参数。但有三项是每次必查的全局项:日志级别(发布配置可能被覆盖)、敏感信息(新代码里的新出口)、降级开关(新依赖必须问)。清单的作用是"按着问一遍",不是"每项都写报告"。
12.2 超时设多短合适?设短了误伤正常慢请求怎么办?
用数据说话:取下游最近一周 P99 的 2 倍作为 read 超时,connect 固定 1~2s。设完观察一周超时率,>0.5% 说明下游 P99 在恶化(要么加超时要么推动下游优化)。防误伤的正确姿势是重试 + 幂等配合:超时收紧到 P99×2,GET 等幂等操作自动重试一次,非幂等操作靠第 6 点的幂等保护下重试。最忌讳一刀切 30s——那是把故障传播免费放行。
12.3 幂等和防重复提交是一回事吗?
不是,边界在"重从哪来":防重提交防的是用户手抖双击,秒级窗口,前端 token/按钮置灰就够;幂等防的是系统级重试——MQ 重投、超时重发、用户回退刷新重发,跨度分钟~小时级,必须服务端唯一业务键硬防。上线清单按幂等标准查:任何写入口重复调用两次,结果必须和一次相同。两者都做,但别用防抖冒充幂等。
12.4 这些检查能不能靠 Sonar/CodeQL 全自动拦住?
能拦住形态(吞异常、Executors、System.out、未关闭资源),拦不住语义:Sonar 不知道你这个 Redis key 该不该有 TTL、不知道 800ms 对这个下游算不算合理超时、更不知道这个写接口的幂等键应该用 orderNo 还是 userId。所以分工是:静态检查拦形态进 CI 门禁,清单拦语义进 CR 和发布评审。别指望任何工具替代"上线前 10 分钟的人工过表"。
12.5 老代码一堆问题,清单查出来的存量怎么处理?
分三档:P0 直接修(无超时、无 TTL、吞异常,改动小收益大,随本次发布带走);P1 排期修(线程池整改、脱敏补齐,进下个迭代);P2 记录技术债(架构级问题如全量本地缓存),登记到债本并设 owner。原则是"新代码零容忍、存量渐进清偿"——清单首先保证新增代码不再制造事故,而不是一次性还完历史债。
12.6 清单谁来维护?多久更新一次?
owner 是团队的"发布评审责任人",更新触发条件是每一次 P1 以上事故复盘后:把根因转化为一条新的检查项或收紧一条既有标准(例如发生过大 Key 事故后,"缓存过期"那条从"建议 TTL"收紧为"强制 TTL + 例外评审")。每季度整体回顾一次,删掉连续两季度都"零命中"的过时项,保持清单精炼可执行。
十三、总结
十点速查卡(发布单勾选版)
□ 1 日志 root=INFO,无 DEBUG 残留;循环体无日志;异步 Appender
□ 2 脱敏 手机/卡号/证件/token 四出口(日志/响应/存储/三方)全查
□ 3 异常 全局兜底生效;rollbackFor;吞异常零容忍;try-with-resources
□ 4 线程池 无 Executors.*;有界队列;命名线程;拒绝策略明确;优雅关闭
□ 5 超时 每个远程调用 connect+read;下游之和<上游预算;SQL 有兜底超时
□ 6 幂等 写接口/MQ消费/回调:重复调两次结果不变;唯一键+唯一索引
□ 7 SQL 新SQL附 EXPLAIN(ANALYZE);索引列裸列;防深分页/N+1
□ 8 缓存 所有 key 有 TTL+抖动;防击穿/穿透;单 key<10KB;一致性策略明确
□ 9 内存 ThreadLocal remove;流关闭;静态集合有界;压测后老年代回落
□ 10 降级 新依赖有开关:秒级生效+默认关+降级返回;熔断规则就绪;演练过
给团队的建议
| 项 | 建议 |
|---|---|
| 落地 | 清单进发布单模板;CR 以此为底本分工核查 |
| 自动化 | Sonar/流水线门禁拦形态问题,人工只查业务语义 |
| 裁剪 | 按改动半径裁剪检查范围;日志/脱敏/开关三项全局必查 |
| 闭环 | 每次 P1 事故复盘 → 转化为清单条目;季度回顾删过时项 |
| 存量 | 新代码零容忍,存量分 P0/P1/P2 渐进清偿 |
| 演练 | 开关不演练不算有;EXPLAIN 不截图不算查过 |
一句话
这份清单的本质不是十条技术知识,而是一套对抗遗忘的流程设计:线上事故的根因 90% 不是"不会写"而是"忘了查"——超时没设让下游抖动把线程池挂满,日志级别没改让磁盘被循环里的 log.info 打满,缓存没有 TTL 让凌晨的批量任务把集群撑爆,这些知识点人人都会,但发布评审前的匆忙时刻没有人逐行按住代码问"重复调用两次会怎样""下游挂了会怎样"。十个点各自对应一类事故形态:日志和脱敏管"往外说什么",异常兜底和内存泄漏管"资源收不收得回来",线程池和超时管"故障会不会被放大",幂等和 SQL 索引管"数据的正确性与效率",缓存过期管"定时炸弹有没有拆",降级预案管"故障来时有没有按钮可按"。清单的用法比内容更重要:开发自检进发布单、CR 以此分工、核心链路过会,Sonar 拦形态、人工查语义,每一次 P1 事故复盘后把它转化为新的检查条目——清单长出来的方式,就是团队把每一次血泪变成制度的方式;上线前多花的 10 分钟,买回的是凌晨 2 点不用爬起来翻日志的那个夜晚。
互动话题:你们上线前有什么"必查清单"?哪一条是被真实事故教训换来的?上面十条里你觉得最容易漏的是哪一条?欢迎在评论区补充你的检查项,我们一起把这份清单迭代成社区版。
标题:Java 代码质量自检清单:上线前必查的 10 个点
作者:jiangyi
地址:http://jiangyi.space/articles/2026/10/18/1792330079016.html
公众号:服务端技术精选
- 引言
- 一、日志级别:磁盘是被循环里的 log.info 打满的
- 二、敏感信息脱敏:每一个出口都要过一遍
- 三、异常兜底:三种最典型的错误写法
- 四、线程池参数:Executors 的四个快捷方法都别用
- 五、超时时间:全链路预算制,不是每个点各定各的
- 六、幂等性:写接口和消费端都要有"业务唯一键"
- 七、SQL 走索引:新 SQL 上线前必须 EXPLAIN 过
- 八、缓存过期:没有 TTL 的 key 都是定时炸弹
- 九、内存泄漏:Java 不用手动释放,但泄漏点一点不少
- 十、降级预案:故障来时你要有按钮可按
- 十一、怎么用这份清单:嵌进发布流程才算真清单
- 十二、常见问题
- 12.1 十个点全查一遍太重了,能不能只查高风险的?
- 12.2 超时设多短合适?设短了误伤正常慢请求怎么办?
- 12.3 幂等和防重复提交是一回事吗?
- 12.4 这些检查能不能靠 Sonar/CodeQL 全自动拦住?
- 12.5 老代码一堆问题,清单查出来的存量怎么处理?
- 12.6 清单谁来维护?多久更新一次?
- 十三、总结
- 十点速查卡(发布单勾选版)
- 给团队的建议
- 一句话
评论