AI Friendly 前端工程体系:从概念到落地实践

0 阅读20分钟

写给前端开发者、前端架构师和大前端团队的系统性指南


前言:为什么你的 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-apihooks 自动生成数据层零手写
mswAPI Mock基于 schema 的 mock
Pact契约测试防止接口悄悄变更

组件与文档

工具用途AI Friendly 价值
Storybook组件文档AI 的组件使用示例库
Chromatic视觉回归防止 AI 引入 UI 变更
TypeDocAPI 文档生成类型注释转文档

CI/CD

工具用途AI Friendly 价值
size-limit包大小门禁防止大依赖悄悄引入
bundlemonBundle 追踪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