
你是不是也遇到过这种情况用 AI 编程助手比如 Cursor、GitHub Copilot时精心写了半天 prompt结果生成的代码要么跑不通要么和你的项目上下文完全不搭边。你开始怀疑是不是自己的 prompt 技巧太差或者 AI 模型还不够聪明但 Matt Pocock一位知名的 TypeScript 专家和开发者布道师提出了一个颠覆性的观点真正决定 AI 编程效果的往往不是你的 prompt而是你的代码库本身的结构。换句话说如果你的代码写得一团糟AI 再聪明也读不懂。他引入了一个叫做“深模块”Deep Module的架构设计理念认为这是“治疗”AI 读不懂你代码的良方。这篇文章要解决的正是这个被很多开发者忽略的核心问题。我们花了太多时间研究“如何向 AI 提问”却很少思考“如何让 AI 更好地理解我们已有的代码”。本文将深入拆解 Matt Pocock 的观点并结合大量实际开发场景为你讲清楚为什么“深模块”架构比“神 prompt”更重要背后的逻辑是什么“深模块”具体是什么它与“浅模块”有何本质区别如何在实际项目中无论是前端 React/TypeScript还是后端 Node.js/Java应用“深模块”思想来重构代码有哪些立即可用的代码示例和重构技巧能立刻提升 AI 在你项目中的表现如果你已经受够了 AI 生成无关代码、无法理解业务逻辑的窘境那么改变代码结构可能是你接下来最值得投入的一项“基础设施”投资。本文不仅有理论更有能直接复制粘贴到项目中的实践方案。1. 问题的本质AI 如何“阅读”你的代码库在讨论解决方案前我们必须先理解问题。当你向 Cursor 的 Chat 或 Copilot 提出一个需求时AI 并不是像人类一样通读整个项目然后深思熟虑。它的工作流程更接近于一种“受限的上下文检索与模式匹配”。1.1 AI 编程助手的上下文处理机制以目前主流的 AI 编程工具为例有限的上下文窗口无论是 GPT-4 还是 Claude都有 token 限制如 128K。工具会智能地选取与你当前编辑文件最相关的代码片段、打开的文件、最近的修改等填充到这个窗口里作为 AI 的“短期记忆”。基于嵌入的检索更高级的工具如 Cursor 的“引用代码库”功能会为你的代码库建立向量索引。当你提问时它会检索语义上最相关的代码块并将其作为上下文提供给 AI。模式匹配与补全在行内补全场景AI 主要关注当前文件的前后文和语言惯例进行“下一个 token 预测”。关键洞察AI 对代码的理解深度严重依赖于它所能“看到”的上下文的质量和清晰度。如果你的代码结构混乱、职责不清、接口复杂那么即使被检索到AI 也很难提取出准确的意图和模式。1.2 糟糕的代码结构如何“毒害”AI假设你有一个用户管理模块代码分散在多个文件且互相紧耦合// 文件src/utils/helpers.ts 一个什么都放的“工具杂货铺” export function validateEmail(email: string): boolean { /* ... */ } export function formatUserName(user: any): string { /* ... */ } export function sendEmail(to: string, subject: string, content: string) { /* ... */ } export function calculateUserScore(orders: any[]): number { /* ... */ } export const APP_CONFIG { /* ... */ }; // 文件src/components/UserProfile.tsx import { formatUserName, calculateUserScore } from ../utils/helpers; // 同时这个组件还直接调用了 API 层和状态管理当你在这个UserProfile组件里问 AI“帮我在用户头像旁边添加一个根据最近活跃度显示的徽章”。AI 可能会看到calculateUserScore但不知道orders参数具体是什么结构从哪里来。看不到“活跃度”的业务定义在哪里。可能会错误地复用formatUserName的逻辑或者生成一个全新的、与现有工具函数重复的calculateActivityBadge函数。结果生成的代码需要你大量修改才能集成或者引入了新的重复逻辑。你感觉 AI 很“笨”但实际上是你的代码没有给它提供清晰的“地图”。2. 解药深模块Deep Module架构设计这个概念并非 Matt Pocock 独创它源自 John Ousterhout 的经典著作《A Philosophy of Software Design》。其核心思想是评价一个模块好坏的标准不是行数多少而是接口的简洁度与内部功能的强大度之间的比值。2.1 深模块 vs. 浅模块一个直观对比特性浅模块 (Shallow Module)深模块 (Deep Module)接口复杂、庞大、暴露大量细节简单、小巧、隐藏复杂细节实现可能很简单与接口复杂度不匹配内部可能很复杂但对外接口简洁认知负荷高。使用者需要了解很多接口细节才能使用。低。使用者通过简单的接口就能获得强大的功能。对 AI 的影响AI 需要处理大量无关接口信息难以理解核心职责。AI 通过简洁接口就能把握模块核心功能易于正确调用和扩展。类比一台面板上有100个按钮但只能播放音乐的“播放器”。一台只有一个“播放”按钮但内部集成了高品质音响、降噪、网络流媒体的智能音箱。2.2 深模块的四个关键特征强大的抽象模块提供一个高层次的、解决问题的抽象而不是一系列低层次的操作步骤。简洁的接口暴露给外部的 API 或方法数量尽可能少参数清晰。隐藏的实现将复杂性、算法、数据转换、第三方依赖等封装在模块内部。明确的职责一个模块只做一件事并把它做到极致。3. 实战重构将“浅模块”代码转化为“深模块”让我们用一个具体的例子看看如何重构代码使其对 AI 更友好。场景一个电商应用中的“价格计算”逻辑。3.1 重构前分散且透明的“浅模块”代码// 文件1: src/utils/priceCalculations.ts export function applyDiscount(price: number, discountRate: number): number { return price * (1 - discountRate); } export function addTax(price: number, taxRate: number): number { return price * (1 taxRate); } export function formatPrice(price: number, currency: string): string { return new Intl.NumberFormat(en-US, { style: currency, currency }).format(price); } export function isFreeShipping(subtotal: number, threshold: number): boolean { return subtotal threshold; } // 文件2: src/components/Checkout.tsx import { applyDiscount, addTax, formatPrice, isFreeShipping } from ../utils/priceCalculations; function Checkout({ items, userDiscount }) { // 业务逻辑散落在组件中 const subtotal items.reduce((sum, item) sum item.price, 0); const discountedPrice applyDiscount(subtotal, userDiscount); const finalPrice addTax(discountedPrice, 0.08); // 硬编码税率 const shippingEligible isFreeShipping(subtotal, 50); return ( div pSubtotal: {formatPrice(subtotal, USD)}/p pFinal Price: {formatPrice(finalPrice, USD)}/p pShipping: {shippingEligible ? Free : $5.99}/p /div ); }问题AI 在Checkout组件里看到一堆零散的函数调用和硬编码的数字。如果让它“添加一个会员双倍积分功能”它很难判断积分应该基于subtotal、discountedPrice还是finalPrice计算逻辑该加在哪里。3.2 重构后封装良好的“深模块”代码我们创建一个PriceEngine深模块。// 文件: src/lib/price/PriceEngine.ts export interface PriceCalculationParams { items: Array{ price: number; }; discountRate: number; taxRate: number; freeShippingThreshold: number; } export interface CalculatedPrice { subtotal: number; discountedAmount: number; taxAmount: number; finalAmount: number; isEligibleForFreeShipping: boolean; } export class PriceEngine { private params: PriceCalculationParams; constructor(params: PriceCalculationParams) { this.params params; } calculate(): CalculatedPrice { const subtotal this.calculateSubtotal(); const discountedAmount this.applyDiscount(subtotal); const taxAmount this.calculateTax(discountedAmount); const finalAmount discountedAmount taxAmount; return { subtotal, discountedAmount, taxAmount, finalAmount, isEligibleForFreeShipping: this.checkFreeShipping(subtotal), }; } format(price: number, currency: string USD): string { return new Intl.NumberFormat(en-US, { style: currency, currency }).format(price); } // 私有方法隐藏实现细节 private calculateSubtotal(): number { return this.params.items.reduce((sum, item) sum item.price, 0); } private applyDiscount(subtotal: number): number { return subtotal * (1 - this.params.discountRate); } private calculateTax(amount: number): number { return amount * this.params.taxRate; } private checkFreeShipping(subtotal: number): boolean { return subtotal this.params.freeShippingThreshold; } }现在组件中的使用变得极其简洁// 文件: src/components/Checkout.tsx import { PriceEngine } from ../lib/price/PriceEngine; function Checkout({ items, userDiscount }) { // 所有复杂逻辑被封装 const priceEngine new PriceEngine({ items, discountRate: userDiscount, taxRate: 0.08, freeShippingThreshold: 50, }); const price priceEngine.calculate(); return ( div pSubtotal: {priceEngine.format(price.subtotal)}/p pFinal Price: {priceEngine.format(price.finalAmount)}/p pShipping: {price.isEligibleForFreeShipping ? Free : $5.99}/p /div ); }3.3 为什么重构后对 AI 更友好接口极简AI 在Checkout组件里只看到一个PriceEngine的导入和两个方法调用calculate,format。它立刻明白这里是处理价格的。意图明确当你想让 AI“添加会员双倍积分”时你可以直接在PriceEngine类上下文中提问。AI 看到calculate()方法返回CalculatedPrice类型它会自然地建议在这个类型中添加pointsEarned: number字段并在calculate()方法内部添加积分计算逻辑。所有相关逻辑都聚集在一个文件里AI 的上下文高度相关。隐藏变化税率、免邮阈值等细节被封装在参数和私有方法中。AI 不会在业务组件里被这些细节干扰从而更专注于核心业务流。4. 跨技术栈的深模块设计模式深模块是一种思想不限于 TypeScript 或前端。4.1 后端Node.js with NestJS服务层封装// 浅模块风格控制器里充满逻辑 Controller(users) export class UsersController { constructor(private usersService: UsersService) {} Post(register) async register(Body() dto: RegisterUserDto) { // 验证、业务逻辑、加密、邮件发送全堆在这里或分散在多个服务方法中 const exists await this.usersService.findByEmail(dto.email); if (exists) throw new ConflictException(Email exists); const hashedPwd await bcrypt.hash(dto.password, 10); const user await this.usersService.create({ ...dto, password: hashedPwd }); await this.mailService.sendWelcomeEmail(user.email); return user; } } // 深模块风格一个清晰的“用例”服务 Injectable() export class UserRegistrationService { // 依赖注入其他服务 constructor( private userRepo: UserRepository, private mailService: MailService, ) {} // 一个强大的公共方法隐藏所有复杂性 async execute(dto: RegisterUserDto): PromiseUser { await this.validateRegistration(dto); const user await this.createUserEntity(dto); await this.userRepo.save(user); await this.sendWelcomeNotification(user); return user; } // 私有方法封装细节 private async validateRegistration(dto: RegisterUserDto): Promisevoid { // ... 检查邮箱唯一性、密码强度等 } private async createUserEntity(dto: RegisterUserDto): PromiseUser { // ... 密码哈希、生成验证令牌等 } private async sendWelcomeNotification(user: User): Promisevoid { // ... 发送邮件、记录日志等 } } // 控制器变得非常薄 Controller(users) export class UsersController { constructor(private registrationService: UserRegistrationService) {} Post(register) async register(Body() dto: RegisterUserDto) { return this.registrationService.execute(dto); } }对 AI 的益处AI 在修改注册逻辑如添加手机号验证时只需关注UserRegistrationService这个深模块无需跳转查看控制器、仓库、邮件服务等多个文件上下文集中生成代码的准确性大幅提高。4.2 通用原则创建“领域语言”深模块的终极目标是让你的代码库形成一套高级的“领域特定语言”DSL。当你的模块提供了像PriceEngine.calculate()、UserRegistrationService.execute()这样高层次的抽象时AI 就能用这种高级语言和你对话而不是纠缠于底层的applyDiscount或bcrypt.hash。5. 结合 AI 工具的最佳实践工作流理解了深模块我们可以优化使用 Cursor/Copilot 的工作流。5.1 第一步在正确的上下文中提问错误在庞大的App.tsx里问“如何实现价格计算”正确打开或创建src/lib/price/PriceEngine.ts文件然后问“在这个PriceEngine类中如何添加一个计算会员积分的方法积分规则是每消费1美元得1积分折扣后金额计算。”5.2 第二步利用 AI 进行重构你可以直接给 AI 指令“将当前这个分散的utils/priceCalculations.ts和Checkout组件中的逻辑重构为一个深模块PriceEngine。” 一个设计良好的 AI 能够根据现有代码生成类似于第 3.2 节的初步结构。5.3 第三步定义清晰的接口和类型这是帮助 AI 理解模块边界的关键。在创建新模块时先让人或 AI 写出主要的接口Interface和类型Type。// 先定义清楚模块要做什么 export interface Campaign { id: string; name: string; discountType: percentage | fixed; value: number; } export interface PricingResult { original: number; discounted: number; applicableCampaigns: Campaign[]; } export interface PricingCalculator { calculateFinalPrice(items: LineItem[], campaigns: Campaign[]): PricingResult; }然后让 AI 去实现PricingCalculator。有了清晰的接口约束AI 的实现会更符合预期。5.4 第四步迭代与封装AI 生成代码后检查是否有暴露过多的内部细节。将不必要公开的辅助函数改为private将配置参数收拢到构造函数或配置对象中。不断问自己“这个模块的接口还能更简单吗”6. 常见问题与排查思路问题现象可能原因排查方式解决方案AI 生成的函数总是操作错误的数据结构模块接口混乱数据结构不一致或未隐藏。检查相关模块的输入输出类型定义是否清晰、统一。定义并导出清晰的 DTO数据传输对象或领域模型让所有函数都基于这些标准类型操作。AI 无法理解跨多个文件的业务逻辑逻辑过于分散形成“浅模块”网络。寻找一个业务流程如“用户下单”看它涉及了多少个文件。将该业务流程重构为一个“深模块”服务、用例或管理器聚合相关逻辑。AI 在补全时提供完全不相关的建议当前文件职责不单一包含太多不同领域的代码。审查当前文件是否混合了视图、逻辑、工具等多种代码。使用“抽取函数”或“抽取类”重构将不同职责的代码分离到不同的深模块中。使用“引用代码库”功能后AI 引用了无关代码代码库中命名相似但功能无关的模块太多。检查被引用的无关代码的命名和位置。采用更具描述性、唯一性的模块名和文件名。遵循功能分区目录结构如/lib/auth,/lib/payment。对 AI 描述需求很费力需要写很长 prompt代码抽象层次太低缺乏领域语言。尝试用一句话描述你想让某个模块做的事如果这句话很长且包含“和”、“然后”、“首先”等词说明抽象不够。将这一连串操作封装到一个新的深模块方法中并用那句描述来命名这个方法。7. 最佳实践与工程建议从领域驱动设计DDD中汲取灵感聚合根Aggregate、实体Entity、值对象Value Object和领域服务Domain Service天然就是深模块。它们定义了清晰的边界和职责。依赖注入DI是好朋友它强制你定义清晰的接口并将模块的依赖关系显式化这极大地帮助了 AI 理解模块的上下文和职责。如上文 NestJS 示例。编写简洁的模块文档JSDoc/TSDoc在模块和类级别用一两句话说明它的核心职责。AI 在检索时会读取这些注释从而更好地理解模块用途。/** * 核心定价引擎负责计算订单的各类价格、折扣、税费及运费资格。 * 封装了所有定价规则和计算逻辑。 */ export class PriceEngine { ... }为深模块编写单元测试测试即文档。清晰、覆盖全面的测试用例向 AI 展示了模块在各种边界条件下的预期行为是极佳的上下文。避免“上帝对象”和“工具类陷阱”一个包含 50 个静态方法的Utils类是典型的浅模块。应该按领域将其拆分为StringUtils、DateUtils、PriceUtils等并最终演进为更深的领域模块。循序渐进不必一步到位不要试图一次性重构整个项目。下次当你需要 AI 协助修改某个功能时就以那个功能为起点将其重构为一个更深的模块。积少成多代码库对 AI 的友好度会逐渐提升。8. 总结与后续方向Matt Pocock 的观点之所以深刻是因为它指出了人机协作中的一个根本性转变在 AI 时代代码的可读性对象不再仅仅是人类同事还包括 AI 智能体。“深模块”架构本质上是在为 AI 优化代码的“可检索性”和“可推理性”。提升 AI 编程效率从痴迷于编写“完美 prompt”转向精心设计“清晰代码结构”是一个更高杠杆率的投资。这不仅能让你更好地驾驭 AI 工具更能从根本上提升代码质量降低维护成本让团队协作也更顺畅。你的下一步行动可以是审计一个模块在你的项目中找一个经常让 AI“犯糊涂”的功能点用本文的“深模块”标准评估它。进行一次小规模重构花 30 分钟尝试将这个功能点重构成一个接口更简洁、职责更明确的模块。测试 AI 协作效果在重构后的模块上向 Cursor 或 Copilot 提出一个新的、相关的功能需求感受生成代码的准确度变化。记住最好的 prompt 工程可能就是从写好你的下一条代码注释、设计好下一个函数接口开始的。当你开始像为一位强大的、但注意力有限的合作伙伴编写文档一样去编写代码时你就已经走在了人机协同编程的前沿。