关于 refactoring
refactoring 是面向开发者的代码重构技能,强调在保持外部行为不变的前提下,系统地改善代码结构与可维护性。内容涵盖抽取、重命名、类型改进和依赖解耦。
主要功能
- 01分析代码异味与依赖关系
- 02拆分函数和组件
- 03整理命名与文件结构
- 04简化条件逻辑
- 05改进类型并解耦依赖
适用场景
- 01消除重复代码
- 02拆解过长的函数或组件
- 03降低 TypeScript 中 any 的使用
- 04整理复杂条件与文件布局
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mgd34msu/ goodvibes-plugin --skill refactoring安装后在 Claude Code 中输入/refactoring确认已加载。
附带文件references/refactoring-patterns.mdscripts/validate-refactoring.sh
详细介绍
资源
scripts/
validate-refactoring.sh
references/
refactoring-patterns.md
重构质量技能
此技能将教你如何使用 GoodVibes 精准工具安全、系统地重构代码。重构可以在不改变外部行为的情况下改进代码结构和可维护性,加快未来的开发速度并减少错误。
何时使用此技能
在以下情况下加载此技能:
- 需要消除代码重复
- 函数或组件变得过于庞大
- 需要改进类型安全(减少
any的使用) - 条件逻辑变得复杂且难以理解
- 需要重新组织文件和文件夹结构
- 依赖关系紧密耦合,需要反转依赖
- 需要优化数据库架构或查询
- 代码异味正在拖慢开发
触发语句:“重构这段代码”“减少重复”“提取函数”“简化条件逻辑”“改进类型”“重新组织”“清理代码”。
核心工作流
阶段 1:分析——了解当前状态
重构之前,先梳理当前代码结构并找出重构机会。
步骤 1.1:识别代码异味
使用 discover 找出需要重构的常见代码异味。
discover:
queries:
# Large files (> 300 lines)
- id: large_files
type: glob
patterns: ["src/**/*.{ts,tsx,js,jsx}"]
# Code duplication
- id: duplicate_patterns
type: grep
pattern: "(function|const|class)\\s+\\w+"
glob: "**/*.{ts,tsx,js,jsx}"
# Type safety issues
- id: any_usage
type: grep
pattern: ":\\s*any(\\s|;|,|\\))"
glob: "**/*.{ts,tsx}"
# Complex conditionals
- id: nested_conditions
type: grep
pattern: "if\\s*\\(.*if\\s*\\("
glob: "**/*.{ts,tsx,js,jsx}"
verbosity: files_only
这会揭示:
- 需要拆分的文件
- 重复的代码模式
- 类型安全方面的不足
- 复杂的条件逻辑
步骤 1.2:了解依赖关系
使用 precision_grep 梳理代码在整个代码库中的使用情况。
precision_grep:
queries:
- id: function_usage
pattern: "importFunctionName\\("
glob: "**/*.{ts,tsx,js,jsx}"
output:
format: locations
verbosity: standard
这很重要,因为:
- 重构需要更新所有调用点
- 必须提前识别会造成破坏的更改
- 可以安全地移除未使用的代码
步骤 1.3:确保测试存在
只有在测试能够验证行为保持不变时,重构才是安全的。
discover:
queries:
- id: test_files
type: glob
patterns: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"]
- id: source_files
type: glob
patterns: ["src/**/*.{ts,tsx}"]
verbosity: files_only
关键规则:
- 如果没有测试,请在重构之前编写测试
- 测试可作为安全网,确保行为保持不变
- 在重构前后都运行测试
阶段 2:提取函数/组件
大型函数和组件难以测试和维护。提取可复用的部分。
步骤 2.1:找出提取机会
寻找重复的逻辑,或只负责一项工作的代码块。
precision_read:
files:
- path: "src/components/UserProfile.tsx"
extract: symbols
verbosity: standard
适合提取的内容:
- 只执行一项特定任务的代码块
- 多处重复的逻辑
- 超过 50 行的函数
- 承担多项职责的 React 组件
步骤 2.2:提取函数
之前(重复的验证逻辑):
// In user-routes.ts
export async function POST(request: Request) {
const body = await request.json();
if (!body.email || !body.email.includes('@')) {
return Response.json({ error: 'Invalid email' }, { status: 400 });
}
// ... rest of logic
}
// In profile-routes.ts
export async function PUT(request: Request) {
const body = await request.json();
if (!body.email || !body.email.includes('@')) {
return Response.json({ error: 'Invalid email' }, { status: 400 });
}
// ... rest of logic
}
之后(提取验证逻辑):
// lib/validation.ts
import { z } from 'zod';
export const emailSchema = z.string().email();
export function validateEmail(email: unknown): { valid: true; email: string } | { valid: false; error: string } {
const result = emailSchema.safeParse(email);
if (!result.success) {
return { valid: false, error: 'Invalid email format' };
}
return { valid: true, email: result.data };
}
// user-routes.ts
import { validateEmail } from '@/lib/validation';
export async function POST(request: Request) {
const body = await request.json();
const emailResult = validateEmail(body.email);
if (!emailResult.valid) {
return Response.json({ error: emailResult.error }, { status: 400 });
}
// ... rest of logic using emailResult.email
}
使用 precision_edit 执行提取:
precision_edit:
operations:
- action: replace
path: "src/api/user-routes.ts"
old_text: |
const body = await request.json();
if (!body.email || !body.email.includes('@')) {
return Response.json({ error: 'Invalid email' }, { status: 400 });
}
new_text: |
const body = await request.json();
const emailResult = validateEmail(body.email);
if (!emailResult.valid) {
return Response.json({ error: emailResult.error }, { status: 400 });
}
verbosity: minimal
步骤 2.3:提取 React 组件
之前(承担多项职责的大型组件):
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null);
const [posts, setPosts] = useState<Post[]>([]);
const [followers, setFollowers] = useState<User[]>([]);
useEffect(() => {
fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
fetch(`/api/users/${userId}/posts`).then(r => r.json()).then(setPosts);
fetch(`/api/users/${userId}/followers`).then(r => r.json()).then(setFollowers);
}, [userId]);
if (!user) return <div>Loading...</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
<div>
<h2>Posts</h2>
{posts.map(post => (
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
))}
</div>
<div>
<h2>Followers</h2>
{followers.map(follower => (
<div key={follower.id}>
<img src={follower.avatar} alt="" />
<span>{follower.name}</span>
</div>
))}
</div>
</div>
);
}
之后(提取组件和钩子):
// hooks/useUser.ts
export function useUser(userId: string) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, [userId]);
return { user, loading };
}
// components/UserPosts.tsx
export function UserPosts({ userId }: { userId: string }) {
const [posts, setPosts] = useState<Post[]>([]);
useEffect(() => {
fetch(`/api/users/${userId}/posts`).then(r => r.json()).then(setPosts);
}, [userId]);
return (
<div>
<h2>Posts</h2>
{posts.map(post => (
<PostCard key={post.id} post={post} />
))}
</div>
);
}
// components/UserFollowers.tsx
export function UserFollowers({ userId }: { userId: string }) {
const [followers, setFollowers] = useState<User[]>([]);
useEffect(() => {
fetch(`/api/users/${userId}/followers`).then(r => r.json()).then(setFollowers);
}, [userId]);
return (
<div>
<h2>Followers</h2>
{followers.map(follower => (
<FollowerCard key={follower.id} user={follower} />
))}
</div>
);
}
// components/UserProfile.tsx
function UserProfile({ userId }: { userId: string }) {
const { user, loading } = useUser(userId);
if (loading || !user) return <div>Loading...</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
<UserPosts userId={userId} />
<UserFollowers userId={userId} />
</div>
);
}
优点:
- 每个组件只负责一项职责
- 更容易单独测试
- 可在整个应用中复用
- 性能更佳(可以单独记忆化各部分)
阶段 3:重命名和重新组织
命名不佳、文件组织混乱会拖慢开发。
步骤 3.1:改进命名
使用描述性名称重命名变量、函数和文件。
不佳的命名:
function getData(id: string) { ... } // Too generic
const x = getUserById(userId); // Unclear abbreviation
let flag = true; // What does this flag represent?
良好的命名:
function getUserProfile(userId: string) { ... } // Specific, clear intent
const userProfile = getUserById(userId); // Descriptive
let isEmailVerified = true; // Boolean naming convention
使用 precision_edit 的 replace_all 进行重命名:
precision_edit:
operations:
- action: replace
path: "src/lib/user.ts"
old_text: "function getData"
new_text: "function getUserProfile"
replace_all: true
verbosity: minimal
步骤 3.2:重新组织文件结构
使用基于功能或基于层次的结构,将相关文件归类在一起。
之前(扁平结构):
src/
user-routes.ts
user-service.ts
user-repository.ts
post-routes.ts
post-service.ts
post-repository.ts
之后(基于功能的结构):
src/
features/
users/
api/
routes.ts
services/
user-service.ts
repositories/
user-repository.ts
types/
user.types.ts
index.ts # Barrel export
posts/
api/
routes.ts
services/
post-service.ts
repositories/
post-repository.ts
types/
post.types.ts
index.ts
优点:
- 相关代码集中在一起
- 更容易查找文件
- 功能边界清晰
- Barrel 导出简化导入
使用 precision_write 创建 Barrel 导出:
precision_write:
files:
- path: "src/features/users/index.ts"
content: |
export * from './types/user.types';
export * from './services/user-service';
export * from './repositories/user-repository';
verbosity: minimal
阶段 4:简化条件逻辑
嵌套条件和复杂的布尔逻辑容易出错。
步骤 4.1:使用守卫子句
之前(嵌套条件):
function processOrder(order: Order) {
if (order.status === 'pending') {
if (order.items.length > 0) {
if (order.paymentConfirmed) {
// Process order
return processPayment(order);
} else {
throw new Error('Payment not confirmed');
}
} else {
throw new Error('Order has no items');
}
} else {
throw new Error('Order is not pending');
}
}
之后(守卫子句):
function processOrder(order: Order) {
if (order.status !== 'pending') {
throw new Error('Order is not pending');
}
if (order.items.length === 0) {
throw new Error('Order has no items');
}
if (!order.paymentConfirmed) {
throw new Error('Payment not confirmed');
}
return processPayment(order);
}
优点:
- 结构更扁平,更易阅读
- 优先检查错误条件
- 正常执行路径位于末尾
第 4.2 步:将条件逻辑提取为命名函数
之前(复杂的布尔逻辑):
if (user.role === 'admin' || (user.role === 'moderator' && user.permissions.includes('delete')) || user.id === post.authorId) {
deletePost(post.id);
}
之后(命名函数):
function canDeletePost(user: User, post: Post): boolean {
if (user.role === 'admin') return true;
if (user.role === 'moderator' && user.permissions.includes('delete')) return true;
if (user.id === post.authorId) return true;
return false;
}
if (canDeletePost(user, post)) {
deletePost(post.id);
}
优点:
- 代码自解释
- 逻辑可复用
- 更易测试
第 4.3 步:对复杂条件逻辑使用策略模式
之前(冗长的 switch 语句):
function calculateShipping(order: Order): number {
switch (order.shippingMethod) {
case 'standard':
return order.weight * 0.5;
case 'express':
return order.weight * 1.5 + 10;
case 'overnight':
return order.weight * 3 + 25;
case 'international':
return order.weight * 5 + 50;
default:
throw new Error('Unknown shipping method');
}
}
之后(策略模式):
interface ShippingStrategy {
calculate(weight: number): number;
}
class StandardShipping implements ShippingStrategy {
calculate(weight: number): number {
return weight * 0.5;
}
}
class ExpressShipping implements ShippingStrategy {
calculate(weight: number): number {
return weight * 1.5 + 10;
}
}
class OvernightShipping implements ShippingStrategy {
calculate(weight: number): number {
return weight * 3 + 25;
}
}
class InternationalShipping implements ShippingStrategy {
calculate(weight: number): number {
return weight * 5 + 50;
}
}
const shippingStrategies: Record<string, ShippingStrategy> = {
standard: new StandardShipping(),
express: new ExpressShipping(),
overnight: new OvernightShipping(),
international: new InternationalShipping(),
};
function calculateShipping(order: Order): number {
const strategy = shippingStrategies[order.shippingMethod];
if (!strategy) {
throw new Error('Unknown shipping method');
}
return strategy.calculate(order.weight);
}
优点:
- 开闭原则(易于添加新策略)
- 每个策略都可以独立测试
- 消除冗长的 switch 语句
第 5 阶段:改进类型
强类型可以在编译时捕获错误。
第 5.1 步:消除 any 类型
查找所有 any 用法:
precision_grep:
queries:
- id: any_usage
pattern: ":\\s*any(\\s|;|,|\\))"
glob: "**/*.{ts,tsx}"
output:
format: locations
verbosity: standard
之前(不安全):
function processData(data: any) {
return data.value.toUpperCase(); // Runtime error if value is not a string
}
之后(类型安全):
interface DataWithValue {
value: string;
}
function processData(data: DataWithValue): string {
return data.value.toUpperCase();
}
第 5.2 步:使用可辨识联合类型
之前(类型较弱):
interface ApiResponse {
success: boolean;
data?: User;
error?: string;
}
function handleResponse(response: ApiResponse) {
if (response.success) {
console.log(response.data.name); // TypeScript can't guarantee data exists
}
}
之后(可辨识联合类型):
type ApiResponse =
| { success: true; data: User }
| { success: false; error: string };
function handleResponse(response: ApiResponse) {
if (response.success) {
console.log(response.data.name); // TypeScript knows data exists here
} else {
console.error(response.error); // TypeScript knows error exists here
}
}
优点:
- 全面类型检查
- 无法表示不可能的状态
- 更好的 IntelliSense
第 5.3 步:添加泛型约束
之前(泛化过度):
function getProperty<T>(obj: T, key: string) {
return obj[key]; // Type error: no index signature
}
之后(恰当的约束):
function getProperty<T extends Record<string, unknown>, K extends keyof T>(
obj: T,
key: K
): T[K] {
return obj[key];
}
const user = { name: 'Alice', age: 30 };
const name = getProperty(user, 'name'); // Type is string
const age = getProperty(user, 'age'); // Type is number
优点:
- 类型推断可以正确工作
- 在编译时捕获无效的键访问
第 6 阶段:依赖倒置
依赖抽象而非实现,以解耦代码。
第 6.1 步:提取接口
之前(紧耦合):
import { PrismaClient } from '@prisma/client';
class UserService {
private prisma = new PrismaClient();
async getUser(id: string) {
return this.prisma.user.findUnique({ where: { id } });
}
}
之后(依赖注入):
interface UserRepository {
findById(id: string): Promise<User | null>;
create(data: CreateUserInput): Promise<User>;
update(id: string, data: UpdateUserInput): Promise<User>;
}
class PrismaUserRepository implements UserRepository {
constructor(private prisma: PrismaClient) {}
async findById(id: string): Promise<User | null> {
return this.prisma.user.findUnique({ where: { id } });
}
async create(data: CreateUserInput): Promise<User> {
return this.prisma.user.create({ data });
}
async update(id: string, data: UpdateUserInput): Promise<User> {
return this.prisma.user.update({ where: { id }, data });
}
}
class UserService {
constructor(private userRepo: UserRepository) {}
async getUser(id: string) {
return this.userRepo.findById(id);
}
}
// Usage
const prisma = new PrismaClient();
const userRepo = new PrismaUserRepository(prisma);
const userService = new UserService(userRepo);
优点:
- 易于在测试中模拟(注入伪造的仓库)
- 无需更改 UserService,即可将 Prisma 替换为其他 ORM
- 关注点分离清晰
第 6.2 步:使用工厂模式
之前(难以测试):
function sendEmail(to: string, subject: string, body: string) {
const client = new SendGridClient(process.env.SENDGRID_API_KEY!); // BAD: Non-null assertion bypasses runtime validation
client.send({ to, subject, body });
}
之后(工厂注入):
interface EmailClient {
send(email: { to: string; subject: string; body: string }): Promise<void>;
}
class SendGridEmailClient implements EmailClient {
constructor(private apiKey: string) {}
async send(email: { to: string; subject: string; body: string }) {
// SendGrid implementation
}
}
class MockEmailClient implements EmailClient {
async send(email: { to: string; subject: string; body: string }) {
console.log('Mock email sent:', email);
}
}
function createEmailClient(): EmailClient {
if (process.env.NODE_ENV === 'test') {
return new MockEmailClient();
}
const apiKey = process.env.SENDGRID_API_KEY;
if (!apiKey) {
throw new Error('SENDGRID_API_KEY environment variable is required');
}
return new SendGridEmailClient(apiKey);
}
function sendEmail(client: EmailClient, to: string, subject: string, body: string) {
return client.send({ to, subject, body });
}
优点:
- 测试不会发送真实邮件
- 易于切换邮件服务提供商
- 可按环境定制行为
第 7 阶段:数据库重构
数据库架构和查询需要谨慎重构。
第 7.1 步:添加索引以提升性能
检查现有架构:
precision_read:
files:
- path: "prisma/schema.prisma"
extract: content
verbosity: standard
之前(缺少索引):
model Post {
id String @id @default(cuid())
title String
published Boolean @default(false)
authorId String
createdAt DateTime @default(now())
}
之后(添加索引):
model Post {
id String @id @default(cuid())
title String
published Boolean @default(false)
authorId String
createdAt DateTime @default(now())
@@index([authorId])
@@index([published, createdAt])
}
适合添加索引的字段:
- 外键(authorId、userId)
- WHERE 子句中的字段(published、status)
- ORDER BY 中的字段(createdAt、updatedAt)
- 多列查询的复合索引
第 7.2 步:规范化数据库架构
之前(非规范化):
model Order {
id String @id
customerName String
customerEmail String
customerPhone String
}
之后(规范化):
model Customer {
id String @id
name String
email String @unique
phone String
orders Order[]
}
model Order {
id String @id
customerId String
customer Customer @relation(fields: [customerId], references: [id])
@@index([customerId])
}
优点:
- 无数据重复
- 在一个位置更新客户信息
- 引用完整性
第 7.3 步:优化查询
之前(N+1 查询):
const posts = await prisma.post.findMany();
for (const post of posts) {
const author = await prisma.user.findUnique({ where: { id: post.authorId } });
console.log(`${post.title} by ${author.name}`); // Note: Use structured logger in production
}
之后(预加载):
const posts = await prisma.post.findMany({
include: {
author: true,
},
});
for (const post of posts) {
console.log(`${post.title} by ${post.author.name}`); // Note: Use structured logger in production
}
使用 discover 查找 N+1 模式:
discover:
queries:
- id: n_plus_one
type: grep
pattern: "(for|forEach|map).*await.*(prisma|db|query|find)"
glob: "**/*.{ts,tsx,js,jsx}"
verbosity: locations
第 8 阶段:验证和测试
只有经过测试验证,重构才是安全的。
第 8.1 步:重构前运行测试
建立基线:开始之前,测试必须通过。
precision_exec:
commands:
- cmd: "npm run test"
verbosity: standard
如果测试失败,请先修复。
第 8.2 步:逐步重构
每次只做小幅更改,并在每一步之后进行验证。
工作流:
- 运行测试(建立基线)
- 进行一项重构更改
- 再次运行测试
- 如果测试通过,则提交并继续
- 如果测试失败,则回退并排查
使用 precision_exec 进行验证:
precision_exec:
commands:
- cmd: "npm run typecheck"
- cmd: "npm run lint"
- cmd: "npm run test"
verbosity: standard
第 8.3 步:为重构后的代码添加测试
如果覆盖率下降,请添加测试。
precision_exec:
commands:
- cmd: "npm run test -- --coverage"
verbosity: standard
检查覆盖率:
- 函数覆盖率应达到 80% 以上
- 关键路径覆盖率应达到 100%
- 边界情况应有明确的测试
第 9 阶段:更新文档
重构后的代码需要更新文档。
第 9.1 步:更新 JSDoc 注释
之前(已过时):
/**
* Gets user data from the database
*/
function getData(id: string) { ... } // Function was renamed
之后(当前):
/**
* Retrieves a user profile by ID including related posts and followers
* @param userId - The unique identifier for the user
* @returns User profile with posts and followers, or null if not found
*/
function getUserProfile(userId: string): Promise<UserProfile | null> { ... }
第 9.2 步:更新 README 和架构文档
如果文件结构发生变化,请更新文档。
更新示例:
- 更新 README 中的文件树
- 更新导入示例
- 更新架构图
- 更新贡献指南
阶段 10:自动化验证
运行验证脚本,确保重构质量。
./scripts/validate-refactoring.sh /path/to/project
脚本会验证:
- 前后测试均通过
- 代码重复减少
- 类型安全性提高(没有新增
any类型) - 函数长度在限制范围内
- 消除导入循环
- 测试覆盖率维持或提高
常见重构模式
请参阅 references/refactoring-patterns.md,了解详细的重构前后示例。
快速参考:
提取方法
适用场景: 函数过长或执行多项操作
解决方法: 将逻辑块提取到独立函数中
重命名变量/函数
适用场景: 名称不明确或具有误导性
解决方法: 将名称改为更具描述性,并遵循约定
用多态替换条件判断
适用场景: 根据类型执行的 switch 语句
解决方法: 使用策略模式或类层次结构
引入参数对象
适用场景: 函数参数过多
解决方法: 将相关参数组合成一个对象
替换魔法数字
适用场景: 使用了缺乏上下文的硬编码数字
解决方法: 将数字提取为具名常量
合并重复的条件代码片段
适用场景: 所有分支中都有相同的代码
解决方法: 将公共代码移到条件判断之外
用于重构的精准工具
Discover 工具
在整个代码库中查找适合重构的代码。
示例:查找重复代码
discover:
queries:
- id: validation_patterns
type: grep
pattern: "if.*!.*email.*includes"
glob: "**/*.{ts,tsx}"
- id: large_functions
type: symbols
query: "function"
verbosity: locations
Precision Edit
执行安全、原子的重构编辑。
示例:提取函数
precision_edit:
operations:
- action: replace
path: "src/api/user.ts"
old_text: |
if (!email || !email.includes('@')) {
return { error: 'Invalid email' };
}
new_text: |
const emailValidation = validateEmail(email);
if (!emailValidation.valid) {
return { error: emailValidation.error };
}
verbosity: minimal
Precision Exec
验证重构不会造成任何问题。
precision_exec:
commands:
- cmd: "npm run typecheck"
- cmd: "npm run lint -- --fix"
- cmd: "npm run test"
verbosity: standard
验证脚本
使用 scripts/validate-refactoring.sh 验证重构质量。
./scripts/validate-refactoring.sh /path/to/project
脚本检查:
- 更改前后测试均通过
- 代码重复度指标
- 类型安全性(没有新增
any类型) - 函数长度限制
- 导入循环
- 测试覆盖率维持不变
快速参考
重构检查清单
重构前:
- 测试已编写且通过
- 了解当前代码结构
- 确定重构目标(提取、重命名、重新组织)
- 列出所有使用位置
重构期间:
- 进行小幅、渐进式更改
- 每次更改后运行测试
- 让每次提交保持聚焦且原子化
- 更新导入和引用
重构后:
- 所有测试均通过
- 类型检查通过
- Lint 检查通过
- 测试覆盖率维持或提高
- 更新文档
- 运行验证脚本
何时进行重构
适合重构的时机:
- 添加新功能前(先清理代码)
- 修复 Bug 时(修复的同时改进结构)
- 代码审查期间(提出改进建议)
- 发现代码异味时(重复、复杂)
不适合重构的时机:
- 调试紧急问题时(先修复,之后再重构)
- 没有测试时(不安全,可能破坏行为)
- 临近截止日期时(可能引入 Bug)
- 需求快速变化时
重构安全规则
- 测试是必需的 — 没有测试时绝不重构
- 一次只做一项更改 — 不要将重构与功能开发混在一起
- 频繁验证 — 每次更改后运行测试
- 原子化提交 — 每次提交都应处于完整且可正常工作的状态
- 保持行为不变 — 重构不应改变功能
高级技巧
使用精准工具自动重构
使用 discover 查找模式,然后使用 precision_edit 修复。
# Step 1: Find all console.log statements
discover:
queries:
- id: console_logs
type: grep
pattern: "console\\.log\\("
glob: "src/**/*.{ts,tsx}"
verbosity: locations
# Step 2: Replace with proper logger
precision_edit:
operations:
- action: replace
path: "src/file1.ts"
old_text: "console.log("
new_text: "logger.info("
replace_all: true
verbosity: minimal
并行重构
使用批量操作同时重构多个文件。
precision_edit:
operations:
- action: replace
path: "src/api/user.ts"
old_text: "getData"
new_text: "getUserProfile"
replace_all: true
- action: replace
path: "src/api/post.ts"
old_text: "getData"
new_text: "getPostDetails"
replace_all: true
verbosity: minimal
重构指标
追踪改进情况。
重构前测量:
precision_exec:
commands:
- cmd: "find src -not -path '*/node_modules/*' -not -path '*/dist/*' -name '*.ts' -exec wc -l {} + | tail -1"
- cmd: "grep -r --include='*.ts' --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git --exclude-dir=.next -- 'any' src | wc -l"
verbosity: standard
重构后测量并比较:
- 代码行数(移除重复代码后应减少)
any的使用次数(应减少)- 文件数量(拆分大型文件后可能增加)
- 测试覆盖率(应维持或提高)
与其他技能集成
- 使用 code-review 识别重构需求
- 使用 type-safety 技能了解类型改进模式
- 重构错误处理逻辑时使用 error-handling 技能
- 使用 testing-strategy 确保测试覆盖充分
- 重构 UI 组件时使用 component-architecture
- 重构数据库查询时使用 database-layer
资源
references/refactoring-patterns.md- 常见重构模式及示例scripts/validate-refactoring.sh- 自动化重构验证- Martin Fowler 的重构目录 - https://refactoring.com/catalog/
- Robert C. Martin 的《代码整洁之道》
- Martin Fowler 的《重构:改善既有代码的设计》