# Backend Engineering

> 后端资深工程师技能 - 架构设计、服务端开发、中间件、错误处理、日志、配置管理、部署运维。当你涉及后端服务开发、Go/Python/Node/Java/PHP后端代码、中间件配置、服务间通信、配置管理、日志策略、进程管理、交叉编译、部署流程时必须使用此技能。即使用户只是说"写个接口"或"后端加个功能"，也应触发。

- Skill: `yuexueyu/backend-engineering` (Agent Skill)
- Install (CLI): `npx skillmds@latest add yuexueyu/backend-engineering`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yuexueyu/backend-engineering/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: yuexueyu (https://skillmd.com/u/yuexueyu)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/yuexueyu/backend-engineering

---


# 后端资深工程师技能 (Backend Engineering Skill)

## 快速规则（日常开发时自动加载，只需读到这里）

> **[后端核心清单]** ① 入参永远不信任客户端，服务端必须重新校验所有字段（类型/范围/长度/格式） ② 错误分层处理：参数层→业务层→基础设施层，每层有明确的错误码和用户可读消息 ③ 配置不硬编码，用环境变量/配置文件/数据库，支持热加载
> **[事务三禁]** ❌事务内调外部HTTP（外部超时=长时间持锁，全库阻塞） ❌无WHERE的UPDATE/DELETE（一次清空全表） ❌SELECT *（新增字段意外暴露敏感数据）
> **[健壮性铁律]** 外部依赖(DB/Redis/MQ/HTTP)必须有超时+重试+熔断，非核心失败不拖垮核心流程

写/改后端代码时，强制遵守：
1. **入参校验**：所有外部输入在handler层校验完毕再进service层，service层不处理格式问题
2. **分层架构**：Handler(入参校验+响应格式) → Service(业务逻辑) → DAO/Repository(数据访问)，禁止跨层调用（跨层=职责混乱，改一层影响全局，无法单独测试）
3. **错误处理**：每个函数返回error必须处理，禁止`_ = someFunc()`忽略error（忽略的error会在生产环境变成无声的数据损坏）；错误向上传播时附加上下文
4. **事务范围**：事务范围最小化，只包含必须原子执行的操作；事务内禁止调外部HTTP/RPC（外部超时=长时间持锁，全库阻塞）
5. **超时控制**：所有外部调用(HTTP/DB/Redis/RPC)必须设置超时，无超时=潜在goroutine泄漏
6. **配置管理**：配置从环境变量/配置文件/数据库读取，禁止硬编码（硬编码=改配置就要重新编译部署，且密钥泄漏到Git历史无法撤回）；敏感配置(密钥/密码)不进代码仓库
7. **日志规范**：关键操作(支付/认证/数据变更)必须有日志；日志含请求ID可追踪；敏感数据mask
8. **幂等设计**：写操作必须考虑重复执行的结果；支付/订单类用唯一约束+幂等键

---

## 完整审查流程（手动 /backend-engineering 或专项审查时执行）

### Phase 1: 架构与分层审查

1. **目录结构扫描**：
   - 检查是否遵循分层架构(handler/service/model/config/middleware)
   - 识别跨层调用（handler直接操作DB、service直接写HTTP响应）
   - 检查循环依赖（A import B, B import A）

2. **依赖注入与耦合度**：
   - 服务间是否通过接口解耦
   - 全局变量/单例是否过多（增加测试难度和并发风险）
   - 配置是否集中管理（散落的os.Getenv/硬编码）

### Phase 2: 错误处理审查

3. **错误传播链检查**（Grep所有error处理）：
   - 被忽略的error（`_ = func()` 或只log不return）
   - 错误信息是否附加了上下文（`fmt.Errorf("xxx: %w", err)` vs 裸传err）
   - panic/recover使用是否合理（只在顶层recover，业务层禁止panic）
   - 错误码体系是否统一（自定义error type vs 字符串比较）

4. **错误分层**：

| 层级 | 错误类型 | HTTP状态码 | 处理方式 |
|------|----------|-----------|---------|
| 参数校验 | 格式/类型/范围错误 | 400 | 返回具体字段+原因 |
| 业务逻辑 | 余额不足/已存在/不允许 | 422 | 返回业务错误码+用户消息 |
| 认证授权 | 未登录/无权限 | 401/403 | 统一认证中间件处理 |
| 基础设施 | DB/Redis/MQ连接失败 | 503 | 日志+告警+客户端提示重试 |
| 未知错误 | 未预期异常 | 500 | 生成唯一错误ID+日志完整堆栈 |

### Phase 3: 并发与资源管理

5. **并发安全**：
   - 共享状态(map/slice/全局变量)是否有锁保护
   - goroutine是否有泄漏风险（未关闭channel/无退出条件/无超时）
   - 数据库操作是否考虑并发（乐观锁/悲观锁/原子操作）
   - 连接池配置是否合理（最大连接数/空闲连接/超时）

6. **资源管理**：
   - DB连接/HTTP客户端/文件句柄是否正确关闭(defer)
   - 大文件处理是否用流式(stream)而非全量加载到内存
   - 定时任务/后台goroutine是否有graceful shutdown机制

### Phase 4: 配置与环境管理

7. **配置审查**：
   - 硬编码的URL/端口/密钥/超时值（Grep常见模式）
   - 不同环境(dev/staging/prod)的配置是否隔离
   - 敏感配置是否加密或通过安全渠道传递
   - 配置变更是否需要重启（热加载机制）

8. **环境一致性**：
   - 开发/测试/生产环境的差异是否文档化
   - 交叉编译配置(GOOS/GOARCH)是否正确
   - 构建脚本/Dockerfile是否可重复构建

### Phase 5: 日志与可观测性

9. **日志审查**：
   - 关键操作是否有日志（用户注册/登录/支付/数据变更/权限变更）
   - 日志级别是否合理（Debug/Info/Warn/Error分明）
   - 是否有请求ID贯穿全链路（可追踪）
   - 敏感数据是否mask（密码/token/手机号/身份证）
   - 日志是否会膨胀（循环中打日志/高频API每次打日志）

10. **监控与告警**：
    - 是否有健康检查端点(/health)
    - 关键指标是否可观测（请求量/响应时间/错误率/队列长度）
    - 是否有超时/错误率告警机制

### Phase 6: 部署与运维

11. **部署流程**：
    - 构建产物是否可重现（锁文件/固定版本/多阶段构建）
    - 部署是否零停机（优雅关闭/健康检查/滚动更新）
    - 回滚方案是否就绪（保留上一版本二进制/数据库Migration可逆）

12. **进程管理**：
    - 是否有进程守护(systemd/supervisor)
    - 信号处理(SIGTERM/SIGINT)是否优雅关闭
    - 启动依赖是否处理（等待DB就绪再启动服务）

### Phase 7: 性能基线

13. **性能检查**：
    - 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`

## 约束
- 所有发现必须有文件:行号引用
- 架构建议基于实际项目规模（不过度设计小项目）
- 性能建议基于实际瓶颈（不凭经验猜测）
- 部署建议考虑现有基础设施（不推荐用户没有的工具）
- **代码简洁优先**：能用标准库不用第三方，能用已有方案不另起炉灶
- **不主动重构**：只在用户明确要求时才重构，平时只做最小化修改
- **保持现有风格**：遵循项目已有的命名/缩进/组织规范，不引入新范式

