ִ 这是个很值得深想的问题。AI 辅助开发的"屎山"问题本质上是一个治理问题,不是 AI 本身的问题——AI 是放大器,好的治理被放大变得更好,混乱的治理被放大变成更快速的熵增。
下面从几个层次系统性地拆解:
一、根基层:建立 AI 可读的「项目宪法」
这是最重要、最被忽视的一层。AI 每次进入项目都是"新来的",如果没有一份清晰的项目约定文件,它会根据它对"一般最佳实践"的理解来生成代码,而不是你们团队的最佳实践。
CLAUDE.md / AGENTS.md(项目级 AI 说明书)
# 项目架构约定
- 状态管理:只用 Zustand,不引入 Redux/Jotai
- 样式方案:Tailwind utility-first,禁止写内联 style
- 数据获取:统一用 React Query,不直接用 useEffect 发请求
- 组件类型:UI组件在 /components/ui,业务组件在 /components/features
- 禁止事项:不允许使用 any,不允许直接操作 DOM
# 当前技术债务(AI 不要动这些文件)
- /legacy/payment 模块正在重构中,不要修改
这个文件的价值是:所有 AI 工具(Claude、Cursor、Copilot)都会优先读取它,让 AI 的每次输出都在你的约定范围内。
关键治理动作:
- 把架构决策记录(ADR, Architecture Decision Record)写成 Markdown 放进仓库,让 AI 能检索到"为什么这样设计"
- 在
.cursor/rules或.claude/目录下按功能域拆分规则文件,避免一个巨型文件
二、类型层:TypeScript 是 AI Friendly 的地基
类型系统是 AI 和人类开发者共同依赖的「合约」。类型越严格,AI 产出的代码越准确,越不容易引入运行时错误。
具体要做的事:
- 开启严格模式,不妥协:
"strict": true+"noUncheckedIndexedAccess": true。宽松的类型配置会让 AI 生成"能跑但不安全"的代码。 - 所有 API 响应类型化:用 Zod / valibot 在运行时校验 + 推导类型,而不是手写 interface 然后靠 as 强转。AI 理解 Zod schema 的语义能力远强于理解注释。
// AI 不友好:靠注释约定
const user = response.data as User // hope it's correct
// AI 友好:运行时 + 编译时双重保障
const user = UserSchema.parse(response.data)
- 用 branded types 表达业务约束:让类型携带业务语义,AI 就不会混淆
UserId和OrderId。
type UserId = string & { readonly __brand: 'UserId' }
type OrderId = string & { readonly __brand: 'OrderId' }
- 函数签名要表达意图:返回类型显式标注,参数用 options object 而非位置参数(AI 不会搞错参数顺序)。
三、架构层:让 AI 能「安全地局部推理」
屎山代码最大的特征是:牵一发而动全身。AI 改一个地方,不知道影响了哪里。好的架构应该让每个模块可以被独立理解和修改。
边界清晰的模块化(Feature-Sliced Design 或类似思想)
src/
features/
user-auth/
api.ts # 只有这里能调用 /auth/* 接口
store.ts # 只有这里管理 auth 状态
components/ # 只有 user-auth 用的组件
types.ts # 只有这里的类型
index.ts # 对外暴露的公共 API
shared/
ui/ # 纯展示组件,无业务逻辑
lib/ # 通用工具函数
这种结构的 AI Friendly 价值:AI 在处理某个 feature 时,只需要加载这个 feature 目录下的文件就能完整理解上下文,不需要全局扫描。
禁止跨层依赖(用 ESLint 规则强制执行)
// eslint-plugin-boundaries 或 eslint-plugin-import 配置
"boundaries/no-unknown-imports": "error"
// features 不能互相引用,只能通过 shared 通信
副作用隔离:数据获取、DOM 操作、全局状态修改都要集中在明确的地方,纯函数写业务逻辑。AI 对纯函数的理解和改写准确率远高于充满副作用的代码。
四、契约层:用 Schema 驱动前后端协作
这一层是防止 AI 生成"前后端对不上"代码的关键。
OpenAPI / tRPC / GraphQL 作为单一真相来源
后端定义 OpenAPI spec
↓
openapi-typescript 自动生成前端类型
↓
AI 生成业务代码时,类型已经正确
↓
即使 AI 不知道后端细节,也不会写出类型错误的调用
具体工具链:
openapi-typescript:从 OpenAPI 生成 TypeScript 类型,接口变更时自动同步orval/hey-api:进一步生成 React Query hooks,连数据获取层都不需要手写msw(Mock Service Worker) :基于同一份 schema 生成 mock,AI 写测试时有准确的 mock 数据
这样做之后,AI 就算不知道接口文档,也能从类型推断出正确的调用方式。
五、组件层:建立可被 AI 正确使用的 Design System
这是大前端团队最有杠杆效应的投入点。
组件要"自文档化"
interface ButtonProps {
/**
* 按钮的视觉层级
* - primary: 页面主操作,每个视图最多一个
* - secondary: 次要操作
* - ghost: 不需要强调的操作,如取消
*/
variant: 'primary' | 'secondary' | 'ghost'
/**
* 加载状态:显示 spinner 并禁用点击
* 异步操作期间必须设为 true,防止重复提交
*/
loading?: boolean
}
丰富的 JSDoc 注释让 AI 在使用组件时不只是知道有这个 prop,还知道什么场景用什么值。
Storybook + storybook-addon-docs
每个组件有完整的使用示例,AI 在生成业务页面时可以参考正确的用法,而不是凭空猜测组件 API。更重要的是,AI 被 Cursor 等工具索引时会优先读 stories 文件。
用 component-index.ts 明确暴露边界
// ui/index.ts —— AI 只应该从这里引入组件
export { Button } from './button'
export { Modal } from './modal'
// 没有 export 的内部实现文件,AI 不应该直接引用
六、测试层:让 AI 的每次修改都有安全网
没有测试,AI 改代码就像在黑暗中走路。有了测试,AI 不仅能改代码,还能自我验证。
测试策略(按 AI Friendly 优先级排序)
- 类型测试(零运行成本,最高性价比):用
tsd或expect-type确保泛型和条件类型正确 - 单元测试:纯函数、工具函数、业务逻辑——AI 改这些时能立刻验证
- 组件测试(Testing Library) :测用户行为,不测实现细节(实现细节会随 AI 重构变化)
- 契约测试(Pact) :验证前后端接口约定不被破坏
关键原则:测试要测行为,不测实现。测"用户点击提交按钮后,表单被清空",而不是"某个 setState 被调用"。这样 AI 重构内部实现时,测试不会因为内部细节变化而大面积失败。
在 CI 中加入 AI 感知的门禁:
# .github/workflows/ai-guard.yml
- name: Type Coverage Check
run: npx type-coverage --at-least 95 # 类型覆盖率不低于 95%
- name: Circular Dependency Check
run: npx madge --circular src/ # AI 引入循环依赖立刻报错
- name: Bundle Size Guard
run: npx size-limit # AI 引入大依赖立刻发现
七、防腐层:阻止 AI 引入「熵增」的 CI 规则
这是防止屎山代码最重要的工程机制——把约定变成机器强制执行的规则,AI 生成的代码也必须通过。
ESLint 规则集(团队共建,持续维护)
// 防止 AI 引入错误模式的规则
rules: {
'no-restricted-imports': ['error', {
patterns: ['lodash', '!lodash-es'], // 只允许 tree-shakable 版本
}],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-non-null-assertion': 'warn',
'import/no-cycle': 'error', // 禁止循环依赖
'react-hooks/exhaustive-deps': 'error', // AI 经常漏写依赖
}
Prettier + EditorConfig:统一格式化,AI 生成的代码进入仓库前自动格式化,不产生格式噪音。
Husky + lint-staged:在 commit 时自动跑类型检查和 lint,AI 生成的代码在进入仓库前就被校验。
八、文档层:让团队知识对 AI 可索引
决策日志(ADR)
# ADR-007: 选择 Zustand 而非 Redux Toolkit
## 背景
2024年Q3评估状态管理方案...
## 决策
选择 Zustand,原因:
1. 样板代码少 70%,AI 生成代码更简洁
2. 不需要 Provider 包裹,减少组件树复杂度
## 后果
- 禁止在新代码中引入 Redux(见 eslint no-restricted-imports)
- 现有 Redux 代码在 /legacy 目录,计划 2025Q1 迁移完毕
这类文件放在仓库里,AI 在修改相关代码时能检索到,不会"好心"引入你们已经决定废弃的方案。
/docs/conventions/ 目录,按主题拆分:
api-calling.md:如何调用接口error-handling.md:错误处理规范performance.md:性能约定(哪些操作必须做懒加载)
总结:建设优先级路线图
第一阶段(治理基础)
├── CLAUDE.md / AGENTS.md —— 给 AI 立规矩
├── TypeScript strict mode —— 类型地基
└── ESLint + Husky 门禁 —— 机器强制执行约定
第二阶段(结构清晰)
├── Feature-Sliced 或类似模块化架构
├── OpenAPI → 自动生成类型和 hooks
└── Design System 组件自文档化
第三阶段(测试安全网)
├── 行为测试(而非实现测试)
├── 契约测试
└── CI 质量门禁(类型覆盖率、包大小、循环依赖)
第四阶段(知识沉淀)
├── ADR 决策日志
├── 分域约定文档
└── Storybook 组件使用示例
核心思想只有一句话:把团队的集体智慧编码进机器可执行的规则和 AI 可读的文档里,而不是依赖"大家都知道"的口头约定。 AI 时代,口头约定会被 AI 无视;机器约束和文档约定会被 AI 遵守。