代码重构工作流

Claude Code 工作流专题 · 安全高效地优化现有代码

专题:Claude Code 工作流系统学习

关键词:Claude Code, 重构, Refactoring, 代码优化, 依赖分析, 设计模式, 安全重构, 架构决策

本文目录

一、重构前的分析

代码重构并非盲目修改代码,而是需要充分的前置分析来确保每次改动都是安全的、可度量的。重构前分析的核心目标是回答三个问题:改什么、怎么改、影响谁。以下从四个维度展开重构前的分析工作。

1.1 代码依赖图分析

在开始任何重构之前,首先要绘制出目标模块的依赖关系图。依赖图能帮助开发者直观地看到模块之间的耦合程度、循环依赖风险以及潜在的影响链。对于前端项目,可以使用 Dependency-Cruiser 生成依赖图;对于后端项目,可以使用 NDepend 或 PyCGraph 等工具。以下是一个使用 TypeScript 编写的简易依赖分析脚本:

// dependency-analyzer.ts - 分析模块依赖关系 import * as fs from 'fs'; import * as path from 'path'; interface DependencyNode { name: string; filePath: string; imports: string[]; importedBy: string[]; get depth(): number; } class DependencyAnalyzer { private nodes: Map<string, DependencyNode> = new Map(); analyze(entryPoint: string): void { this.traverse(entryPoint); this.detectCycles(); this.reportCoupling(); } private traverse(filePath: string): Set<string> { const visited = new Set<string>(); const content = fs.readFileSync(filePath, 'utf-8'); const imports = this.extractImports(content); const node: DependencyNode = { name: path.basename(filePath), filePath, imports, importedBy: [], get depth() { return this.imports.length; } }; this.nodes.set(filePath, node); return visited; } private extractImports(content: string): string[] { const importRegex = /import\s+.*\s+from\s+['"]([^'"]+)['"]/g; const imports: string[] = []; let match; while ((match = importRegex.exec(content)) !== null) { imports.push(match[1]); } return imports; } private detectCycles(): void { const visited = new Set<string>(); const recursionStack = new Set<string>(); const dfs = (nodePath: string): boolean => { visited.add(nodePath); recursionStack.add(nodePath); const node = this.nodes.get(nodePath); if (!node) return false; for (const imp of node.imports) { if (!visited.has(imp)) { if (dfs(imp)) { console.warn(`检测到循环依赖: ${nodePath} -> ${imp}`); return true; } } else if (recursionStack.has(imp)) { console.error(`循环依赖风险: ${node.name} -> ${imp}`); return true; } } recursionStack.delete(nodePath); return false; }; for (const [filePath] of this.nodes) { if (!visited.has(filePath)) dfs(filePath); } } private reportCoupling(): void { let totalDependencies = 0; this.nodes.forEach((node) => { totalDependencies += node.imports.length + node.importedBy.length; }); const avgCoupling = totalDependencies / this.nodes.size; console.log(`平均模块耦合度: ${avgCoupling.toFixed(2)}`); } }

通过依赖分析工具,我们可以快速识别出高耦合模块和循环依赖点,为后续重构提供精准的目标定位。Claude Code 可以直接读取项目中的 import/require 语句,配合 /analyze-deps 技能自动生成依赖关系报告。

1.2 测试覆盖评估

重构的底气来自测试。在动手修改任何代码之前,必须对目标模块的测试覆盖率进行评估。低覆盖率的模块需要优先补充测试,否则重构后的回归风险极高。以下是一个测试覆盖评估的示例脚本:

# coverage-checker.py - 检查测试覆盖缺口 import json import subprocess from pathlib import Path class CoverageEvaluator: def __init__(self, project_root: str): self.project_root = Path(project_root) self.coverage_data = {} def run_coverage(self): """运行测试覆盖率工具并收集结果""" result = subprocess.run( ["pytest", "--cov=src", "--cov-report=json", "."], capture_output=True, text=True, cwd=self.project_root ) with open(self.project_root / "coverage.json") as f: self.coverage_data = json.load(f) def find_gaps(self, threshold: float = 0.8): """找出覆盖率低于阈值的模块""" gaps = [] for file_path, file_data in self.coverage_data.get("files", {}).items(): covered = file_data.get("covered_lines", 0) missing = file_data.get("missing_lines", 0) total = covered + missing if total == 0: continue rate = covered / total if rate < threshold: gaps.append({ "file": file_path, "coverage": round(rate * 100, 2), "missing_lines": missing, "risk": "高" if rate < 0.5 else "中" }) return sorted(gaps, key=lambda x: x["coverage"]) def print_report(self): gaps = self.find_gaps() print(f"共发现 {len(gaps)} 个覆盖率缺口") for gap in gaps: print(f" [{gap['risk']}] {gap['file']}: {gap['coverage']}%") # Claude Code 可以自动解析此报告并建议优先补充测试 evaluator = CoverageEvaluator(".") evaluator.run_coverage() evaluator.print_report()

最佳实践:在重构之前,使用 Claude Code 的 /test 命令自动运行测试套件并分析覆盖率报告。对于覆盖率低于 60% 的模块,建议先编写关键路径的测试用例,然后再进行重构。Claude Code 可以根据函数签名自动生成测试桩,大幅提高测试编写效率。

1.3 重构范围界定

明确重构的范围是控制风险的关键。大范围重构应当拆分为多个小步骤,每个步骤有清晰的目标和可度量的成果。范围界定可以采用"洋葱模型":最内层是目标函数或类,向外依次是模块、服务、系统。每个层次的重构策略和验证方式各不相同。

// scope-definer.ts - 重构范围界定工具 type RefactorScope = 'function' | 'class' | 'module' | 'service' | 'system'; interface RefactorPlan { scope: RefactorScope; targetFiles: string[]; estimatedAffectedFiles: number; preRequisites: string[]; validationStrategy: string; } const scopeDefinitions: Record<RefactorScope, RefactorPlan> = { 'function': { scope: 'function', targetFiles: [], estimatedAffectedFiles: 1, preRequisites: ['单元测试就绪'], validationStrategy: '单测验证' }, 'class': { scope: 'class', targetFiles: [], estimatedAffectedFiles: 2, preRequisites: ['单元测试就绪', '接口契约检查'], validationStrategy: '接口测试' }, 'module': { scope: 'module', targetFiles: [], estimatedAffectedFiles: 5, preRequisites: ['单元测试就绪', '集成测试就绪', '模块接口文档'], validationStrategy: '集成测试 + 契约测试' }, 'service': { scope: 'service', targetFiles: [], estimatedAffectedFiles: 15, preRequisites: ['所有测试就绪', 'API文档', '回滚计划'], validationStrategy: 'E2E测试 + 性能基准' }, 'system': { scope: 'system', targetFiles: [], estimatedAffectedFiles: 50, preRequisites: ['全量测试就绪', '灰度发布', '回滚方案'], validationStrategy: '全量回归 + 流量回放' } };

1.4 影响范围分析

影响范围分析需要追踪代码变更可能波及的所有调用链路。对于大型项目,这通常需要结合静态分析和动态追踪两种手段。静态分析通过扫描代码库找出所有引用点;动态追踪则通过运行时日志或链路追踪工具观察实际执行路径。使用 Claude Code 的 影响范围分析 技能,可以自动扫描整个代码库并生成影响报告:

# impact-analyzer.py - 变更影响范围分析 from dataclasses import dataclass, field from typing import List, Set import ast import os @dataclass class ImpactReport: changed_function: str direct_callees: List[str] = field(default_factory=list) indirect_callees: List[str] = field(default_factory=list) affected_files: Set[str] = field(default_factory=set) risk_level: str = "low" suggested_actions: List[str] = field(default_factory=list) class ImpactAnalyzer: def __init__(self, source_dir: str): self.source_dir = source_dir self.function_registry = {} def build_registry(self): """扫描整个代码库,构建函数调用注册表""" for root, _, files in os.walk(self.source_dir): for file in files: if file.endswith('.py'): filepath = os.path.join(root, file) with open(filepath) as f: tree = ast.parse(f.read()) for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): calls = [n.func.attr if isinstance(n, ast.Attribute) else n.func.id for n in ast.walk(node) if isinstance(n, ast.Call) and hasattr(n.func, 'id')] self.function_registry[node.name] = { "file": filepath, "calls": calls } def analyze(self, function_name: str, depth: int = 3) -> ImpactReport: """递归分析函数修改带来的影响面""" report = ImpactReport(changed_function=function_name) visited = set() def _traverse(name: str, current_depth: int): if name in visited or current_depth > depth: return visited.add(name) func_info = self.function_registry.get(name) if not func_info: return report.affected_files.add(func_info["file"]) for callee in func_info["calls"]: if current_depth == 0: report.direct_callees.append(callee) else: report.indirect_callees.append(callee) _traverse(callee, current_depth + 1) _traverse(function_name, 0) report.risk_level = "high" if len(report.affected_files) > 10 else \ "medium" if len(report.affected_files) > 3 else \ "low" return report

二、重构模式详解

Martin Fowler 在《重构:改善既有代码的设计》一书中系统总结了数十种重构手法。本节结合 Claude Code 的实际能力,重点介绍六种最常用且最适合 AI 辅助的重构模式。

2.1 提取方法(Extract Method)

提取方法是最基础也最常用的重构手法。当一段代码可以独立成方法时,提取出来不仅提高可读性,还为后续的重构铺平道路。Claude Code 能够自动识别长函数中的逻辑片段并建议提取点:

// 重构前:一个大而全的函数 function processOrder(order: Order): void { // 验证订单 if (!order.items || order.items.length === 0) { throw new Error('订单不能为空'); } if (order.total <= 0) { throw new Error('订单金额非法'); } if (!order.customer.email) { throw new Error('客户邮箱缺失'); } // 计算折扣 let discount = 0; if (order.total > 1000) discount = 0.1; if (order.customer.isVIP) discount = 0.2; if (order.couponCode) discount = 0.15; // 发送通知 const message = `订单 ${order.id} 已处理,实付: ${order.total * (1 - discount)}`; emailService.send(order.customer.email, '订单确认', message); smsService.send(order.customer.phone, message); } // 重构后:提取为四个独立方法 function processOrder(order: Order): void { validateOrder(order); const discount = calculateDiscount(order); const finalAmount = order.total * (1 - discount); notifyCustomer(order, finalAmount); } function validateOrder(order: Order): void { if (!order.items?.length) throw new Error('订单不能为空'); if (order.total <= 0) throw new Error('订单金额非法'); if (!order.customer?.email) throw new Error('客户邮箱缺失'); } function calculateDiscount(order: Order): number { if (order.couponCode) return 0.15; if (order.customer.isVIP) return 0.20; if (order.total > 1000) return 0.10; return 0; } function notifyCustomer(order: Order, amount: number): void { const message = `订单 ${order.id} 已处理,实付: ${amount}`; emailService.send(order.customer.email, '订单确认', message); smsService.send(order.customer.phone, message); }

Claude Code 技巧:选中大段代码后,输入提示 "/refactor "将这段代码提取为多个私有方法"",Claude Code 会自动分析代码块中的职责边界并生成提取方案。如果配合 --preview 标志,还可以预览 diff 后再决定是否应用。

2.2 重命名(Rename)

好的命名是代码自文档化的基础。重命名看似简单,实则需要谨慎处理跨文件引用。Claude Code 的重命名联动功能可以智能识别所有引用点并同步更新:

# 重构前:表意不清的命名 def calc(a, b, c): return (a + b) * c class Mgr: def __init__(self): self.d = {} def proc(self, k, v): self.d[k] = v def get_all(self): return list(self.d.values()) # 重构后:清晰准确的命名 def calculate_total_price(base_price: float, tax: float, quantity: int) -> float: return (base_price + tax) * quantity class CacheManager: def __init__(self): self._cache_store = {} def set(self, key: str, value: Any) -> None: self._cache_store[key] = value def get_all_values(self) -> List[Any]: return list(self._cache_store.values())

2.3 移动文件与模块拆分

随着项目规模增长,文件位置的合理性和模块边界的清晰度直接影响可维护性。移动文件不只是剪贴操作,还需要同步更新所有 import/require 路径。Claude Code 的跨文件变更能力可以一次性完成这些调整:

# 重构前:所有工具函数堆积在一个文件中 # utils/helpers.py (超过 2000 行) def format_date(dt): ... def calculate_tax(amount, rate): ... def send_email(to, subject, body): ... def validate_email(email): ... def generate_report(data, format): ... # 重构后:按职责拆分到独立模块 # utils/date_utils.py def format_date(dt): ... # utils/finance_utils.py def calculate_tax(amount, rate): ... def generate_report(data, format): ... # utils/email_utils.py def send_email(to, subject, body): ... def validate_email(email): ... # Claude Code 自动更新所有引用处的 import 语句 # from utils.helpers import send_email # → from utils.email_utils import send_email

2.4 拆分模块(Extract Module)

当单个模块承载了过多职责时,需要按照单一职责原则进行拆分。判断标准:如果一段代码的修改原因与同一模块中另一段代码的修改原因不同,就应该拆分开来。

// 重构前:臃肿的用户服务 class UserService { async createUser(data: CreateUserDto): Promise<User> { ... } async sendWelcomeEmail(email: string): Promise<void> { ... } async generateAvatarUrl(name: string): Promise<string> { ... } async checkPermission(userId: string, resource: string): Promise<boolean> { ... } async logLoginActivity(userId: string): Promise<void> { ... } async calculateUserStats(userId: string): Promise<UserStats> { ... } async exportUserReport(userId: string): Promise<Buffer> { ... } } // 重构后:按职责拆分为四个服务 class UserCreationService { async createUser(data: CreateUserDto): Promise<User> { ... } } class UserNotificationService { async sendWelcomeEmail(email: string): Promise<void> { ... } } class UserAuthorizationService { async checkPermission(userId: string, resource: string): Promise<boolean> { ... } } class UserAnalyticsService { async calculateUserStats(userId: string): Promise<UserStats> { ... } async exportUserReport(userId: string): Promise<Buffer> { ... } }

2.5 设计模式引入

引入设计模式的目标是解决代码中的特定问题,而不是为了用模式而用模式。Claude Code 能够分析代码中的坏味道并推荐合适的设计模式。以下是一个策略模式替代条件分支的典型案例:

// 重构前:if-else 地狱 function calculateShipping(order: Order): number { if (order.type === 'standard') { return order.weight * 1.5 + 5; } else if (order.type === 'express') { return order.weight * 3 + 10; } else if (order.type === 'overnight') { return order.weight * 5 + 20; } else if (order.type === 'international') { return order.weight * 8 + order.customsFee; } throw new Error('未知配送类型'); } // 重构后:策略模式 interface ShippingStrategy { calculate(order: Order): number; } class StandardShipping implements ShippingStrategy { calculate(order: Order): number { return order.weight * 1.5 + 5; } } class ExpressShipping implements ShippingStrategy { calculate(order: Order): number { return order.weight * 3 + 10; } } class OvernightShipping implements ShippingStrategy { calculate(order: Order): number { return order.weight * 5 + 20; } } class InternationalShipping implements ShippingStrategy { calculate(order: Order): number { return order.weight * 8 + order.customsFee; } } // 策略注册表 const shippingStrategies = new Map<string, ShippingStrategy>([ ['standard', new StandardShipping()], ['express', new ExpressShipping()], ['overnight', new OvernightShipping()], ['international', new InternationalShipping()], ]); function calculateShipping(order: Order): number { const strategy = shippingStrategies.get(order.type); if (!strategy) throw new Error('未知配送类型'); return strategy.calculate(order); }

2.6 API 重构

API 重构是风险最高的重构类型之一,因为 API 通常有多个消费者。安全地进行 API 重构需要遵循"增-迁-删"三步策略:先新增接口、再迁移消费者、最后废弃旧接口。Claude Code 可以同时扫描前端和后端代码,确保所有迁移点都被覆盖:

// API 重构模式:版本兼容迁移 // 步骤1:新增带版本号的 API(保留旧 API) // @deprecated 使用 POST /api/v2/users app.post('/api/v1/users', createUserV1); app.post('/api/v2/users', createUserV2); // 步骤2:在旧 API 中添加废弃标记和日志 app.post('/api/v1/users', async (req, res) => { console.warn('[DEPRECATED] /api/v1/users 被调用,来自: ' + req.ip); return createUserV1(req, res); }); // 步骤3:Claude Code 扫描所有调用方 // 正则匹配:find . -type f -name "*.ts" -exec grep -l "/api/v1/users" {} \; // 步骤4:批量更新所有引用到 v2 // fetch('/api/v1/users', { ... }) → fetch('/api/v2/users', { ... }) // 步骤5:确认所有消费者迁移后,删除旧 API // app.post('/api/v1/users', createUserV1); // 删除这行

三、安全重构策略

重构失败的根本原因往往不是技术问题,而是策略问题。以下四种安全策略构成了重构工作的"安全带",确保即使在出现问题时也能快速恢复。

3.1 小步提交原则

每次重构只做一件小事,然后提交。小步提交有双重意义:一是降低每次变更的风险面,二是让代码评审更加聚焦。推荐的分隔粒度是:一次提交只涉及一个重构手法。例如,不要在"提取方法"的提交中混入"重命名变量"。以下是一个示例提交序列:

# 小步提交的典型序列 # 提交1:添加测试(测试先行) git commit -m "test: 为 OrderService.calculateTotal 添加单元测试" # 提交2:提取方法(纯重构,不改变行为) git commit -m "refactor: 从 OrderService.processOrder 中提取 validateOrder 方法" # 提交3:重命名 git commit -m "refactor: 将 calc 重命名为 calculateTotalPrice" # 提交4:移动文件 git commit -m "refactor: 将财务计算函数迁移到 utils/finance/ 目录" # 提交5:引入设计模式 git commit -m "refactor: 使用策略模式替代配送费计算中的条件分支" # 每一步之后都运行测试 git commit -m "chore: 更新快照测试" # 如果测试需要更新

提交信息规范:推荐使用 Conventional Commits 格式(如 refactor: xxxtest: xxx),便于生成变更日志和自动化发布流程。Claude Code 可以自动根据 diff 内容生成符合规范的提交信息。

3.2 测试先行策略

测试先行(Test-First)是安全重构的基石。在修改任何代码之前,先为待重构的函数编写测试用例,确保重构前后测试结果一致。这种方式本质上是在建立"行为不变性"的契约:

// 测试先行示例:重构前的测试(使用 Vitest) import { describe, it, expect } from 'vitest'; import { processOrder } from './order-service'; describe('processOrder', () => { it('应该拒绝空订单', () => { expect(() => processOrder({ items: [] })).toThrow('订单不能为空'); }); it('应该拒绝金额为0的订单', () => { expect(() => processOrder({ items: ['item1'], total: 0 })) .toThrow('订单金额非法'); }); it('VIP客户应该享受20%折扣', () => { const result = processOrder({ items: ['item1'], total: 1000, customer: { isVIP: true, email: 'test@test.com' } }); expect(result.finalAmount).toBe(800); }); it('优惠券代码应该提供15%折扣', () => { const result = processOrder({ items: ['item1'], total: 1000, customer: { email: 'test@test.com' }, couponCode: 'SAVE15' }); expect(result.finalAmount).toBe(850); }); it('普通订单满1000应享受10%折扣', () => { const result = processOrder({ items: ['item1'], total: 1000, customer: { email: 'test@test.com' } }); expect(result.finalAmount).toBe(900); }); });

3.3 渐进重构策略

渐进重构(Strangler Fig Pattern)是指通过逐步替换的方式,在不中断现有功能的前提下完成重构。这种策略特别适合大型系统的模块替换。核心思想是"并行运行—流量切换—旧模块下线":

// 渐进重构:绞杀者模式示例 import { legacyPaymentProcessor, newPaymentProcessor } from './payment'; // 阶段1:路由层做流量分发,新老并行 async function paymentRouter(req: PaymentRequest): Promise<PaymentResult> { const useNew = await featureFlag.isEnabled('new-payment', req.userId); if (useNew) { return newPaymentProcessor.process(req); } const legacyResult = await legacyPaymentProcessor.process(req); // 双写:将老系统的请求发送到新系统做比对(静默模式) newPaymentProcessor.process(req).catch(() => {}); // 仅记录,不阻塞 return legacyResult; } // 阶段2:按比例逐步放大新系统流量 // Claude Code 可以自动修改 featureFlag 的配置: // 第一周:5% 用户使用新系统 // 第二周:20% 用户使用新系统 // 第三周:50% 用户使用新系统 // 第四周:100% 用户使用新系统 // 阶段3:验证稳定后,移除旧系统代码 // - 删除 legacyPaymentProcessor 相关文件 // - 简化 paymentRouter 为直接调用 newPaymentProcessor // - 清理 featureFlag 配置

3.4 回滚计划

再周全的计划也有意外。提前准备好回滚计划是专业工程师的标志。回滚计划应当包含:回滚触发条件、回滚步骤、回滚后的验证方法。使用 Git 的回滚策略通常有两种:git revert(保留历史)和 git reset(丢弃历史),推荐使用 git revert 以保留审计线索。

# 回滚计划模板 # ================== # 触发条件:任何回归测试失败、性能退化超过 5%、API 错误率上升超过 1% # 方案A:git revert(推荐,保留历史) git revert HEAD --no-edit git push origin main # 方案B:回退到指定版本(紧急情况) git log --oneline -10 # 查看最近提交 git revert <commit-hash> --no-edit # 逐个 revert 重构提交 # 方案C:使用 git bisect 定位问题提交 git bisect start git bisect bad # 当前版本有问题 git bisect good <last-known-good> # 上次好的版本 # 然后 git bisect 会二分查找定位问题提交 # 回滚后的验证步骤 npm run test # 回归测试 npm run build # 构建检查 curl -f http://localhost:3000/health # 健康检查

四、Claude Code 自动重构

Claude Code 作为 AI 辅助编程工具,在代码重构领域提供了强大的自动化能力。合理利用这些能力,可以将重构效率提升数倍。本节详细介绍 Claude Code 的核心重构功能。

4.1 提示模板

Claude Code 支持通过自然语言描述重构目标,自动完成代码修改。编写高质量的重构提示是发挥 AI 能力的关键。以下是一些经过验证的提示模板:

// 提示模板1:提取方法 // "从 processOrder 函数中提取订单验证逻辑为独立的 validateOrder 方法。 // 新方法应接受 Order 参数,抛出明确的错误信息。 // 保持原有函数签名不变,内部调用新方法。" // 提示模板2:类型化重构 // "将以下 JavaScript 代码转换为 TypeScript: // 1. 为所有函数参数和返回值添加类型注解 // 2. 创建 interfaces 文件存放共享类型 // 3. 将 any 类型替换为具体的联合类型或泛型" // 提示模板3:性能优化 // "分析 UserService.getUserProfile 方法中的 N+1 查询问题, // 使用 JOIN 查询或 DataLoader 模式进行批量加载优化。 // 保持接口兼容性,并用测试验证性能提升。" // 提示模板4:错误处理统一 // "将项目中所有 try-catch 块统一为使用 AppError 类的错误处理模式。 // 要求:所有业务异常都包含 errorCode、httpStatus、message 三个字段。 // 移除直接使用 new Error() 的方式。"

提示工程黄金法则:给 Claude Code 的提示应当包含三个要素——当前状态(现状)、目标状态(期望)、约束条件(边界)。例如:"当前代码使用回调(现状),希望改为 async/await(期望),不能改变外部 API 签名(约束)。"

4.2 批量修改与跨文件变更

Claude Code 最强大的能力之一是在多个文件中执行一致的修改。这在涉及 API 重命名、类型迁移、导入路径更新等场景中尤为实用:

// 跨文件批量修改示例:统一导入路径 // 变更前 // 变更后 import { User } from '../types/models/user'; import { User } from '@types/user'; import { Payment } from '../types/models/payment'; import { Payment } from '@types/payment'; import { Order } from '../types/models/order'; import { Order } from '@types/order'; // Claude Code 可以一次性处理: // 1. 搜索所有文件中包含 '../types/models/' 的模式 // 2. 根据新路径规则生成替换方案 // 3. 逐个文件应用修改(或批量应用) // 4. 验证所有 import 路径的有效性 // 另一个场景:重命名导出函数 // export function calculateTotal → export function computeTotal // Claude Code 会自动更新所有引用此函数的文件 // 使用 Claude Code 的命令行参数: // claude -p "将所有文件中的 calculateTotal 重命名为 computeTotal"

风险提示:批量修改虽然高效,但一定要在修改前确保所有文件都已提交(git commit),这样不满意时可以轻松恢复。修改后立即运行全量测试套件,不要等到下次提交前才发现问题。

4.3 重命名联动

Claude Code 的"重命名联动"功能可以跨越文件边界,智能识别深层引用关系。与传统的全局搜索替换不同,Claude Code 能够理解代码的 AST 结构,从而避免误替换同名但不同含义的标识符:

// 重命名联动:类名变更 // 重命名前: export class DataProv { async fetch(): Promise<Data> { ... } } // 引用点1 - src/services/user-service.ts import { DataProv } from '../providers/data-prov'; const provider = new DataProv(); // 引用点2 - src/contexts/data-context.tsx import { DataProv } from '../providers/data-prov'; class DataContext { private prov: DataProv; } // Claude Code 重命名后: // 1. 将 DataProv 重命名为 DataProvider(类定义文件) // 2. 更新所有 import 语句中的 DataProv → DataProvider // 3. 更新所有 new DataProv() → new DataProvider() // 4. 更新所有类型标注中的 DataProv → DataProvider // 5. 更新文件名 data-prov.ts → data-provider.ts(可选) // 6. 更新文件中的重新导出语句 // 智能过滤:以下情况不会被错误修改 // - 变量名 dataProv(大小写不同) // - 字符串字面量中的 "DataProv" // - 注释中的 DataProv(取决于配置)

4.4 重构任务自动化脚本

可以将常见的重构任务编写为可复用的自动化脚本,在 Claude Code 中通过技能(Skill)的形式调用:

# claude-refactor-script.sh - 重构自动化助手 # 用法: ./claude-refactor.sh TARGET_DIR=${1:-.} DATE=$(date +"%Y%m%d%H%M%S") BRANCH_NAME="refactor/$DATE" echo "=== Claude Code 重构自动化脚本 ===" # 步骤1:创建重构分支 git checkout -b "$BRANCH_NAME" # 步骤2:运行测试,保存基线 npm test -- --reporter json > "test-baseline-$DATE.json" echo "测试基线已保存" # 步骤3:运行 lint,保存基线 npm run lint -- --format json > "lint-baseline-$DATE.json" # 步骤4:使用 Claude Code 执行重构 claude -p "分析 $TARGET_DIR 目录中的代码,识别长函数(超过30行) 和重复代码块,建议重构方案。" # 步骤5:回归测试 npm test -- --reporter json > "test-result-$DATE.json" echo "重构后测试完成" # 步骤6:对比结果 node -e " const baseline = require('./test-baseline-$DATE.json'); const result = require('./test-result-$DATE.json'); const pass = baseline.numPassedTests === result.numPassedTests; console.log(pass ? '全部测试通过,重构安全' : '测试结果有变化,请检查'); process.exit(pass ? 0 : 1); "

五、重构验证

重构的终极检验标准是:代码行为未发生变化。验证重构的正确性需要从多个维度进行系统性的检查。

5.1 编译检查

对于 TypeScript、Java、Go 等静态类型语言,编译是重构安全的第一道防线。在提交重构代码之前,确保项目可以通过编译。Claude Code 可以在修改代码后自动触发编译检查:

# TypeScript 严格模式编译检查 npx tsc --noEmit --strict # 常见编译错误及修复: // 错误1:类型不兼容 // Type 'string | undefined' is not assignable to type 'string' // 修复:添加类型守卫或使用默认值 const email: string = user.email ?? ''; // 错误2:导入路径错误(文件移动后) // Cannot find module '../utils/old-location' // 修复:更新为新的导入路径 import { formatDate } from '../utils/date/format'; // 错误3:接口不匹配(API 重构后) // Property 'oldField' does not exist on type 'NewType' // 修复:使用新接口属性名 // const name = data.oldField; // 错误 const name = data.newField; // 正确

5.2 测试通过率对比

重构后测试不应出现新增的失败用例。最佳实践是重构前后分别运行测试,对比两个报告。以下是一个对比脚本:

# test-compare.sh - 重构前后测试结果对比 # 保存重构前的测试结果 npm test 2>&1 | tee before-refactor.log PASSED_BEFORE=$(grep -c "PASS" before-refactor.log) FAILED_BEFORE=$(grep -c "FAIL" before-refactor.log) # 执行重构(由 Claude Code 完成) claude -p "对 src/services/ 目录进行重构,提取重复逻辑" # 运行重构后的测试 npm test 2>&1 | tee after-refactor.log PASSED_AFTER=$(grep -c "PASS" after-refactor.log) FAILED_AFTER=$(grep -c "FAIL" after-refactor.log) # 对比结果 echo "====== 重构前后测试对比 ======" echo "重构前: $PASSED_BEFORE 通过, $FAILED_BEFORE 失败" echo "重构后: $PASSED_AFTER 通过, $FAILED_AFTER 失败" if [ "$FAILED_AFTER" -gt "$FAILED_BEFORE" ]; then echo "警告: 重构新增了测试失败!请检查以下差异:" diff <(grep "FAIL" before-refactor.log) <(grep "FAIL" after-refactor.log) exit 1 else echo "通过: 测试覆盖率未退化" fi

5.3 代码质量对比

代码质量度量指标可以帮助量化重构的效果。常用的质量指标包括圈复杂度(Cyclomatic Complexity)、代码重复率、继承深度等。重构应当降低这些指标:

// 质量指标对比表单 // 使用 Plato 或 SonarQube 进行量化分析 // 生成质量报告的命令 npx plato -r -d report src/ // 关键质量指标示例: // =============================================================== // 指标 重构前 重构后 变化 目标 // --------------------------------------------------------------- // 圈复杂度(平均) 8.3 4.1 -51% <= 10 // 代码重复率 12.5% 2.3% -82% <= 5% // 函数平均行数 42 18 -57% <= 30 // 注释密度 8% 15% +88% >= 10% // 技术债务(小时) 68 22 -68% 持续降低 // ===============================================================

5.4 性能基准对比

重构不应引入显著的性能退化。对于性能敏感的系统,应该在重构前后运行基准测试(Benchmark),确保关键路径的执行时间没有恶化:

// performance-benchmark.ts - 性能基准对比 import { Bench } from 'tinybench'; async function runBenchmark() { const bench = new Bench({ name: '重构性能对比', time: 100 }); const order = generateTestOrder(100); bench .add('重构前 - processOrder', () => { legacyProcessOrder(order); }) .add('重构后 - processOrder', () => { refactoredProcessOrder(order); }); await bench.run(); const results = bench.tasks.map(task => ({ name: task.name, mean: task.result!.mean, p99: task.result!.p99, rme: task.result!.rme })); const before = results.find(r => r.name.includes('重构前'))!; const after = results.find(r => r.name.includes('重构后'))!; const diff = ((after.mean - before.mean) / before.mean * 100).toFixed(2); console.log(`性能变化: ${diff}%`); if (Number(diff) > 5) { console.warn(`警告: 性能退化超过 5%!`); } else { console.log(`通过: 性能在可接受范围内`); } } runBenchmark();

六、重构文档记录

重构的成果不仅体现在代码层面,还需要通过文档的形式沉淀下来。良好的文档记录能够帮助团队理解重构的背景、过程和决策依据。

6.1 变更日志(Changelog)

每次重构应当在变更日志中记录:涉及的文件、重构的类型、预期效果。推荐的 CHANGELOG.md 格式:

# CHANGELOG.md ## [2.4.0] - 2026-05-08 ### 重构 (Refactoring) - **核心模块**: 从 `OrderService` 中提取 `OrderValidator`、`DiscountCalculator`、`NotificationService` 三个独立服务 - 圈复杂度从 8.3 降至 4.1 - 函数平均长度从 42 行降至 18 行 - 涉及文件: `order-service.ts`, `order-validator.ts`, `discount-calculator.ts`, `notification-service.ts` - **支付模块**: 使用策略模式替代配送费计算中的条件分支 - 消除 4 层嵌套 if-else - 新增配送策略类: `StandardShipping`, `ExpressShipping`, `OvernightShipping`, `InternationalShipping` - 涉及文件: `shipping.ts`, `shipping-strategies.ts` - **工具函数**: 将 `utils/helpers.ts`(2000+行)拆分为 6 个独立模块 - `date-utils.ts`, `finance-utils.ts`, `email-utils.ts`, `validation-utils.ts`, `report-utils.ts`, `string-utils.ts` - 所有 import 路径已自动更新(Claude Code 联动)

6.2 架构决策记录(ADR)

ADR 是记录重大架构决策的标准方式。每次有影响力的重构,都应当创建或更新 ADR。ADR 的推荐格式如下:

# ADR-2026-05-08-001: 引入策略模式替代配送条件分支 ## 状态 已采纳 ## 背景 配送费计算函数 calculateShipping 使用多层 if-else 判断配送类型, 每次新增配送方式都需要修改核心函数,违反了开放-关闭原则。 ## 决策 采用策略模式(Strategy Pattern),将每种配送方式的计费逻辑 封装到独立的策略类中,通过 Map 注册表进行路由。 ## 影响 - 正面:新增配送方式无需修改现有代码,只需新增策略类并注册 - 正面:单元测试可以独立测试每种配送策略 - 负面:增加了类的数量(从 1 个函数变为 5 个类) - 权衡:代码量略有增加,但可维护性显著提升 ## 替代方案 1. 使用 switch 语句(与 if-else 类似,不解决根本问题) 2. 使用函数映射表(轻量级方案,但失去类型安全的优势) 3. 使用装饰器模式(适合叠加行为,不适合替代策略) ## 相关重构 - 提取方法:从 calculateShipping 中提取了验证逻辑 - 重命名:ShippingType 枚举值从缩写改为全称

6.3 代码注释更新

重构后应当同步更新相关代码注释,删除过时的注释、补充新的说明。特别要注意的是:注释应当解释"为什么这样做",而非重复"做了什么"。Claude Code 可以协助这个过程:

// 过时的注释(重构后删除或修改) // 之前的注释:// 计算折扣 - 循环遍历所有商品 // 重构后有意义的注释: // 使用策略模式计算折扣,每个策略只负责一种折扣规则。 // 策略注册在 construction 中完成,便于单元测试和扩展。 class DiscountCalculator { private strategies: DiscountStrategy[]; constructor() { // 注:策略注册顺序决定优先级,VIP 策略优先于满减策略 this.strategies = [ new VipDiscountStrategy(), new CouponDiscountStrategy(), new VolumeDiscountStrategy(), ]; } calculate(order: Order): number { return this.strategies .map(s => s.apply(order)) .reduce((max, current) => Math.max(max, current), 0); } }

ADR 自动生成:Claude Code 可以根据重构的 diff 自动生成 ADR 草稿。在完成重构后,使用命令 claude -p "根据最近的 git diff 生成 ADR 文档,存储在 docs/adr/ 目录下" 即可获得初步的 ADR 内容,再根据实际情况微调即可。

七、最佳实践与总结

7.1 黄金准则

重构的黄金准则:每次重构只改变一件事——要么改变结构不改变行为(重构),要么改变行为不改变结构(功能开发)。二者决不可混入同一次提交。

7.2 完整工作流图谱

将本文介绍的所有环节串联起来,形成一个完整的可执行工作流:

# 完整 Claude Code 重构工作流 # 步骤1: 拉取最新代码,创建重构分支 git checkout main && git pull && git checkout -b refactor/order-service-improvement # 步骤2: 运行基线测试和覆盖率 npm test && npm run coverage # 步骤3: 使用 Claude Code 进行依赖分析 claude -p "分析 src/services/ 目录的模块依赖关系,找出高耦合模块并输出报告" # 步骤4: Claude Code 执行重构 claude -p "重构 OrderService:将验证、折扣计算、通知逻辑提取为独立类" # 步骤5: 编译检查 npx tsc --noEmit # 步骤6: 回归测试 npm test # 步骤7: 质量对比 npx plato -r -d report src/ # 步骤8: 提交代码 git add . && git commit -m "refactor: 提取 OrderService 的子模块" # 步骤9: 创建 ADR claude -p "根据最近的 git diff 生成 ADR 文档" # 步骤10: 推送并创建 PR git push -u origin refactor/order-service-improvement gh pr create --title "refactor: 优化 OrderService 架构"

7.3 常见陷阱与对策

常见陷阱 后果 对策
重构范围过大 代码大面积冲突,难以 Code Review 使用范围界定工具,拆分为多次提交
缺少测试保护 引入回归缺陷而不知 测试先于重构,或先补充测试
混入功能变更 重构无法回滚,功能耦合难调试 严格分离重构提交和功能提交
依赖图理解不足 修改波及意料之外的模块 重构前运行依赖分析工具
忽略文档同步 架构图与代码不一致 在重构工作流中加入文档步骤
过度设计 引入了不必要的抽象层 YAGNI 原则:只重构当前需要的

7.4 总结

代码重构是软件工程中不可或缺的实践活动,而 Claude Code 为重构工作流注入了 AI 驱动的自动化能力。通过系统性地运用本文介绍的分析工具、重构模式、安全策略、自动化能力和验证方法,开发者可以在保证代码质量的前提下,持续优化项目架构。

记住重构的终极目标不是让代码变得更"漂亮",而是让代码更容易理解和修改。每一次重构都是对过去决策的反思,也是为未来铺平道路。将重构纳入日常开发习惯,配合 Claude Code 的高效辅助,你的代码库将始终保持健康、可维护的状态。

延伸阅读:

1. Martin Fowler - 《重构:改善既有代码的设计(第2版)》

2. Joshua Kerievsky - 《重构与模式》

3. Michael Feathers - 《修改代码的艺术》

4. Claude Code 官方文档 - 自动重构功能参考