Load PROACTIVELY when task involves improving code structure without changing behavior. Use when user says "refactor this", "clean up this code", "remove dead code", "reduce duplication", "reorganize the files", or "extract this into a function". Covers extract function/component, rename symbol, simplify conditionals, improve type safety, dependency inversion, dead code removal, circular dependency resolution, and database query refactoring with automated safety validation (typecheck, tests) at each step.
Refactors code to improve structure and maintainability while preserving behavior through automated safety validation.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/refactoring-patterns.mdscripts/validate-refactoring.shscripts/
validate-refactoring.sh
references/
refactoring-patterns.md
This skill teaches you how to perform safe, systematic code refactoring using GoodVibes precision tools. Refactoring improves code structure and maintainability without changing external behavior, making future development faster and reducing bugs.
Load this skill when:
any usage)Trigger phrases: "refactor this code", "reduce duplication", "extract function", "simplify conditionals", "improve types", "reorganize", "clean up code".
Before refactoring, map the current code structure and identify refactoring opportunities.
Use discover to find common code smells that need refactoring.
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
What this reveals:
Use precision_grep to map how code is used across the codebase.
precision_grep:
queries:
- id: function_usage
pattern: "importFunctionName\\("
glob: "**/*.{ts,tsx,js,jsx}"
output:
format: locations
verbosity: standard
Why this matters:
Refactoring is only safe when tests validate behavior preservation.
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
Critical rule:
Large functions and components are hard to test and maintain. Extract reusable pieces.
Look for repeated logic or code blocks that do one thing.
precision_read:
files:
- path: "src/components/UserProfile.tsx"
extract: symbols
verbosity: standard
Extraction candidates:
Before (duplicated validation logic):
// 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
}
After (extracted validation):
// 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
}
Use precision_edit to perform the extraction:
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
Before (large component with multiple responsibilities):
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>
);
}
After (extracted components and hooks):
// 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>
);
}
Benefits:
Poor naming and file organization slow development.
Rename variables, functions, and files to be descriptive.
Bad naming:
function getData(id: string) { ... } // Too generic
const x = getUserById(userId); // Unclear abbreviation
let flag = true; // What does this flag represent?
Good naming:
function getUserProfile(userId: string) { ... } // Specific, clear intent
const userProfile = getUserById(userId); // Descriptive
let isEmailVerified = true; // Boolean naming convention
Use precision_edit with replace_all for renaming:
precision_edit:
operations:
- action: replace
path: "src/lib/user.ts"
old_text: "function getData"
new_text: "function getUserProfile"
replace_all: true
verbosity: minimal
Group related files together using feature-based or layer-based structure.
Before (flat structure):
src/
user-routes.ts
user-service.ts
user-repository.ts
post-routes.ts
post-service.ts
post-repository.ts
After (feature-based structure):
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
Benefits:
Use precision_write to create barrel exports:
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
Nested conditionals and complex boolean logic are error-prone.
Before (nested conditionals):
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');
}
}
After (guard clauses):
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);
}
Benefits:
Before (complex boolean logic):
if (user.role === 'admin' || (user.role === 'moderator' && user.permissions.includes('delete')) || user.id === post.authorId) {
deletePost(post.id);
}
After (named function):
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);
}
Benefits:
Before (long switch statement):
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');
}
}
After (strategy pattern):
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);
}
Benefits:
Strong typing catches bugs at compile time.
any TypesFind all any usage:
precision_grep:
queries:
- id: any_usage
pattern: ":\\s*any(\\s|;|,|\\))"
glob: "**/*.{ts,tsx}"
output:
format: locations
verbosity: standard
Before (unsafe):
function processData(data: any) {
return data.value.toUpperCase(); // Runtime error if value is not a string
}
After (type-safe):
interface DataWithValue {
value: string;
}
function processData(data: DataWithValue): string {
return data.value.toUpperCase();
}
Before (weak typing):
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
}
}
After (discriminated union):
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
}
}
Benefits:
Before (too generic):
function getProperty<T>(obj: T, key: string) {
return obj[key]; // Type error: no index signature
}
After (proper constraints):
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
Benefits:
Decouple code by depending on abstractions, not implementations.
Before (tight coupling):
import { PrismaClient } from '@prisma/client';
class UserService {
private prisma = new PrismaClient();
async getUser(id: string) {
return this.prisma.user.findUnique({ where: { id } });
}
}
After (dependency injection):
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);
Benefits:
Before (hard to test):
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 });
}
After (factory injection):
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 });
}
Benefits:
Database schemas and queries need careful refactoring.
Check existing schema:
precision_read:
files:
- path: "prisma/schema.prisma"
extract: content
verbosity: standard
Before (missing indexes):
model Post {
id String @id @default(cuid())
title String
published Boolean @default(false)
authorId String
createdAt DateTime @default(now())
}
After (with indexes):
model Post {
id String @id @default(cuid())
title String
published Boolean @default(false)
authorId String
createdAt DateTime @default(now())
@@index([authorId])
@@index([published, createdAt])
}
When to add indexes:
Before (denormalized):
model Order {
id String @id
customerName String
customerEmail String
customerPhone String
}
After (normalized):
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])
}
Benefits:
Before (N+1 query):
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
}
After (eager loading):
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
}
Use discover to find N+1 patterns:
discover:
queries:
- id: n_plus_one
type: grep
pattern: "(for|forEach|map).*await.*(prisma|db|query|find)"
glob: "**/*.{ts,tsx,js,jsx}"
verbosity: locations
Refactoring is only safe when validated by tests.
Establish baseline: tests must pass before you start.
precision_exec:
commands:
- cmd: "npm run test"
verbosity: standard
If tests fail, fix them first.
Make small changes and validate after each step.
Workflow:
Use precision_exec to validate:
precision_exec:
commands:
- cmd: "npm run typecheck"
- cmd: "npm run lint"
- cmd: "npm run test"
verbosity: standard
If coverage drops, add tests.
precision_exec:
commands:
- cmd: "npm run test -- --coverage"
verbosity: standard
Check coverage:
Refactored code needs updated documentation.
Before (outdated):
/**
* Gets user data from the database
*/
function getData(id: string) { ... } // Function was renamed
After (current):
/**
* 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> { ... }
If file structure changed, update documentation.
Example updates:
Run the validation script to ensure refactoring quality.
./scripts/validate-refactoring.sh /path/to/project
The script validates:
any types)See references/refactoring-patterns.md for detailed before/after examples.
Quick reference:
When: Function is too long or does multiple things
Fix: Extract logical blocks into separate functions
When: Name is unclear or misleading
Fix: Rename to be descriptive and follow conventions
When: Switch statement based on type
Fix: Use strategy pattern or class hierarchy
When: Function has too many parameters
Fix: Group related parameters into an object
When: Hardcoded numbers without context
Fix: Extract to named constants
When: Same code in all branches
Fix: Move common code outside conditional
Find refactoring candidates across the codebase.
Example: Find duplication
discover:
queries:
- id: validation_patterns
type: grep
pattern: "if.*!.*email.*includes"
glob: "**/*.{ts,tsx}"
- id: large_functions
type: symbols
query: "function"
verbosity: locations
Perform safe, atomic refactoring edits.
Example: Extract function
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
Validate refactoring doesn't break anything.
precision_exec:
commands:
- cmd: "npm run typecheck"
- cmd: "npm run lint -- --fix"
- cmd: "npm run test"
verbosity: standard
Use scripts/validate-refactoring.sh to validate refactoring quality.
./scripts/validate-refactoring.sh /path/to/project
The script checks:
any types)Before refactoring:
During refactoring:
After refactoring:
Good times to refactor:
Bad times to refactor:
Use discover to find patterns, then precision_edit to fix them.
# 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
Refactor multiple files simultaneously using batch operations.
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
Track improvement over time.
Measure before refactoring:
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
Measure after refactoring and compare:
any usage count (should decrease)references/refactoring-patterns.md - Common refactoring patterns with examplesscripts/validate-refactoring.sh - Automated refactoring validationActivates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.