一、为什么 AI 时代规范更重要
一个常见的误解:用了 AI 写代码,规范就不重要了,因为 AI 每次生成的代码都不一样,规范约束不了 AI。
实际情况是反过来的:规范在 AI 时代变得更重要
原因一:规范是给 AI 的精准指令。.cursorrules 里写的规范,AI 每次生成代码都会遵守。规范越详细,AI 输出越稳定。
原因二:AI 生成的代码量大大增加,没有规范约束,代码库会以更快的速度混乱。
原因三:AI 有"默认行为",不明确禁止,它会悄悄做你不想要的事情(用错 ORM、忘记加 Swagger、返回明文密码等)。
二、代码风格规范(code-style.md)
文件路径:docs/engineering/code-style.md
# 代码风格规范
> 本规范适用于所有代码,包括 AI 辅助生成的代码。
> AI 生成代码后,必须对照本规范进行审查。
## 文件命名
- 所有源码文件:kebab-case
- ✅ user-profile.service.ts
- ❌ UserProfileService.ts
- ❌ userProfileService.ts
- 测试文件:与被测文件同名,加 .spec.ts
- ✅ user.service.spec.ts
## 变量和函数命名
- 变量和函数:camelCase
- ✅ getUserById、totalAmount、isActive
- ❌ get_user_by_id、TotalAmount
- 类名:PascalCase
- ✅ UserService、CreateUserDto
- 常量(不变的配置值):SCREAMING_SNAKE_CASE
- ✅ MAX_RETRY_COUNT、DEFAULT_PAGE_SIZE
- 布尔值变量:以 is、has、can、should 开头
- ✅ isActive、hasPermission、canDelete、shouldRetry
## 函数规范
- 单个函数不超过 30 行(不含注释和空行)
- 函数只做一件事(单一职责)
- 函数参数不超过 4 个,超过就用对象传
- ❌ createUser(name, email, password, role, isActive, avatar)
- ✅ createUser(createUserDto: CreateUserDto)
- 所有 public 方法必须有 JSDoc 注释
## JSDoc 注释格式
所有 Service 里的 public 方法按以下格式写:
/\*\*
- 根据 ID 查找用户
- @param id 用户 ID
- @returns 用户对象(不含 password 字段),用户不存在时抛出 NotFoundException
\*/
async findOne(id: number): Promise<SafeUser>
## 注释规范
- 行内注释:解释"为什么",不解释"是什么"
- ❌ // 给 age 加 1
- ✅ // 用户年龄从出生年份计算,需要加 1 因为还没到生日
## TypeScript 规范
- 禁止使用 any 类型
- 不确定类型时用 unknown,配合类型守卫
- 实在需要宽松类型时用 Record<string, unknown>
- 优先使用 interface 定义数据结构(不用 class 的场景)
- 可选字段用 ?
- ✅ name?: string
- ❌ name: string | undefined
- 枚举值用 enum
- ✅ enum UserRole { USER = 'USER', ADMIN = 'ADMIN' }
- ❌ type UserRole = 'USER' | 'ADMIN'
## 绝对禁止项
- ❌ console.log(用 NestJS Logger)
- ❌ any 类型
- ❌ 硬编码配置值(密钥、URL、端口、账号密码)
- ❌ 空的 catch 块
- ❌ 注释掉的无用代码(直接删,Git 有历史记录)
- ❌ throw new Error(...)(用 NestJS 内置异常类)
三、Git Commit 规范(commit-guide.md)
文件路径:docs/engineering/commit-guide.md
# Git Commit 规范
使用 Conventional Commits 规范:https://www.conventionalcommits.org
## 格式
<type>(<scope>): <subject>
[body - 可选]
[footer - 可选]
## type 类型
| type | 使用场景 |
| -------- | ---------------------------------------- |
| feat | 新增功能 |
| fix | Bug 修复 |
| docs | 文档更新 |
| style | 代码格式调整(不影响逻辑,如空格、分号) |
| refactor | 重构(不是新功能也不是修 Bug) |
| test | 添加或修改测试 |
| chore | 构建工具、依赖更新、CI 配置等 |
| perf | 性能优化 |
| revert | 回滚某个 commit |
## scope 范围
按项目的模块划分:user、order、product、auth、common、config、prisma
## subject 写法规则
- 不超过 50 个字符
- 动词开头,现在时态("添加"不是"添加了")
- 末尾不加句号
- 中英文均可,团队内统一即可
## 完整示例
feat(user): 新增用户软删除功能
使用 deletedAt 字段实现软删除,避免物理删除导致关联数据异常。
findAll 查询自动过滤已删除的用户。
Closes #123
---
fix(auth): 修复 JWT Token 过期后未返回 401 的问题
---
refactor(order): 提取重复的用户验证逻辑为 validateUser 私有方法
---
test(user): 补充 UserService.remove 软删除的单元测试
---
chore: 升级 Prisma 到 5.8.0
## 用 Claude Code 辅助写 Commit
在 git add 之后,用 Claude Code 根据 diff 生成 commit message:
````bash
claude "看一下 git diff --staged 的内容,按照 Conventional Commits 规范,
用中文帮我写一条 commit message"
```text
复制生成的 message 使用,或者在此基础上微调。
## 自动校验配置(commitlint + Husky)
安装:
```bash
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
chmod +x .husky/commit-msg
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
```text
配置后效果:
- 不符合规范的 commit message 会被自动拦截
- 强制所有人(包括 AI 生成的代码)的提交都符合规范
```plain
---
## 四、PR Review 规范(pr-review-guide.md)
文件路径:`docs/engineering/pr-review-guide.md`
```markdown
# PR 提交与 Review 规范
## PR 创建规范
1. 一个 PR 只做一件事,不混入不相关的改动
2. PR 标题格式和 Commit 规范一致
3. PR 描述必须填写(使用项目 PR 模板)
4. 提交前本地运行通过:npm test + npx tsc --noEmit
## AI Code Review 步骤(提交 PR 前完成)
### 第一步:安全扫描
```bash
claude "扫描本次改动涉及的文件,检查安全漏洞:
SQL注入、硬编码密钥、权限缺失、敏感字段暴露、路径穿越"
```text
### 第二步:规范检查
```bash
claude "检查改动的代码是否符合 @docs/engineering/code-style.md 里的规范,
列出不符合的地方"
```text
### 第三步:测试覆盖
```bash
npm test -- --coverage
```text
确认覆盖率没有下降。
### 第四步:填写 PR 模板
把 AI 扫描结果摘要粘贴到 PR 描述的"AI Review 结果摘要"部分。
## Vibe Coding 时代的 Review 原则
AI 帮你保证了代码风格的一致性,Review 者应该把精力放在:
- 业务逻辑是否正确
- 边界条件是否考虑全了
- 数据库设计是否合理
- 接口设计是否符合 RESTful 规范
- 性能问题(N+1 查询、缺少索引)
不需要花时间在:
- 命名是否规范(.cursorrules 保证)
- 是否有注释(.cursorrules 保证)
- 返回格式是否统一(.cursorrules 保证)
## 合入规则
- 至少 1 个 Reviewer Approve
- CI 全部通过(TypeScript 检查 + 测试 + AI 安全扫描)
- 没有未解决的 Review 评论
- PR 描述的 Checklist 全部勾选
## Review 者行为规范
- PR 创建后 24 小时内给出 Review
- 发现问题用 Request Changes,并给出明确的修改建议
- 不纠结代码风格(那是 AI 和 Rules 的责任)
- 讨论方案时给出两种以上的备选,不只是"这样不好"
```plain
---
## 五、演示操作步骤
### 准备工作
**Step 1**:创建规范文件目录
```bash
mkdir -p docs/engineering
touch docs/engineering/code-style.md
touch docs/engineering/commit-guide.md
touch docs/engineering/pr-review-guide.md
````
**Step 2**:把第二、三、四章的内容分别写入对应文件
---
### 配置 Husky + commitlint
**Step 1**:安装依赖
```bash
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
```
**Step 2**:初始化 Husky
```bash
npx husky init
```
执行后会生成 `.husky/` 目录和 `pre-commit` 文件。
**Step 3**:创建 commit-msg hook
```bash
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
chmod +x .husky/commit-msg
```
**Step 4**:创建 commitlint 配置文件
```bash
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
```
**Step 5**:在 `package.json` 里确认有 `prepare` 脚本(Husky 需要)
```json
{
"scripts": {
"prepare": "husky"
}
}
```
**Step 6**:验证 Husky 配置
```bash
# 目录结构确认
ls -la .husky/
cat .husky/commit-msg
# 测试不规范的 commit(应该被拦截)
git add docs/
git commit -m "改了一些东西"
# 期望:报错,提示不符合规范
# 测试规范的 commit(应该通过)
git commit -m "docs: 添加工程规范文件"
# 期望:成功提交
```
---
### 演示 AI 辅助写 Commit
**Step 1**:修改任意一个文件(比如给 `user.service.ts` 加一个方法)
**Step 2**:暂存改动
```bash
git add src/modules/user/user.service.ts
git diff --staged # 查看 diff 内容
```
**Step 3**:用 Claude Code 生成 commit message
```bash
claude "看一下 git diff --staged 的内容,按照 Conventional Commits 规范,
用中文帮我写一条 commit message,格式:type(scope): subject"
```
**Step 4**:用生成的 message 提交
```bash
git commit -m "feat(user): 新增用户搜索功能,支持按 username 模糊匹配"
```
---
### 在 .cursorrules 里引用规范文件
**Step 1**:打开 `.cursorrules`,加入以下内容:
```plain
## 代码规范参考
- 代码风格规范:@docs/engineering/code-style.md
- 生成代码时必须符合上述规范文件里的所有约束
```
**Step 2**:新建 Cursor Chat,测试 AI 是否会读取规范文件
```plain
帮我在 UserService 里新增一个 findByEmail 方法,
根据 email 查找用户,找不到时抛出异常
```
**Step 3**:检查生成的代码是否符合 `code-style.md` 里的规范:
- 是否有 JSDoc 注释(规范要求)
- 方法名是否是 camelCase
- 是否用了 NestJS 异常类(不是 `throw new Error`)
- 是否有 `any` 类型(不应该有)
---
## 六、规范的维护原则
### 第一版要简单
第一次建立规范,只写最核心的 10 条,不要写 50 条。
没人看完 50 条的规范,AI 也可能因为文件过长而忽略后半段。
### 问题驱动追加
每次发现 AI 生成了不符合预期的代码,问一下:
1. 这个问题规范里有没有提到?
2. 如果没有,追加对应的约束
3. 如果有,可能是约束写得不够精确,改得更具体
### 定期回顾
每个月回顾一次规范文件:
- 删掉不再适用的条目
- 合并重复或相似的条目
- 给模糊的条目补充示例
### 团队一起维护
规范文件提交到 Git,团队成员都可以提 PR 修改。
修改规范的 PR 需要至少 2 个人 Approve,避免个人主观偏好主导规范。
---
## Spec Coding 实战补充:11 VibeCoding 工程规范
> 来源:`Spec Coding实战/11 VibeCoding 工程规范.md`,已合并到本章节。
### 1. 为什么 VibeCoding 需要工程规范
VibeCoding 降低了写代码的门槛,但同时也带来了一个新问题:**每个人(包括 AI)生成代码的风格都不一样**。
没有规范的 AI 辅助开发团队,代码库会快速退化成"每个模块像不同的人写的,风格各异,无法维护"。
工程规范解决的是:**让团队里所有人(以及 AI)生成的代码,看起来像是同一个人写的。**
这不是强迫症,这是可维护性。
---
### 2. 规范文件的组织结构
```plain
project-standards/
├── README.md # 规范总览,新人必读
├── code-style.md # 代码风格规范
├── directory-structure.md # 目录结构规范
├── commit-guide.md # 提交规范
├── pr-template.md # PR 模板
├── api-design.md # 接口设计规范
└── testing-guide.md # 测试规范
# 项目根目录的配套文件
.cursorrules # AI 工具规范
.eslintrc.js / eslint.config.js
prettier.config.js
.commitlintrc.js
.husky/
pre-commit
commit-msg
```
---
### 3. 目录结构规范
#### 后端(NestJS)
```plain
src/
├── common/ # 全局公共基础设施
│ ├── constants/ # 常量、枚举、错误码
│ │ ├── error-code.ts
│ │ └── app.constants.ts
│ ├── decorators/ # 自定义装饰器
│ │ ├── current-user.decorator.ts
│ │ └── roles.decorator.ts
│ ├── dto/ # 公共 DTO(分页等)
│ │ └── page-query.dto.ts
│ ├── exceptions/ # 自定义异常
│ │ └── business.exception.ts
│ ├── filters/ # 全局 Filter
│ │ └── all-exceptions.filter.ts
│ ├── guards/ # 全局 Guard
│ │ └── jwt-auth.guard.ts
│ ├── interceptors/ # 全局 Interceptor
│ │ └── transform.interceptor.ts
│ └── utils/ # 工具函数(纯函数,无副作用)
│ └── pagination.util.ts
├── config/ # 配置模块
│ └── configuration.ts
├── modules/ # 业务模块(每个模块独立目录)
│ └── [feature-name]/
│ ├── [feature].module.ts
│ ├── [feature].controller.ts
│ ├── [feature].service.ts
│ ├── [feature].service.spec.ts
│ └── dto/
│ ├── create-[feature].dto.ts
│ ├── update-[feature].dto.ts
│ └── query-[feature].dto.ts
└── prisma/
├── prisma.module.ts
└── prisma.service.ts
```
##### 规则
- 每个业务模块在 `modules/` 下独立目录,禁止跨模块直接引用 Service(通过 Module 导出)
- `common/` 只放真正通用的东西,业务逻辑不进 common
- 文件命名全部 kebab-case,与类名用 PascalCase 一一对应
---
#### 前端(Vue3)
```plain
src/
├── api/ # 接口层
│ ├── request.ts # axios 实例(统一拦截器)
│ ├── user.api.ts # 用户模块接口(文件名 = 模块.api.ts)
│ └── types/ # 接口类型定义(与后端 DTO 对齐)
│ └── user.types.ts
├── assets/ # 静态资源(图片、字体、图标)
├── components/ # 可复用组件(与业务无关)
│ └── [ComponentName]/
│ └── index.vue
├── composables/ # 组合式函数(use 前缀)
│ ├── usePageQuery.ts
│ └── useRequest.ts
├── layouts/ # 布局组件
│ ├── DefaultLayout.vue
│ └── AuthLayout.vue
├── router/
│ └── index.ts
├── stores/ # Pinia store(use + 名词 + Store)
│ └── useUserStore.ts
├── styles/ # 全局样式
│ ├── variables.scss # SCSS 变量
│ └── global.scss # 全局样式(慎用)
├── utils/ # 工具函数
│ └── format.ts
└── views/ # 页面级组件(按模块分目录)
└── user/
├── UserList.vue
└── UserDetail.vue
```
---
### 4. 代码风格规范
#### prettier.config.js
```javascript
// prettier.config.js
module.exports = {
semi: false, // 无分号
singleQuote: true, // 单引号
tabWidth: 2, // 2 空格
printWidth: 100, // 每行最大 100 字符
trailingComma: 'all', // 多行时末尾逗号
bracketSpacing: true, // 对象括号内空格 { a: 1 }
arrowParens: 'always', // 箭头函数参数始终加括号 (x) => x
endOfLine: 'lf', // 换行符 LF(跨平台一致)
}
```
#### eslint.config.js(ESLint v9 flat config)
```javascript
// eslint.config.js
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import prettierConfig from 'eslint-config-prettier'
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
prettierConfig,
{
rules: {
'@typescript-eslint/no-explicit-any': 'error', // 禁止 any
'@typescript-eslint/no-unused-vars': 'error', // 禁止未使用变量
'@typescript-eslint/explicit-function-return-type': 'warn', // 函数返回类型
'no-console': 'warn', // 提醒用 logger
'prefer-const': 'error', // 优先 const
},
}
)
```
#### 命名规范速查
```plain
文件名: kebab-case user-profile.service.ts
类名: PascalCase UserProfileService
接口/类型: PascalCase CreateUserDto, UserStatus
变量/函数: camelCase getUserById, currentUser
常量: UPPER_SNAKE_CASE JWT_SECRET, MAX_PAGE_SIZE
枚举: PascalCase OrderStatus
枚举成员: UPPER_SNAKE_CASE OrderStatus.PENDING
CSS 类名: BEM .user-card__avatar--active
数据库表名: snake_case order_items
数据库列名: snake_case created_at, is_deleted
```
---
### 5. 提交规范(Conventional Commits)
#### commit-guide.md
格式:`type(scope): description`
```plain
feat(user): add email verification on registration
fix(order): fix N+1 query in findAll method
refactor(auth): extract token validation to guard
docs(api): update user module swagger annotations
test(user): add edge case tests for create method
chore(deps): upgrade prisma to 5.10.0
perf(product): add index on product.status field
```
##### type 枚举
| type | 用途 |
| ---------- | -------------------------- |
| `feat` | 新功能 |
| `fix` | Bug 修复 |
| `refactor` | 重构(不改功能,不修 bug) |
| `docs` | 文档更新 |
| `test` | 测试相关 |
| `chore` | 构建、依赖、配置等 |
| `perf` | 性能优化 |
| `style` | 代码格式(不影响逻辑) |
| `ci` | CI/CD 配置 |
###### scope 约定
- 用模块名:`user`、`order`、`auth`、`product`
- 用基础设施层:`prisma`、`config`、`common`
- 全局改动用 `*`:`chore(*): update eslint config`
#### .commitlintrc.js
```javascript
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'refactor',
'docs',
'test',
'chore',
'perf',
'style',
'ci',
],
],
'subject-max-length': [2, 'always', 100],
'subject-case': [0], // 不强制大小写,中文友好
},
}
```
---
### 6. Git Hooks(Husky v9 配置)
```bash
# 安装
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional
# 初始化
npx husky init
```
#### .husky/pre-commit
```plain
#!/bin/sh
npx lint-staged
```
##### .husky/commit-msg
```plain
#!/bin/sh
npx commitlint --edit $1
```
###### package.json 中的 lint-staged 配置
```json
{
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md,yml}": ["prettier --write"]
}
}
```
这样每次 `git commit`:
1. `pre-commit`:对暂存文件跑 ESLint + Prettier
2. `commit-msg`:验证 commit message 格式
不符合规范的提交直接被拒绝,不需要人工 Review 代码风格。
---
### 7. PR 模板
```markdown
<!-- .github/pull_request_template.md -->
## 改动说明
<!-- 这次 PR 做了什么,为什么要做 -->
## 改动类型
- [ ] feat:新功能
- [ ] fix:Bug 修复
- [ ] refactor:重构
- [ ] docs:文档
- [ ] test:测试
- [ ] chore:配置/依赖
## 关联 Issue
closes #
## 测试说明
<!-- 如何验证这次改动是正确的 -->
- [ ] 单元测试通过(npm test)
- [ ] 手动测试通过(描述测试步骤)
- [ ] 新功能有对应的测试用例
## 数据库变更
- [ ] 无数据库变更
- [ ] 有新的 Migration(已在本地验证 migrate deploy 能成功)
## 需要特别注意
<!-- Review 时需要重点关注的地方,或者有意为之但看起来奇怪的地方 -->
## 截图(如有)
```
---
### 8. 团队协作与知识沉淀
#### 把规范纳入 AI 工具链
所有规范文件要和 `.cursorrules` 联动。`.cursorrules` 是规范的执行层,`project-standards/` 是规范的说明层。
当规范更新时,两者同步更新:
```bash
# 规范更新后的提交流程
1. 更新 project-standards/xxx.md(人可读的说明)
2. 更新 .cursorrules(AI 执行的约束)
3. git commit -m "docs(*): update naming convention for stores"
```
#### 规范的生命周期
规范不是写完就不变的。随着项目演进,要定期回顾:
- 每个 Sprint 结束时,回顾本轮 Code Review 中频繁出现的问题,新问题加进规范
- 技术栈升级时(如 Prisma 大版本升级),同步更新规范里的使用示例
- 新人入职时,让新人读规范并提问,这是发现规范不清楚地方的好时机
#### 用 AI 生成规范文档
```plain
读取 @src/modules/user/ 目录下的所有文件,
分析这个团队当前实际遵循的代码风格和组织方式,
生成一份 user-module-convention.md 文档,
描述:DTO 的写法、Service 的结构、Controller 的惯例、错误处理的方式。
这份文档将作为新团队成员了解项目规范的参考。
```
---
### 9. 小结
工程规范的本质是**把最佳实践变成默认行为**。
好的规范体系有三层:
- **工具层**(ESLint、Prettier、Husky):自动格式化,自动检查,无法绕过
- **约束层**(.cursorrules):告诉 AI 应该怎么写
- **说明层**(project-standards/):告诉人为什么这么写
三层配合,规范才能真正落地,而不是挂在墙上没人看的文档。