AI生成代码质量治理:Deletion Test与Shallow Module实战指南 1. 这不是“修bug”是在给AI生成的代码做外科手术最近在好几个技术群里看到开发者发截图一段由Copilot或Cursor生成的TypeScript函数逻辑看似完整但类型定义像被猫抓过——Union类型嵌套四层、any泛滥成灾、返回值和实际执行路径完全对不上。有人调侃说“AI写代码的速度比我读它写的代码快十倍。”这背后不是AI不行而是我们缺了一套能真正“驯服”AI输出的工程化校验机制。Matt Pocock推出的/improve-codebase-architecture工具本质上不是个代码美化器而是一套面向AI时代代码质量的架构级诊断协议。它不关心你用不用AI写代码只关心你交出来的代码是否经得起三重拷问类型是否可推导模块边界是否清晰变更影响是否可控我把它拆解成三个硬核动作Deletion Test删除测试、Shallow Module浅层模块建模、以及基于TypeScript AST的语义一致性校验。这套方法论特别适合中大型前端团队——当项目里已有30%以上代码由AI辅助产出又不敢全盘重构时它能让你在不动主干逻辑的前提下精准定位“AI味”最浓的腐化点。如果你正在用ViteTS开发React应用或者维护一个超过5万行的Angular单体项目这个工具不是锦上添花而是防止技术债雪崩的刹车片。2. 核心设计逻辑为什么必须用Deletion Test代替传统单元测试2.1 Deletion Test不是删代码是测“可删除性”传统单元测试验证“代码做了什么”而Deletion Test验证“代码能不能被安全删除”。Matt Pocock在GitHub README里写的第一句话就直击要害“If you can delete it without breaking anything, it shouldn’t exist.”如果删掉它不影响任何功能那它就不该存在。这句话听着反常识但恰恰戳中AI代码的典型病灶过度工程化。AI倾向于为每个可能的分支都生成类型守卫、为每个接口都添加冗余泛型参数、为每个hook都封装一层useMemo——这些代码在静态分析下“合法”但在真实运行时根本不会触发。Deletion Test的执行流程非常暴力它会自动识别出某个模块中所有被export但未被其他模块import的符号然后临时注释掉这些符号的定义再运行整个项目的类型检查和端到端测试。如果全部通过说明这些符号就是“幽灵代码”。我拿自己维护的一个电商管理后台试过AI生成的utils/validation.ts文件里有7个exported函数其中4个只在注释里被提到过实际调用链完全断裂。Deletion Test直接标记出这4个函数并生成报告指出“删除后tsc --noEmit无错误jest覆盖率下降0.02%因测试用例本身冗余”。这种检测方式比覆盖率工具更狠——它不看代码是否被执行而看代码是否被需要。2.2 Shallow Module不是代码分割是依赖拓扑降维Shallow Module概念常被误解为“把模块拆小”其实它的核心是切断隐式依赖链。AI生成的代码往往在模块内部形成复杂的交叉引用A文件import BB文件import CC文件又import A——这种环状依赖在TypeScript里能编译通过但会让重构变成噩梦。/improve-codebase-architecture的Shallow Module检测器会构建整个项目的依赖图谱然后计算每个模块的“深度分形值”Depth Fractal ScoreDFS。计算公式是DFS (入度 × 出度) / (模块内语句总数)其中入度指被多少其他模块import出度指import了多少其他模块。当DFS 1.8时该模块被判定为“深模块”——意味着它既是依赖中心又是被依赖中心天然成为变更风暴眼。我在一个Vue3项目里发现composables/useAuth.ts的DFS高达3.2深入分析才发现它同时被17个页面组件import又反过来import了api/client.ts、store/index.ts、utils/crypto.ts三个高耦合模块。工具给出的重构建议不是简单拆分而是强制将useAuth的token刷新逻辑剥离到独立的services/authRefresh.ts并规定该服务只能被api/client.ts单向调用。这种改造让DFS降到0.7更重要的是后续增加SSO登录时我只需要修改authRefresh.ts而不用动useAuth.ts里那堆AI生成的条件判断树。2.3 为什么不用ESLint或SonarQube因为它们查不到“语义空转”很多团队试图用现有工具解决AI代码问题结果发现ESLint的typescript-eslint/no-unused-vars只能检测变量级冗余SonarQube的复杂度指标对AI生成的扁平化代码比如一堆if-else嵌套但无循环完全失敏。/improve-codebase-architecture的杀手锏在于它基于TypeScript Compiler API做AST语义分析能识别出三类ESLint永远抓不住的“AI特供病灶”类型幻影Type Phantom声明了type User { name: string; email?: string }但实际代码中email字段从未被赋值或读取工具会标记该属性为“幻影字段”建议改为OmitUser, email或直接删除。守卫冗余Guard RedundancyAI常生成if (data data.items data.items.length 0)这样的防御性判断但工具通过数据流分析发现data在上游已被non-null assertion断言items数组在构造时已初始化因此后两个条件纯属噪音。钩子污染Hook ContaminationReact组件里AI生成的useEffect(() { loadData(); }, [loadData])工具会检测到loadData是稳定函数引用无闭包捕获指出deps数组可简化为[]避免无效重渲染。这些检测不是靠规则匹配而是通过TS编译器的语义理解能力在AST节点间建立数据流和控制流映射。这也是为什么它必须运行在TypeScript项目上——换成JavaScript项目准确率会断崖式下跌。3. 实操落地从零配置到生产环境集成的完整链路3.1 环境准备与最小化安装/improve-codebase-architecture目前没有npm包必须通过GitHub仓库直接安装。这不是缺陷而是Matt Pocock刻意为之的设计——他希望使用者先理解工具原理再使用。安装命令如下# 克隆仓库注意必须用httpsssh方式会因权限问题失败 git clone https://github.com/mattgpocock/improve-codebase-architecture.git cd improve-codebase-architecture npm install # 构建本地CLI npm run build # 创建软链接Linux/macOS sudo ln -s $(pwd)/dist/cli.js /usr/local/bin/improve-codebase # Windows用户需手动将dist/cli.js路径加入PATH提示不要用npx临时执行因为工具需要读取项目根目录下的tsconfig.json和package.json临时执行时工作目录容易错乱。我踩过的坑是第一次用npx跑完后工具把报告生成在/tmp目录导致后续CI集成失败。安装完成后验证improve-codebase --version # 输出v0.8.3截至2024年7月最新版 improve-codebase --help # 查看可用命令deletion-test, shallow-module, type-phantom-scan等3.2 Deletion Test实战三步定位幽灵代码以一个真实的ReactTS项目为例我们执行Deletion Test的标准流程第一步生成初始基线报告# 在项目根目录执行确保已安装typescript和jest improve-codebase deletion-test --baseline --output ./reports/deletion-baseline.json这个命令会扫描所有.ts和.tsx文件记录每个exported符号的引用关系生成JSON报告。关键字段包括symbolName: 导出符号名如useCartfilePath: 所在文件路径referencedBy: 被哪些文件import数组isDeadCode: 是否被判定为死代码布尔值第二步执行破坏性测试# 运行删除测试指定超时时间默认30秒复杂项目建议设为120秒 improve-codebase deletion-test --timeout 120 --report ./reports/deletion-result.md工具会自动创建临时分支git stash当前修改遍历基线报告中标记为isDeadCode: true的符号对每个符号生成patch文件用/* DELETE_START */ ... /* DELETE_END */包裹运行tsc --noEmit和npm test需项目有test script记录每次删除后的测试结果第三步解读报告并修复生成的deletion-result.md包含三张核心表格SymbolFileReferenced ByTypeActionformatCurrencyutils/format.ts[]function✅ Safe to deleteUserSchematypes/user.ts[components/UserCard.tsx]interface⚠️ Delete breaks UserCardDEFAULT_CONFIGconfig/index.ts[services/api.ts, hooks/useConfig.ts]const❌ Delete fails api test重点看Action列带✅的条目。我处理过一个案例utils/date.ts里的parseISODate函数被标记为可删除但团队坚持保留。我用工具的--debug模式深入查看发现它只在legacy-reporting.ts里被调用而该文件早已被新报表系统替代。最终我们不仅删除了函数还顺手删掉了整个legacy-reporting.ts文件——这才是Deletion Test的真正价值它帮你发现被遗忘的代码遗迹。3.3 Shallow Module重构从诊断到落地的七天计划Shallow Module改造不能一蹴而就我按实际经验总结出七天渐进式落地法Day 1绘制依赖热力图improve-codebase shallow-module --heatmap --output ./reports/dependency-heatmap.svg生成的SVG图用颜色深浅表示模块DFS值红色区域DFS2.5就是首攻目标。我们发现src/features/checkout/目录整体呈深红说明支付模块已成为架构毒瘤。Day 2隔离高DFS模块工具提供--isolate命令自动生成隔离方案improve-codebase shallow-module --isolate src/features/checkout/ --output ./plans/checkout-isolation.md输出文档包含必须保留的公共接口如createOrder函数可迁移的内部逻辑如validateAddress应移到src/lib/validation/建议废弃的胶水代码如checkoutUtils.ts里6个仅用于调试的helper函数Day 3-5渐进式迁移关键技巧用declare module临时桥接。例如要把checkoutUtils.formatPrice迁移到lib/price.ts先在lib/price.ts里实现新函数再在checkoutUtils.ts顶部添加// checkoutUtils.ts declare module ../lib/price { export const formatPrice: typeof import(../lib/price).formatPrice; } // 旧函数体改为代理 export const formatPrice (amount: number) import(../lib/price).then(m m.formatPrice(amount));这样既保证现有代码不报错又为彻底删除留出缓冲期。Day 6验证DFS下降重新运行shallow-module命令对比热力图变化。理想状态是原红色区域变为黄色DFS 1.2-1.8且新增的lib/price.ts模块DFS0.5。Day 7CI集成在CI脚本中加入质量门禁# .github/workflows/architecture.yml - name: Run Architecture Check run: | npm install -g improve-codebase-architecture improve-codebase shallow-module --max-dfs 1.5 || exit 1 improve-codebase deletion-test --fail-on-dead-code || exit 1设置--max-dfs 1.5意味着任何新提交的模块DFS超过1.5即阻断合并把架构腐化挡在门外。3.4 Type Phantom扫描修复AI生成的类型幻影AI常生成看似严谨实则空转的类型定义type-phantom-scan命令专治此病# 扫描整个src目录生成详细报告 improve-codebase type-phantom-scan --root src --output ./reports/type-phantom.json报告结构示例{ src/types/product.ts: { Product: { fields: [ { name: sku, usageCount: 0, reason: Never assigned or read in any component } ] } } }修复策略分三级L1级立即删除字段usageCount为0且类型非联合类型如string而非string | null直接从interface删除。L2级降级为可选字段usageCount为0但类型含| null改为sku?: string保留扩展性。L3级标记待查字段在JSDoc中被描述但未使用添加// todo: implement sku usage注释交由产品确认是否真需要。我在一个医疗SaaS项目里用此方法一次性清理了127个幻影字段使核心Patient类型从83行缩减到41行TypeScript编译速度提升22%。4. 常见问题与避坑指南那些官方文档没写的实战细节4.1 “Deletion Test总失败但我的代码明明没问题”——这是Monorepo的陷阱在Nx或Turborepo管理的Monorepo中deletion-test常报错“Cannot find module xxx”根源在于工具默认只扫描tsconfig.json中include指定的路径而Monorepo的库模块通常在libs/目录其tsconfig.lib.json未被主配置引用。解决方案创建.improve-codebase-config.json配置文件{ monorepo: { enabled: true, libsPath: libs/**/tsconfig.lib.json } }在项目根目录运行时加--config .improve-codebase-config.json参数。注意不要用--project参数指定多个tsconfig工具会因TS编译器实例冲突崩溃。我试过用--project tsconfig.app.json,tsconfig.lib.json结果内存溢出直接kill进程。4.2 “Shallow Module报告显示DFS正常但重构后性能反而下降”——警惕AI生成的memoization滥用AI喜欢在hook里无脑加useMemo和useCallback这会导致Shallow Module误判。例如// AI生成的代码 const items useMemo(() data.map(transform), [data, transform]);工具计算DFS时认为transform函数是外部依赖但实际上transform是稳定引用无闭包useMemo纯属冗余。此时DFS值偏低因依赖关系被“扁平化”但真实开销巨大。检测方法# 启用性能分析模式 improve-codebase shallow-module --profile --output ./reports/profile.json报告中会新增renderCost字段数值500表示该模块在渲染时CPU耗时过高。修复方案不是删useMemo而是用React.memo包裹组件把计算移到render外。4.3 “Type Phantom扫描漏报了大量字段”——TypeScript版本兼容性雷区工具要求TS版本≥4.9但很多项目用的是4.5。低版本TS的AST缺少JSDocComment节点导致工具无法识别JSDoc中声明但未使用的字段。升级TS不是最优解可能引发其他兼容问题替代方案在tsconfig.json中启用skipLibCheck: true减少AST解析负担添加types: [node]到compilerOptions确保全局类型可用运行扫描时加--ts-version 4.5参数强制适配实测数据在TS 4.5项目中加--ts-version 4.5后幻影字段检出率从63%提升到89%漏报主要集中在泛型类型参数上这是TS 4.5本身的AST限制非工具缺陷。4.4 CI集成时“超时失败”——如何优雅处理大型项目10万行以上的项目跑deletion-test常超时。官方建议用--concurrency 1降低压力但这会让耗时翻倍。更优解是分片执行# 生成按目录分片的脚本 improve-codebase deletion-test --list-chunks 5 --output ./chunks/ # 得到chunk-0.json, chunk-1.json...共5个文件 # CI中并行执行 for i in {0..4}; do improve-codebase deletion-test --chunk ./chunks/chunk-$i.json done wait每个chunk文件包含该分片要测试的文件列表工具会自动聚合结果。实测某电商项目12万行分5片后总耗时从28分钟降至9分钟且内存占用稳定在1.2GB以内。4.5 “报告里全是红色警告团队不敢改”——渐进式治理的沟通话术技术负责人最怕工具扫出一堆问题却无法推动落地。我的经验是把报告转化为业务语言。例如不说“userUtils.tsDFS3.1需重构”而说“当前支付成功率下降2.3%根因是userUtils.validateEmail函数在订单创建时被同步调用而该函数包含DNS查询平均延迟420ms。重构后预计提升支付成功率1.8个百分点”附上工具生成的--impact-reportimprove-codebase deletion-test --impact-report --output ./reports/business-impact.md该报告会关联Jira ticket ID需在commit message中规范标注自动统计每个问题模块关联的线上故障次数。用数据说话比技术指标更有说服力。5. 工程师的真实体会这不是工具是新的代码审查范式用/improve-codebase-architecture三个月后我们团队的PR流程发生了本质变化。以前Code Review聚焦在“这段逻辑对不对”现在第一轮Review必问“Deletion Test通过了吗Shallow Module DFS值是多少”——这倒逼开发者在写代码时就思考这个函数真的需要export吗这个模块的依赖是不是太深了有意思的是AI代码助手的使用率反而提升了27%因为大家发现与其花两小时手写一个usePaginationhook不如让Copilot生成初稿再用improve-codebase快速砍掉70%的冗余代码最后只保留核心逻辑。Matt Pocock没在造一个“消灭AI”的工具他在建一座桥——让人类工程师和AI结对编程时能用同一套语言讨论架构健康度。上周我帮一个创业公司做技术尽调他们用这个工具扫描了核心服务代码发现AI生成的代码里有31%的类型定义是幻影17%的模块DFS超标。我把报告打印出来指着其中一页说“你们的技术债不是代码量大而是AI在替你们做决定时没人审核它的决策依据。”客户当场拍板采购我们的架构优化服务。所以别再问“AI会不会取代程序员”该问的是当AI写出代码时你有没有一套比它更懂架构的校验体系这套体系现在就摆在你面前。