写给前端开发者、前端架构师和大前端团队的系统性指南
前言:为什么你的 AI 辅助开发正在制造屎山
2024 年至今,几乎所有前端团队都引入了 AI 编码工具。但大多数团队正在经历一个隐性问题:AI 写的代码越来越多,代码库反而越来越难维护。
原因不在 AI,在于治理缺失。
AI 是放大器。在有清晰约定的代码库里,AI 生成的代码质量出奇地高;在混乱的代码库里,AI 只会更快地把混乱复制和扩散。随着时间推移,没有治理的 AI 辅助开发,会比纯人工开发更快速地产生技术债。
本文要解决的问题:如何系统性地建设"AI Friendly"的前端工程体系,让 AI 成为团队质量的放大器,而不是熵增的加速器。
一、AI Friendly 这个概念从哪里来
"AI Friendly"并非某篇论文或某个人的单一发明,它是几条技术演化线索的交汇。
从 Machine Readable 到 AI Friendly
早期互联网时代,工程师们已经在讨论两种不同的可读性:human readable(语义化 HTML、SEO 友好的内容结构)和 machine readable(结构化数据、schema.org 标注、RSS Feed)。
AI Friendly 是这一思路在大语言模型时代的升级版——不只是让爬虫能解析页面结构,而是让语言模型能理解代码意图、正确推理业务逻辑、准确调用系统接口。
几个关键节点
2023 年:Function Calling 的普及。OpenAI 推出 Function Calling,Anthropic 推出 Tool Use,AI 从"问答机器"变成了"能执行操作的 Agent"。这直接催生了一个新问题:怎样的 API 设计、文档结构才能让 AI Agent 正确理解和使用?
2023-2024 年:Karpathy 的 LLM OS 视角。Andrej Karpathy 多次提出 LLM 正在成为操作系统内核的类比——文件系统、API、开发工具都将被 LLM 调度。这把"如何让系统被 AI 友好使用"提升到了架构层面的命题。
2024 年底:MCP 协议的出现。Anthropic 发布 Model Context Protocol(MCP),明确定义了 AI 与外部系统交互的标准协议。这是"AI Friendly"在工程规范层面最具体的落地,也标志着这一概念从讨论走向了工业标准。
2024-2025 年:llms.txt 约定兴起。类比 robots.txt,越来越多网站在根目录放置 llms.txt,用简洁的 Markdown 描述产品是什么、核心文档在哪里,让 AI 能高效理解和索引站点内容。Anthropic、Vercel 等公司已率先实践。
核心本质
AI Friendly 的本质是减少歧义。 语义清晰的命名、结构化的接口、完整的类型系统和可检索的文档——让 AI 在更少幻觉的前提下正确理解和操作你的系统。
这和"对人友好的好工程"高度重合,只是对标准的要求更为严格:人类开发者可以通过沟通消除歧义,但 AI 只能依赖代码本身携带的信息。
二、大前端团队的 AI Friendly 体系:八个层次
层次一:项目宪法层 —— 给 AI 立规矩
这是最重要、也最容易被忽视的一层。
AI 工具(Claude、Cursor、GitHub Copilot)每次进入项目都是"新来的成员"。如果没有一份清晰的项目约定文件,它会根据自己对"一般最佳实践"的理解来生成代码,而不是你们团队的约定。结果是:代码能跑,但风格各异,技术选型混乱,三个月后没人认得出是同一个项目。
核心交付物:CLAUDE.md / AGENTS.md
在项目根目录维护一份 AI 说明书,主流 AI 工具(Claude Code、Cursor、Windsurf)都会优先读取它:
# 项目 AI 协作说明
## 技术栈约定
- 状态管理:Zustand。禁止引入 Redux、Jotai、Recoil
- 样式方案:Tailwind CSS utility-first。禁止写内联 style 和 CSS Modules
- 数据获取:React Query(TanStack Query)。禁止在 useEffect 中直接发起请求
- 表单:React Hook Form + Zod。禁止使用 Formik
- HTTP 客户端:项目封装的 `src/lib/http.ts`,禁止直接使用 axios/fetch
## 目录结构约定
- UI 纯展示组件:`src/components/ui/`
- 业务组件:`src/components/features/`
- 页面级组件:`src/pages/` 或 `src/app/`(Next.js App Router)
- 自定义 hooks:`src/hooks/`,命名必须以 use 开头
- 类型定义:`src/types/`,每个模块一个文件
## 命名规范
- 组件文件:PascalCase(UserProfile.tsx)
- 工具函数文件:camelCase(formatDate.ts)
- 常量文件:SCREAMING_SNAKE_CASE(API_ENDPOINTS.ts)
- 事件处理函数:handle 前缀(handleSubmit、handleClose)
## 禁止事项(AI 不允许做)
- 不允许使用 `any` 类型
- 不允许使用非空断言 `!`(确有必要时须注释说明)
- 不允许直接操作 DOM(使用 ref)
- 不允许在组件外部修改共享状态
## 当前技术债务区域(不要修改)
- `src/legacy/payment/`:正在重构,不要碰
- `src/utils/oldHelpers.ts`:历史遗留,新代码不要引用,逐步替换
最佳实践:按功能域拆分规则文件
大型项目中,不要把所有规则堆在一个文件里。用目录结构管理:
.claude/
CLAUDE.md # 全局规则
rules/
components.md # 组件开发规范
api.md # 接口调用规范
testing.md # 测试规范
performance.md # 性能约定
架构决策记录(ADR)
每一个重要的技术选型决策都应该有书面记录,放进仓库让 AI 可以检索:
# ADR-007: 选择 Zustand 替代 Redux Toolkit
## 状态:已接受(2024-03)
## 背景
团队评估了 Redux Toolkit、Zustand、Jotai 三种方案...
## 决策
选择 Zustand,核心原因:
1. 样板代码减少约 70%,AI 辅助生成时代码更简洁
2. 无需 Provider 包裹,组件树更扁平
3. TypeScript 支持开箱即用
## 约束
- 禁止在新代码中引入 Redux(ESLint 规则已配置)
- 现有 Redux 代码在 `/legacy`,计划 2025Q1 迁移完毕
层次二:类型系统层 —— AI 的理解地基
TypeScript 类型系统是 AI 和人类开发者共同依赖的「合约」。类型越严格,AI 产出越准确,越不容易引入运行时错误。
开启严格模式,不妥协
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true, // 数组/对象访问返回 T | undefined
"exactOptionalPropertyTypes": true, // 区分 undefined 和缺失属性
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true
}
}
宽松的类型配置会让 AI 生成"能跑但不安全"的代码,随着时间积累,any 和类型断言会像霉菌一样蔓延。
用 Zod 实现运行时 + 编译时双重校验
// ❌ AI 不友好:靠注释约定,靠 as 强转
const user = response.data as User // hope it matches
// ✅ AI 友好:Schema 即文档,运行时保障
import { z } from 'zod'
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
role: z.enum(['admin', 'editor', 'viewer']),
createdAt: z.string().datetime(),
})
type User = z.infer<typeof UserSchema>
const user = UserSchema.parse(response.data)
// 类型自动推导,解析失败时抛出详细错误
AI 理解 Zod schema 的语义能力远强于理解纯注释,因为 schema 本身就是可执行的约束。
Branded Types 表达业务语义
// 防止 AI 混淆不同语义的 string
type UserId = string & { readonly __brand: 'UserId' }
type OrderId = string & { readonly __brand: 'OrderId' }
type ProductId = string & { readonly __brand: 'ProductId' }
// 工厂函数
const toUserId = (id: string): UserId => id as UserId
// 现在 AI 无法把 OrderId 误传给需要 UserId 的函数
function getUser(id: UserId): Promise<User> { ... }
getUser(orderId) // ❌ 编译时报错,AI 生成代码时就会发现
函数签名最佳实践
// ❌ 位置参数:AI 容易搞错顺序
function createUser(name: string, email: string, role: string, active: boolean) {}
// ✅ Options Object:语义清晰,顺序无关
interface CreateUserOptions {
/** 用户显示名称,1-50 字符 */
name: string
/** 登录邮箱,全局唯一 */
email: string
/** 权限角色,默认 viewer */
role?: 'admin' | 'editor' | 'viewer'
/** 初始激活状态,默认 true */
active?: boolean
}
function createUser(options: CreateUserOptions): Promise<User> {}
类型覆盖率监控
# 在 CI 中加入类型覆盖率检查
npx type-coverage --at-least 95 --strict
# 生成报告
npx type-coverage --detail
层次三:架构层 —— 让 AI 安全地局部推理
屎山代码最大的特征是牵一发而动全身。AI 改一个地方,不知道影响了哪里。好的架构应该让每个模块能被独立理解和安全修改。
Feature-Sliced Design(FSD)
FSD 是目前对 AI 最友好的前端架构模式,其核心价值是:AI 在处理某个 feature 时,只需要加载这个 feature 目录下的文件就能完整理解上下文:
src/
├── app/ # 应用初始化、路由、全局 Provider
├── pages/ # 页面组合层,薄层,只做组合
├── widgets/ # 页面级独立区块(如 Header、Sidebar)
├── features/ # 业务功能单元
│ ├── user-auth/
│ │ ├── api.ts # 只有这里调用 /auth/* 接口
│ │ ├── model/ # 状态、业务逻辑
│ │ │ ├── store.ts
│ │ │ └── selectors.ts
│ │ ├── ui/ # 该 feature 专属组件
│ │ │ ├── LoginForm.tsx
│ │ │ └── LogoutButton.tsx
│ │ ├── types.ts
│ │ └── index.ts # 公共 API,唯一出口
│ └── order-management/
│ └── ...
├── entities/ # 业务实体(User、Order、Product)
│ └── user/
│ ├── api.ts
│ ├── model.ts
│ ├── ui/
│ └── index.ts
└── shared/ # 纯通用层,无业务逻辑
├── ui/ # 基础 UI 组件
├── lib/ # 工具函数
├── api/ # HTTP 客户端封装
└── config/ # 环境变量、常量
用 ESLint 强制层级约束
// eslint.config.js - 使用 eslint-plugin-boundaries
import boundaries from 'eslint-plugin-boundaries'
export default [
{
plugins: { boundaries },
rules: {
'boundaries/element-types': ['error', {
default: 'disallow',
rules: [
// pages 可以引用 widgets、features、entities、shared
{ from: 'pages', allow: ['widgets', 'features', 'entities', 'shared'] },
// features 只能引用 entities 和 shared,不能互相引用
{ from: 'features', allow: ['entities', 'shared'] },
// entities 只能引用 shared
{ from: 'entities', allow: ['shared'] },
// shared 不能引用任何业务层
{ from: 'shared', allow: ['shared'] },
]
}]
}
}
]
禁止循环依赖
# 安装
npm install --save-dev madge
# CI 检查
npx madge --circular --extensions ts,tsx src/
副作用集中管理
// ✅ 纯函数处理业务逻辑,AI 最容易正确理解和修改
function calculateOrderTotal(items: OrderItem[], discount: Discount): number {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0)
return applyDiscount(subtotal, discount)
}
// 副作用(网络请求、状态修改)统一在 store 的 action 中
const useOrderStore = create<OrderStore>((set, get) => ({
submitOrder: async (items) => {
set({ isSubmitting: true })
try {
const total = calculateOrderTotal(items, get().discount) // 调用纯函数
const order = await orderApi.create({ items, total })
set({ currentOrder: order, isSubmitting: false })
} catch (error) {
set({ error: toAppError(error), isSubmitting: false })
}
}
}))
层次四:契约层 —— Schema 驱动,消灭前后端歧义
这一层是防止 AI 生成"前后端对不上"代码的关键机制。
OpenAPI → 自动生成类型链路
后端维护 OpenAPI Spec(openapi.yaml)
↓
openapi-typescript 自动生成前端类型
↓
orval / hey-api 生成 React Query hooks
↓
AI 生成业务代码时,类型已经正确
无需手写接口类型,无需对照文档
配置示例:
// orval.config.ts
import { defineConfig } from 'orval'
export default defineConfig({
petstore: {
input: './openapi.yaml',
output: {
mode: 'tags-split', // 按 tag 分割文件
target: './src/api/generated',
schemas: './src/types/api',
client: 'react-query', // 直接生成 React Query hooks
override: {
mutator: {
path: './src/lib/http.ts', // 使用项目统一的 HTTP 客户端
name: 'customInstance',
},
},
},
},
})
生成结果让 AI 可以直接使用,无需猜测接口参数:
// 自动生成,AI 直接调用即可
export const useGetUserById = (userId: string) => {
return useQuery({
queryKey: ['user', userId],
queryFn: () => getUserById(userId), // 类型完全正确
})
}
MSW 基于同一份 Schema 生成 Mock
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw'
import { UserSchema } from '@/types/api'
export const handlers = [
http.get('/api/users/:id', ({ params }) => {
// 基于 Zod schema 生成符合类型的 mock 数据
return HttpResponse.json(
UserSchema.parse({
id: params.id,
name: 'Test User',
role: 'editor',
createdAt: new Date().toISOString(),
})
)
}),
]
AI 写测试时有类型正确的 mock 数据,不再靠猜测。
llms.txt —— 让外部 AI 理解你的产品
如果你们在做面向开发者的产品或文档站,在根目录放置 llms.txt:
# Acme Design System
> 企业级 React 组件库,基于 Tailwind CSS
## 文档
- [快速开始](/docs/getting-started)
- [组件列表](/docs/components)
- [设计令牌](/docs/tokens)
- [Changelog](/CHANGELOG.md)
## 关键约定
- 所有组件支持 `className` prop 用于样式扩展
- 主题通过 CSS 变量配置,见 /docs/theming
- 组件默认不包含业务逻辑,所有状态由调用方管理
层次五:组件层 —— Design System 的 AI Friendly 建设
Design System 是大前端团队杠杆效应最高的投资点。
组件要"自文档化"
JSDoc 注释是组件的第一层文档,AI 在使用组件时会优先读取:
interface ButtonProps {
/**
* 按钮的视觉层级
* - `primary`:页面主操作,每个视图最多出现一次
* - `secondary`:次要操作,可多个并列
* - `ghost`:低强调操作,如「取消」、「返回」
* - `danger`:破坏性操作,如删除、清空
* @default 'secondary'
*/
variant?: 'primary' | 'secondary' | 'ghost' | 'danger'
/**
* 加载状态:展示 spinner 并禁用点击
* 异步操作(如表单提交、数据加载)期间必须设为 true,防止重复触发
* @example
* const [loading, setLoading] = useState(false)
* <Button loading={loading} onClick={handleSubmit}>提交</Button>
*/
loading?: boolean
/**
* 图标位置
* 仅传入 icon 时渲染为图标按钮(正方形),同时传入 children 时图标为前缀
*/
icon?: React.ReactNode
iconPosition?: 'left' | 'right'
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void
disabled?: boolean
type?: 'button' | 'submit' | 'reset'
children?: React.ReactNode
className?: string
}
Storybook 作为组件的完整使用示例
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'
const meta: Meta<typeof Button> = {
title: 'UI/Button',
component: Button,
// AI 通过 argTypes 理解每个 prop 的取值范围
argTypes: {
variant: {
description: '按钮视觉层级,影响颜色和强调程度',
control: { type: 'radio' },
options: ['primary', 'secondary', 'ghost', 'danger'],
},
},
}
export default meta
type Story = StoryObj<typeof Button>
// 每个用例都是一个 AI 可参考的完整示例
export const Primary: Story = {
args: { variant: 'primary', children: '立即购买' }
}
export const LoadingState: Story = {
args: { variant: 'primary', loading: true, children: '提交中...' }
}
export const WithIcon: Story = {
args: {
variant: 'secondary',
icon: <DownloadIcon />,
children: '导出报表',
}
}
// 反例说明:用文档告诉 AI 什么不该做
export const DangerZone: Story = {
args: { variant: 'danger', children: '删除账户' },
parameters: {
docs: {
description: {
story: '⚠️ danger 变体仅用于不可逆操作,必须配合二次确认弹窗使用'
}
}
}
}
明确的公共 API 边界
// src/components/ui/index.ts
// 这是 AI 唯一应该从 ui 目录引入内容的入口
// 没有 export 的内部实现文件,AI 不应该直接引用
export { Button } from './button/Button'
export type { ButtonProps } from './button/Button'
export { Modal, ModalHeader, ModalBody, ModalFooter } from './modal/Modal'
export type { ModalProps } from './modal/Modal'
export { DataTable } from './data-table/DataTable'
export type { DataTableProps, ColumnDef } from './data-table/DataTable'
// 明确标注废弃项,AI 不会使用已废弃的组件
/** @deprecated 使用 Modal 替代,将在 v3.0 移除 */
export { Dialog } from './dialog/Dialog'
组件健康度检查(CI 集成)
# 检查 Storybook 所有 stories 能否正常构建
npx storybook build --test
# 检查是否有组件没有对应的 stories
# 自定义脚本:确保每个 UI 组件都有文档
层次六:测试层 —— AI 修改的安全网
没有测试,AI 改代码就像在黑暗中走路。有了完善的测试,AI 不仅能改代码,还能自我验证。
测试策略金字塔(AI Friendly 优化版)
/\
/ \ E2E Tests
/ \ 少量,覆盖核心用户流程
/------\
/ \ Integration Tests
/ \ 组件 + API 的集成测试
/------------\
/ \ Unit Tests
/ \ 纯函数、工具函数、业务逻辑
/------------------\
/ \ Type Tests
/____________________ \ 零运行成本,类型约束验证
核心原则:测行为,不测实现
// ❌ 测实现:AI 重构内部逻辑后,测试大面积失败
it('should call setState with isLoading: true', () => {
const setStateSpy = jest.spyOn(component, 'setState')
component.handleSubmit()
expect(setStateSpy).toHaveBeenCalledWith({ isLoading: true })
})
// ✅ 测行为:AI 怎么重构内部都不影响测试
it('提交表单时显示加载状态,成功后清空表单', async () => {
const user = userEvent.setup()
render(<LoginForm onSuccess={mockOnSuccess} />)
await user.type(screen.getByLabelText('邮箱'), 'test@example.com')
await user.type(screen.getByLabelText('密码'), 'password123')
await user.click(screen.getByRole('button', { name: '登录' }))
// 验证加载状态
expect(screen.getByRole('button', { name: '登录' })).toBeDisabled()
// 验证成功后行为
await waitFor(() => {
expect(mockOnSuccess).toHaveBeenCalled()
expect(screen.getByLabelText('邮箱')).toHaveValue('')
})
})
类型测试(零成本,高价值)
// src/types/__tests__/api.test-d.ts
import { expectType, expectError } from 'tsd'
import type { UserId, OrderId } from '@/types'
import { getUser } from '@/features/user-auth/api'
// 确保 branded types 不能互相替换
const userId = 'user-123' as UserId
const orderId = 'order-456' as OrderId
expectType<Promise<User>>(getUser(userId))
expectError(getUser(orderId)) // ✅ 应该报错
expectError(getUser('plain-string')) // ✅ 应该报错
契约测试(防止后端悄悄改接口)
// src/api/__tests__/user.contract.test.ts
import { pactWith } from 'jest-pact'
import { UserSchema } from '@/types/api'
pactWith({ consumer: 'Frontend', provider: 'UserService' }, (provider) => {
it('GET /users/:id 返回符合 UserSchema 的数据', async () => {
await provider.addInteraction({
state: 'user 123 exists',
uponReceiving: 'a request for user 123',
withRequest: { method: 'GET', path: '/users/123' },
willRespondWith: {
status: 200,
body: {
id: '123',
name: 'Test User',
role: 'editor',
},
},
})
const response = await fetch('/users/123')
const data = await response.json()
// 用 Zod schema 验证,接口变更时自动失败
expect(() => UserSchema.parse(data)).not.toThrow()
})
})
层次七:CI 防腐层 —— 机器强制执行,不依赖人的自律
这是防止 AI 引入"熵增"最重要的工程保障。把约定变成机器强制执行的规则,AI 生成的代码也必须通过所有门禁才能合入。
ESLint 配置(防止常见 AI 错误模式)
// eslint.config.js
export default [
{
rules: {
// 类型安全
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-non-null-assertion': 'warn',
'@typescript-eslint/no-unsafe-assignment': 'error',
// React 最佳实践(AI 经常犯这些错误)
'react-hooks/exhaustive-deps': 'error', // AI 经常漏写依赖
'react-hooks/rules-of-hooks': 'error',
// 禁止危险模式
'no-restricted-imports': ['error', {
patterns: [
// 强制使用 tree-shakable 版本
{ group: ['lodash'], message: '请使用 lodash-es' },
// 强制从 index 引入,不允许直接引用内部文件
{ group: ['@/features/*/model/*'], message: '请通过 feature 的 index.ts 引入' },
],
}],
// 防止循环依赖
'import/no-cycle': 'error',
// 包大小意识
'no-restricted-globals': ['error', {
name: 'moment',
message: '使用 date-fns 替代 moment,体积减少 97%',
}],
},
},
]
CI Pipeline 完整配置
# .github/workflows/quality-gate.yml
name: Quality Gate
on: [push, pull_request]
jobs:
type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: TypeScript 类型检查
run: npx tsc --noEmit
- name: 类型覆盖率(不低于 95%)
run: npx type-coverage --at-least 95 --strict
lint:
runs-on: ubuntu-latest
steps:
- name: ESLint
run: npx eslint . --max-warnings 0 # 警告也不允许
- name: 循环依赖检查
run: npx madge --circular --extensions ts,tsx src/
- name: 未使用的 exports
run: npx ts-prune
test:
runs-on: ubuntu-latest
steps:
- name: 单元测试 + 覆盖率
run: npx vitest run --coverage
- name: 覆盖率门禁(不低于 80%)
run: npx vitest run --coverage --coverage.thresholds.lines 80
bundle-analysis:
runs-on: ubuntu-latest
steps:
- name: 构建
run: npm run build
- name: 包大小检查
run: npx size-limit # 防止 AI 悄悄引入大依赖
- name: Bundle 可视化(PR 评论)
uses: github/actions/bundle-stats@v1
architecture:
runs-on: ubuntu-latest
steps:
- name: 架构层级约束检查
run: npx eslint . --rule '{"boundaries/element-types": "error"}'
Commit Message 规范(让 AI 生成有意义的提交记录)
# commitlint.config.js
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', // 新功能
'fix', // Bug 修复
'refactor',// 重构(不影响功能)
'perf', // 性能优化
'test', // 测试相关
'docs', // 文档
'chore', // 构建、依赖更新
'revert', // 回滚
]],
'scope-enum': [2, 'always', [
'user-auth', 'order', 'payment', 'shared', 'ui', 'ci',
]],
},
}
层次八:文档层 —— 团队知识的 AI 可索引化
/docs/conventions/ 目录结构
docs/
├── conventions/
│ ├── api-calling.md # 如何调用接口(含错误处理)
│ ├── error-handling.md # 错误处理规范
│ ├── state-management.md # 状态管理使用指南
│ ├── performance.md # 性能优化约定
│ └── accessibility.md # 无障碍要求
├── adr/ # 架构决策记录
│ ├── 001-monorepo.md
│ ├── 007-zustand.md
│ └── 012-react-query.md
└── runbooks/ # 操作手册
├── deploy.md
└── incident-response.md
让文档对 AI 友好的写作规范
<!-- ✅ AI 友好的文档格式 -->
# 错误处理规范
## 核心原则
所有网络请求错误必须通过 `useErrorHandler` hook 处理,禁止直接在组件中 catch 错误并 alert。
## 标准错误类型
| 错误类型 | HTTP 状态码 | 处理方式 |
|---------|-----------|---------|
| 认证失败 | 401 | 清除 token,跳转登录页 |
| 权限不足 | 403 | Toast 提示,不跳转 |
| 资源不存在 | 404 | 展示 404 组件 |
| 服务器错误 | 500 | Toast 提示 + 上报 Sentry |
## 代码示例
**✅ 正确做法**
```typescript
const { mutate: createOrder } = useMutation({
mutationFn: orderApi.create,
onError: (error) => {
// 使用统一错误处理
handleApiError(error, {
403: () => toast.error('您没有权限创建订单'),
default: () => toast.error('创建失败,请稍后重试'),
})
},
})
```
**❌ 错误做法**
```typescript
// 禁止:直接 alert,绕过统一错误处理
try {
await orderApi.create(data)
} catch (e) {
alert('Error: ' + e.message)
}
```
三、防止 AI 时代屎山代码的七个关键机制
在建设完上述体系后,还需要持续运营的机制来防止代码库退化:
1. 定期的架构健康度审查
每个 sprint 结束后,运行一次架构健康度报告:
- 循环依赖数量趋势
- 类型覆盖率趋势
any使用量趋势- 各 feature 的代码行数(警惕单个 feature 过度膨胀)
2. AI 生成代码的 Review Checklist
在 PR 模板中加入 AI 代码专项检查:
## AI 生成代码检查项(如适用)
- [ ] 新引入的依赖是否在 CLAUDE.md 允许范围内?
- [ ] 是否有 `any` 类型或类型断言需要说明原因?
- [ ] 测试是否覆盖核心用户行为?
- [ ] 是否遵循了目录结构约定?
- [ ] 是否有新的循环依赖(CI 会检查,但建议人工确认)?
3. 渐进式 ADR 更新
每当 AI 提出一个你们还没有明确约定的技术方案时,把决策结果写成 ADR 更新到仓库。不让口头约定存在——约定不进仓库就相当于不存在。
4. 组件使用率监控
# 定期检查哪些组件被大量直接复制而非复用
# 这通常说明 Design System 某个组件不够灵活,AI 选择了绕开它
npx ts-prune --ignore-tests | grep "component"
5. 依赖审计自动化
# .github/workflows/dependency-review.yml
- uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
# 阻止 AI 引入有安全漏洞的依赖
6. Bundle Size 变化追踪
// .size-limit.js
module.exports = [
{
path: 'dist/main.js',
limit: '200 kB', // AI 引入大依赖时立刻告警
},
{
path: 'dist/vendor.js',
limit: '500 kB',
},
]
7. 技术债务的显性化管理
// 用代码注释显性标记技术债务,让 AI 知道这是待处理的问题
// TODO(tech-debt, priority:high, ticket:FE-1234):
// 这里使用了临时方案绕过了类型检查,
// 根本原因是后端返回数据不符合约定,待 BE-5678 修复后移除
const data = response as unknown as UserData
四、工具链全景图
类型与代码质量
| 工具 | 用途 | AI Friendly 价值 |
|---|---|---|
| TypeScript 5.x strict | 类型系统 | 减少 AI 幻觉的基础 |
| Zod / Valibot | 运行时校验 | Schema 即文档 |
| type-coverage | 类型覆盖率 | CI 门禁指标 |
| tsd / expect-type | 类型单元测试 | 验证泛型正确性 |
架构约束
| 工具 | 用途 | AI Friendly 价值 |
|---|---|---|
| eslint-plugin-boundaries | 层级约束 | 禁止 AI 破坏架构 |
| madge | 循环依赖检测 | AI 常见引入问题 |
| ts-prune | 未使用 exports | 代码库瘦身 |
| knip | 未使用文件/依赖 | 防止死代码累积 |
API 契约
| 工具 | 用途 | AI Friendly 价值 |
|---|---|---|
| openapi-typescript | 类型自动生成 | 接口类型零手写 |
| orval / hey-api | hooks 自动生成 | 数据层零手写 |
| msw | API Mock | 基于 schema 的 mock |
| Pact | 契约测试 | 防止接口悄悄变更 |
组件与文档
| 工具 | 用途 | AI Friendly 价值 |
|---|---|---|
| Storybook | 组件文档 | AI 的组件使用示例库 |
| Chromatic | 视觉回归 | 防止 AI 引入 UI 变更 |
| TypeDoc | API 文档生成 | 类型注释转文档 |
CI/CD
| 工具 | 用途 | AI Friendly 价值 |
|---|---|---|
| size-limit | 包大小门禁 | 防止大依赖悄悄引入 |
| bundlemon | Bundle 追踪 | PR 级别的大小对比 |
| Sentry | 错误监控 | 快速发现 AI 引入的运行时错误 |
五、实施路线图
建议分三个阶段推进,每个阶段有明确的可验收指标:
第一阶段:治理基础(2-4 周)
目标:让 AI 在明确的边界内工作,建立机器强制执行的基本约束
- 创建并维护
CLAUDE.md,明确技术栈约定和禁止事项 - 开启 TypeScript strict mode,消灭存量
any - 配置 ESLint + Prettier + Husky,commit 前自动检查
- CI 加入类型检查和循环依赖检测
- 为重要的技术决策补写 ADR
验收指标:类型覆盖率 > 90%,零循环依赖,CI 通过率 > 95%
第二阶段:结构清晰(1-2 月)
目标:让 AI 能安全地局部推理,改一处不影响全局
- 迁移到 Feature-Sliced Design 或明确的模块化架构
- 建立 OpenAPI → 自动生成类型的链路
- Design System 组件全部加入 JSDoc 注释和 Storybook stories
- 建立组件公共 API 边界(统一 index.ts 导出)
- ESLint 层级约束规则上线
验收指标:新需求开发时,AI 生成的代码无需大幅修改可直接使用
第三阶段:质量飞轮(持续)
目标:测试和文档形成飞轮,代码库随时间越来越健康
- 核心业务逻辑测试覆盖率 > 80%
- 契约测试覆盖主要接口
- Bundle size 门禁上线
- 建立架构健康度定期审查机制
- PR 模板加入 AI 代码 Review Checklist
验收指标:技术债务指标(循环依赖、any 数量、测试覆盖率)持续改善而非恶化
结语
AI Friendly 的前端工程体系,本质上是把集体智慧编码进机器可执行的规则。
人类开发者可以通过沟通、Code Review、口头约定来协调协作。但 AI 没有这些渠道——它只能依赖代码库本身携带的信息来理解应该怎么做。没有写进 CLAUDE.md 的约定,没有体现在类型系统里的约束,没有 ESLint 规则强制的边界,对 AI 来说等于不存在。
口头约定在 AI 时代会被无视。机器约束和可检索的文档会被遵守。
把规范变成规则,把经验变成类型,把决策变成文档——这就是让 AI 成为团队质量放大器而非熵增加速器的根本路径。
最后更新:2026-05