每个使用 AI 编程助手的开发者都经历过这种挫败感:AI 生成的代码差不多能运行,但使用了错误的命名约定、从错误的路径导入包,或是凭空捏造了一个不存在的 API。这种问题的根本原因几乎总是糟糕的代码库结构,而非 AI 本身的局限性。
AI 助手本质上是上下文预测引擎。它们会根据提供的上下文窗口,生成具备统计学概率上最可能的延续内容。一个结构良好的代码库能提供强大且一致的信号,从而显著提升代码生成的质量。
AI 代码生成技术栈
以下是 AI 助手处理代码生成请求的流程:
处在中间的两大步骤——上下文收集与模式匹配——正是您自身的代码库结构发挥最大作用的环节。
命名一致性高于一切
您能做的最高优先级的改进是:保持极其严格的命名一致性。AI 会从上下文中提供的示例学习您的既往模式。
// 反面示例 —— 不一致的模式会让 AI 感到困惑
const getUser = () => {...}
const fetchUserProfile = () => {...}
const loadUserSettings = () => {...}
const retrieveUserPosts = () => {...}
// 正面示例 —— 动词-名词模式保持一致
const fetchUser = () => {...}
const fetchUserProfile = () => {...}
const fetchUserSettings = () => {...}
const fetchUserPosts = () => {...}
当 AI 看到 fetchUser 与 fetchUserProfile 时,能够一次性准确推断出 fetchUserSettings。
按功能边界组织,而非按类型组织
大多数的 AI 错误都源于其根本不知道该选用哪个组件、Hook 或工具函数。基于功能组织(Colocate by Feature)能将相关的代码放在一起,使得 AI 得以获悉完整的代码上下文:
src/features/
auth/
components/
LoginForm.tsx
RegisterForm.tsx
hooks/
useAuth.ts
useAuthRedirect.ts
api/
authApi.ts
types/
auth.types.ts
index.ts ← barrel export
posts/
components/...
hooks/...
api/
postsApi.ts
types/
post.types.ts
index.ts
当您打开 LoginForm.tsx 时,AI 能清晰看到附近的 useAuth.ts 并自动从中导入对应逻辑,而不是凭空另写一套专属的鉴权方案。
明确的类型契约
集中在一处定义类型,并在各处进行导入。对于分布在各个文件中的推断类型而言,明确具体的类型定义能得到 AI 工具高得多的尊重:
// features/posts/types/post.types.ts
export interface Post {
id: string
title: string
content: string
authorId: string
status: 'draft' | 'published' | 'archived'
createdAt: Date
updatedAt: Date
}
export interface CreatePostInput {
title: string
content: string
status?: Post['status']
}
export interface PostFilters {
status?: Post['status']
authorId?: string
page?: number
limit?: number
}
当 AI 因需要处理跟帖子相关的功能而生成新的 Hook 与组件时,它能准确无误地完成导入操作,因为这些类型都经过了明确的命名与导出。
用 Barrel 导出标记边界
index.ts 文件扮演了模块公共 API 声明的角色。AI 工具会彻底尊重这些边界划分:
// features/posts/index.ts
export { PostList } from './components/PostList'
export { PostDetail } from './components/PostDetail'
export { usePost, usePosts } from './hooks/usePosts'
export type { Post, CreatePostInput } from './types/post.types'
// 不要导出内部的实现细节
相较于深入探求内部路径,AI 会直接从 features/posts 进行安全且合规的导入。
错误处理模式的一致性
针对错误处理方式进行一次定义,并在所有地方复用:
// lib/errors.ts
export class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode: number = 500
) {
super(message)
this.name = 'AppError'
}
}
export class NotFoundError extends AppError {
constructor(resource: string, id: string) {
super(`${resource} with id "${id}" not found`, 'NOT_FOUND', 404)
}
}
export class ValidationError extends AppError {
constructor(message: string, public field?: string) {
super(message, 'VALIDATION_ERROR', 400)
}
}
一旦 AI 在您的代码库中发现这种模式,它就能在无需额外提示的情况下,自动为所有新功能生成准确的错误处理代码。
记录您的不变性约定 (Invariants)
即便只是针对那些并不直观的架构决策提供简短的注释,也同样能极大提升 AI 的代码输出质量:
// hooks/useOptimisticUpdate.ts
// 不变性约定:无论发生成功或失败,始终调用 onSettled。
// 这能确保从服务端重新获取最新数据,从而避免陷入
// 过期的乐观状态。绝不要在 onMutate 中提前返回。
export function useOptimisticUpdate<T>(...) {
return useMutation({
onMutate: async (variables) => {
// ...
},
onSettled: () => {
queryClient.invalidateQueries(...) // 始终执行
},
})
}
总结
一个为了让 AI 提供最佳辅助而架构良好的代码库往往具备:
- 统一的命名约定 —— AI 会从既往模式中准确预测命名
- 按功能聚集 —— 确保相关的代码共享同一片上下文环境
- 清晰的类型契约 —— AI 会去导入真实存在的类型,而不是凭空捏造
- Barrel 导出 —— 清晰的模块边界,AI 会予以遵循
- 记录不变性 —— 帮助 AI 学习并掌握您的架构规则
本仓库中的模板全部遵循了这些原则。正因如此,开发者们反馈只需一个提示词(prompt)就能生成完整的新功能——因为该代码库为 AI 提供了它走向成功所需的一切条件。