| name | backend-engineering |
| description | 后端资深工程师技能 - 架构设计、服务端开发、中间件、错误处理、日志、配置管理、部署运维。当你涉及后端服务开发、Go/Python/Node/Java/PHP后端代码、中间件配置、服务间通信、配置管理、日志策略、进程管理、交叉编译、部署流程时必须使用此技能。即使用户只是说"写个接口"或"后端加个功能",也应触发。 |
后端资深工程师技能 (Backend Engineering Skill)
快速规则(日常开发时自动加载,只需读到这里)
[后端核心清单] ① 入参永远不信任客户端,服务端必须重新校验所有字段(类型/范围/长度/格式) ② 错误分层处理:参数层→业务层→基础设施层,每层有明确的错误码和用户可读消息 ③ 配置不硬编码,用环境变量/配置文件/数据库,支持热加载
[事务三禁] ❌事务内调外部HTTP(外部超时=长时间持锁,全库阻塞) ❌无WHERE的UPDATE/DELETE(一次清空全表) ❌SELECT *(新增字段意外暴露敏感数据)
[健壮性铁律] 外部依赖(DB/Redis/MQ/HTTP)必须有超时+重试+熔断,非核心失败不拖垮核心流程
写/改后端代码时,强制遵守:
- 入参校验:所有外部输入在handler层校验完毕再进service层,service层不处理格式问题
- 分层架构:Handler(入参校验+响应格式) → Service(业务逻辑) → DAO/Repository(数据访问),禁止跨层调用(跨层=职责混乱,改一层影响全局,无法单独测试)
- 错误处理:每个函数返回error必须处理,禁止
_ = someFunc()忽略error(忽略的error会在生产环境变成无声的数据损坏);错误向上传播时附加上下文
- 事务范围:事务范围最小化,只包含必须原子执行的操作;事务内禁止调外部HTTP/RPC(外部超时=长时间持锁,全库阻塞)
- 超时控制:所有外部调用(HTTP/DB/Redis/RPC)必须设置超时,无超时=潜在goroutine泄漏
- 配置管理:配置从环境变量/配置文件/数据库读取,禁止硬编码(硬编码=改配置就要重新编译部署,且密钥泄漏到Git历史无法撤回);敏感配置(密钥/密码)不进代码仓库
- 日志规范:关键操作(支付/认证/数据变更)必须有日志;日志含请求ID可追踪;敏感数据mask
- 幂等设计:写操作必须考虑重复执行的结果;支付/订单类用唯一约束+幂等键
完整审查流程(手动 /backend-engineering 或专项审查时执行)
Phase 1: 架构与分层审查
-
目录结构扫描:
- 检查是否遵循分层架构(handler/service/model/config/middleware)
- 识别跨层调用(handler直接操作DB、service直接写HTTP响应)
- 检查循环依赖(A import B, B import A)
-
依赖注入与耦合度:
- 服务间是否通过接口解耦
- 全局变量/单例是否过多(增加测试难度和并发风险)
- 配置是否集中管理(散落的os.Getenv/硬编码)
Phase 2: 错误处理审查
-
错误传播链检查(Grep所有error处理):
- 被忽略的error(
_ = func() 或只log不return)
- 错误信息是否附加了上下文(
fmt.Errorf("xxx: %w", err) vs 裸传err)
- panic/recover使用是否合理(只在顶层recover,业务层禁止panic)
- 错误码体系是否统一(自定义error type vs 字符串比较)
-
错误分层:
| 层级 | 错误类型 | HTTP状态码 | 处理方式 |
|---|
| 参数校验 | 格式/类型/范围错误 | 400 | 返回具体字段+原因 |
| 业务逻辑 | 余额不足/已存在/不允许 | 422 | 返回业务错误码+用户消息 |
| 认证授权 | 未登录/无权限 | 401/403 | 统一认证中间件处理 |
| 基础设施 | DB/Redis/MQ连接失败 | 503 | 日志+告警+客户端提示重试 |
| 未知错误 | 未预期异常 | 500 | 生成唯一错误ID+日志完整堆栈 |
Phase 3: 并发与资源管理
-
并发安全:
- 共享状态(map/slice/全局变量)是否有锁保护
- goroutine是否有泄漏风险(未关闭channel/无退出条件/无超时)
- 数据库操作是否考虑并发(乐观锁/悲观锁/原子操作)
- 连接池配置是否合理(最大连接数/空闲连接/超时)
-
资源管理:
- DB连接/HTTP客户端/文件句柄是否正确关闭(defer)
- 大文件处理是否用流式(stream)而非全量加载到内存
- 定时任务/后台goroutine是否有graceful shutdown机制
Phase 4: 配置与环境管理
-
配置审查:
- 硬编码的URL/端口/密钥/超时值(Grep常见模式)
- 不同环境(dev/staging/prod)的配置是否隔离
- 敏感配置是否加密或通过安全渠道传递
- 配置变更是否需要重启(热加载机制)
-
环境一致性:
- 开发/测试/生产环境的差异是否文档化
- 交叉编译配置(GOOS/GOARCH)是否正确
- 构建脚本/Dockerfile是否可重复构建
Phase 5: 日志与可观测性
-
日志审查:
- 关键操作是否有日志(用户注册/登录/支付/数据变更/权限变更)
- 日志级别是否合理(Debug/Info/Warn/Error分明)
- 是否有请求ID贯穿全链路(可追踪)
- 敏感数据是否mask(密码/token/手机号/身份证)
- 日志是否会膨胀(循环中打日志/高频API每次打日志)
-
监控与告警:
- 是否有健康检查端点(/health)
- 关键指标是否可观测(请求量/响应时间/错误率/队列长度)
- 是否有超时/错误率告警机制
Phase 6: 部署与运维
-
部署流程:
- 构建产物是否可重现(锁文件/固定版本/多阶段构建)
- 部署是否零停机(优雅关闭/健康检查/滚动更新)
- 回滚方案是否就绪(保留上一版本二进制/数据库Migration可逆)
-
进程管理:
- 是否有进程守护(systemd/supervisor)
- 信号处理(SIGTERM/SIGINT)是否优雅关闭
- 启动依赖是否处理(等待DB就绪再启动服务)
Phase 7: 性能基线
- 性能检查:
- N+1查询(循环中逐条查DB)
- 大量数据全量加载到内存(分页/流式处理)
- 无索引的频繁查询(EXPLAIN检查)
- 重复计算(应缓存的配置/元数据每次重新查询)
- 序列化/反序列化开销(大JSON/Protobuf选择)
Phase 8: 输出报告
## 后端工程审查报告
### 架构问题
| # | 文件:行号 | 问题 | 影响 | 修复建议 | 优先级 |
### 错误处理缺陷
| # | 文件:行号 | 问题 | 风险 | 修复方案 |
### 并发/资源风险
| # | 文件:行号 | 问题 | 场景 | 修复方案 | 严重度 |
### 配置问题
| # | 位置 | 问题 | 建议 |
### 日志/可观测性
| # | 位置 | 缺失内容 | 建议 |
### 部署/运维
| # | 问题 | 当前状态 | 建议 |
代码简洁高效铁律(所有后端语言通用)
写后端代码时强制遵守,违反=出错的根源:
| 规则 | 原因 |
|---|
| 函数单一职责,一个函数只做一件事 | 职责混杂的函数=测试困难+修改危险+读不懂 |
| 函数体不超过50行,超过就拆 | 长函数=高认知负担,容易藏层bug且难读 |
| 强制early return,减少嵌套 | 多层if/else嵌套=可读性崩溃,边界条件先return出去 |
| 不重复自己(DRY),但不过度抽象 | 复制粘贴=改一处忘另一处;但过度抽象=改一处爆全局 |
变量/函数命名自解释,禁止a/b/tmp/data等无意义名 | 坏命名=读代码如解谜,改代码如赌博 |
| 错误处理不偷懒,每个error必须有明确去向 | 忽略error=生产环境的定时炸弹 |
| 禁止过早优化,先正确再快 | 过早优化=代码复杂度爆表,性能提升不可测量 |
| 配置和魔数提取为常量/配置项 | 魔数散落在代码中=改一个要搜全项目,还可能漏改 |
生产事故防御清单(写代码时逐项检查)
每写一个功能,问自己这些问题:
| # | 检查项 | 问自己 | 防御方法 |
|---|
| 1 | 空值处理 | 这个变量可能为nil/null/None吗? | 所有外部输入、DB查询结果、map取值都先检查空值 |
| 2 | 边界条件 | 空数组/空字符串/零值/负数/最大值时会怎样? | 每个函数的第一行应该是边界检查 |
| 3 | 并发安全 | 两个请求同时执行这段代码会怎样? | 共享状态加锁,数据库用原子操作(WHERE count>0) |
| 4 | 重复执行 | 这个操作执行两次会怎样? | 幂等键/唯一约束/INSERT ON DUPLICATE |
| 5 | 失败回滚 | 执行到一半失败了怎么办? | 事务包裹多步操作,失败时清理已创建的资源 |
| 6 | 超时处理 | 外部服务响应慢/不响应时会怎样? | 所有外部调用设超时,超时后有明确降级策略 |
| 7 | 数据一致性 | 写入A表成功但写B表失败时数据还一致吗? | 事务或补偿机制 |
| 8 | 资源泄漏 | 异常时连接/文件/锁会释放吗? | defer/finally/try-with-resources保证释放 |
| 9 | 数值安全 | 金额/数量可能为负数/零/溢出吗? | 校验范围,金额用整数分表示禁止浮点 |
| 10 | 时序依赖 | 调用顺序变了会崩吗? | 减少函数间的隐式依赖,必要时加断言/检查 |
| 11 | 日志追踪 | 出问题时能通过日志定位吗? | 关键操作打日志,含请求ID/用户ID/操作类型 |
| 12 | 配置变更 | 配置改了会崩吗?配置丢了有默认值吗? | 配置有默认值,缺失关键配置时拒绝启动而不是静默运行 |
语言专项规则
Go后端(Go项目强制遵守)
错误处理:
- 每个
if err != nil必须处理:return/log+return/wrap后return
- 用
fmt.Errorf("context: %w", err)附加上下文,禁止裸传err(裸err无法定位出错位置,生产排查时只看到“connection refused”不知哪里调的)
- 业务错误用自定义error类型或哨兵错误,禁止字符串比较(字符串比较耗尽时typo就崩,且无法被编译器检查)
defer中的error也要处理
并发:
map非并发安全,并发读写用sync.Map或sync.RWMutex保护
- goroutine必须有退出机制:context取消/channel关闭/超时
sync.WaitGroup的Add在goroutine外调用,Done用defer
- channel发送方负责关闭,接收方不关闭
性能:
- 热路径避免反射(reflect)和interface{}断言
- 字符串拼接用
strings.Builder而非+循环拼接
- 大slice预分配容量:
make([]T, 0, expectedLen)
- HTTP客户端复用(全局
http.Client),不要每次请求新建
项目结构(推荐):
cmd/api/main.go # 入口
internal/
config/config.go # 配置加载
handler/ # HTTP handler(入参校验+响应)
service/ # 业务逻辑
model/ # 数据模型
middleware/ # 中间件(认证/限流/CORS/日志)
PHP后端(PHP项目强制遵守)
安全:
- SQL必须用预处理语句(PDO/MySQLi prepared),禁止字符串拼接(一条恶意输入可导出/删除全库)
- 禁止
eval()/exec()/system()拼接用户输入(直接远程代码执行RCE,服务器被接管)
- 禁止
extract($_GET)/extract($_POST)(变量覆盖攻击,攻击者可覆盙任意变量包括$_SESSION)
- XSS输出用
htmlspecialchars($str, ENT_QUOTES, 'UTF-8')
- Session用
session_regenerate_id(true)防固定
类型安全:
- 开启
declare(strict_types=1)严格类型
- 函数参数和返回值声明类型
- 禁止
==做安全比较(用===),因为PHP弱类型比较会导致"0" == false
错误处理:
- 生产环境
display_errors = Off,log_errors = On
- 用try/catch处理异常,禁止
@静默错误(静默错误=问题被吐出但你看不到,最终在生产环境爆发)
- 设置全局异常处理器,返回统一格式错误
Python后端(Python项目强制遵守)
安全:
- SQL用参数化查询(
cursor.execute("SELECT * FROM t WHERE id=%s", (id,)))
- 禁止
eval()/exec()处理用户输入(直接RCE,攻击者可执行任意代码)
- 禁止
pickle.loads反序列化不可信数据(pickle可执行任意代码,RCE风险)
- 禁止
yaml.load()用yaml.safe_load()替代(yaml.load可执行任意Python对象构造,RCE风险)
类型安全:
- 用type hints标注函数参数和返回值
- 核心模块用Pydantic/dataclass做数据校验
- 禁止可变默认参数(
def f(items=[])→共享变异)
异步:
async/await不混用同步阻塞调用(会堵塞事件循环)
except Exception禁止裸用(吃掉KeyboardInterrupt和SystemExit)
Node.js后端(Node项目强制遵守)
安全:
- SQL用参数化查询,禁止模板字符串拼接(模板字符串拼接SQL=注入漏洞,一条输入可导出全库)
- 禁止
eval()/new Function()处理用户输入(直接RCE,服务器被接管)
- HTTP响应设置Content-Type和安全头(helmet)
- 禁止
child_process.exec(userInput),用execFile+参数数组(exec走shell可注入命令,execFile直接执行无shell解析)
异步:
- Promise必须catch,禁止未处理的rejection(进程会崩)
async函数必须try/catch,或调用方.catch()
- 设置
process.on('unhandledRejection')全局捕获
性能:
- 禁止同步文件操作(
fs.readFileSync)在请求处理中(阻塞事件循环)
- 大数据用Stream处理,不全量加载到内存
- HTTP客户端复用(keep-alive agent)
Java后端(Java项目强制遵守)
安全:
- SQL用PreparedStatement,禁止字符串拼接(SQL注入是最常见的漏洞,一条输入可导出/删除全库)
- 禁止
Runtime.exec(userInput),用ProcessBuilder+参数数组(exec字符串可注入shell命令,ProcessBuilder参数数组不经过shell解析)
- 反序列化用白名单过滤,禁止
ObjectInputStream直接读不可信数据(Java反序列化可触发任意代码执行,历史CVE无数)
资源管理:
- IO/DB连接用try-with-resources自动关闭
- 线程池用
ExecutorService,禁止无限创建线程(无限线程=流量峰值时OOM崩溃)
- 连接池(HikariCP)配置合理的max/min/timeout
并发:
- 共享可变状态用
synchronized/Lock/AtomicXxx保护
- 禁止
new Thread()直接创建,用线程池(直接创建线程无法复用且无数量上限,高并发时耗尽系统资源)
ConcurrentHashMap替代Collections.synchronizedMap
约束
- 所有发现必须有文件:行号引用
- 架构建议基于实际项目规模(不过度设计小项目)
- 性能建议基于实际瓶颈(不凭经验猜测)
- 部署建议考虑现有基础设施(不推荐用户没有的工具)
- 代码简洁优先:能用标准库不用第三方,能用已有方案不另起炉灶
- 不主动重构:只在用户明确要求时才重构,平时只做最小化修改
- 保持现有风格:遵循项目已有的命名/缩进/组织规范,不引入新范式