code-refactor
Code Refactor
Overview
This skill guides systematic refactoring of one or more codebase components through three phases:
- Research - Deep analysis of component behavior, usage patterns, and dependencies
- Proposal - Concrete change suggestions with code samples and impact analysis
- Test Plan - Test strategy to validate changes without regressions
Each phase produces a markdown document, creating a complete refactoring proposal that can be reviewed before implementation.
Output Structure
Ask the user for an output directory (e.g., ./docs/refactoring/ or ./proposals/).
Create a subfolder for the refactoring effort:
{output-directory}/
└── {component-name}-refactor/
├── 01-research.md # Phase 1: Component Analysis
├── 02-proposal.md # Phase 2: Change Proposals
└── 03-test-plan.md # Phase 3: Test Strategy
For multi-component refactoring, create subfolders:
{output-directory}/
└── {thread-name}-refactor/
├── component-a/
│ ├── 01-research.md
│ ├── 02-proposal.md
│ └── 03-test-plan.md
└── component-b/
├── 01-research.md
├── 02-proposal.md
└── 03-test-plan.md
Phase 1: Research
Goal: Comprehensive understanding of the component before proposing changes.
For multi-component refactoring, use parallel Explore agents to research each component simultaneously, then synthesize findings.
Step 1: Component Inventory (Use /codebase-librarian)
Start with the /codebase-librarian skill to establish baseline understanding:
- Project foundation (language, tooling)
- Entry points relevant to the component
- Services the component interacts with
- Infrastructure dependencies
- Domain model elements involved
This provides context for the deeper component analysis that follows.
Step 2: Component Deep Dive
After the inventory, analyze the specific component(s) being refactored.
Understand how it works:
- Core responsibilities and behavior
- Internal structure and control flow
- State management and data transformations
- Error handling patterns
- Configuration and initialization
Understand instantiation and usage:
- Where is it instantiated?
- How many instantiation patterns exist?
- What parameters/dependencies are injected?
- Is it a singleton, factory-created, or ad-hoc?
Map the surroundings:
- Dependents (what calls this component)
- Dependencies (what this component calls)
- Before: What happens before invocation?
- After: What happens after invocation?
Step 3: Call Graph
Create a visual representation of the component's call relationships:
┌─────────────────────────────────────────────────────────────┐
│ CALLERS │
├─────────────────────────────────────────────────────────────┤
│ OrderController.create() ──┐ │
│ OrderController.update() ──┼──► PaymentService │
│ CheckoutWorker.process() ──┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PaymentService │
├─────────────────────────────────────────────────────────────┤
│ processPayment() │
│ refundPayment() │
│ validateCard() │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DEPENDENCIES │
├─────────────────────────────────────────────────────────────┤
│ StripeClient │ PaymentRepository │
│ FraudDetectionService │ EventEmitter │
└─────────────────────────────────────────────────────────────┘
Research Output Format
Write to 01-research.md:
# Research: [Component Name]
**Generated**: [Date]
**Scope**: [Component path/module]
## Executive Summary
[2-3 paragraphs covering:]
- Component's purpose and role in the system
- Key findings about current implementation
- Areas of concern or complexity discovered
## Component Analysis
### Core Behavior
[What the component does, its responsibilities]
### Internal Structure
[Classes, functions, modules that compose it]
### State & Data Flow
[How data moves through the component]
## Instantiation Patterns
| Location | Pattern | Parameters | Frequency |
|----------|---------|------------|-----------|
| `src/api/orders.ts:45` | Direct new | config, logger | 3 places |
| `src/workers/checkout.ts:12` | Factory | config | 1 place |
## Dependency Analysis
### Dependents (Who calls this)
| Caller | Method | Context |
|--------|--------|---------|
| `OrderController` | `processPayment()` | HTTP request handling |
| `CheckoutWorker` | `processPayment()` | Background job |
### Dependencies (What this calls)
| Dependency | Usage | Abstracted? |
|------------|-------|-------------|
| `StripeClient` | Payment processing | No - direct SDK |
| `PaymentRepository` | Persistence | Yes - interface |
### Execution Context
**Before invocation**: [What typically happens before this component is called]
**After invocation**: [What happens with the result]
## Call Graph
[ASCII diagram as shown above]
## Key Observations
- [Notable pattern or concern 1]
- [Notable pattern or concern 2]
- [Areas that may need attention during refactoring]
Phase 2: Proposal
Persona: Pragmatic Senior Engineer
Mindset: Balance ideal architecture with practical constraints. Quick wins build momentum and trust. Large changes need clear justification. Every change should have a clear "why".
Proposal Structure
Order changes from quick wins to complex restructuring:
- Quick Wins - Small, low-risk improvements (hours)
- Medium Changes - Meaningful refactoring (days)
- Large Restructuring - Significant architectural changes (weeks)
For Each Change
Provide:
- Problem Statement - What's wrong and why it matters
- Proposed Solution - Concrete approach
- Code Samples - Before and after comparisons
- Impact Analysis - Files affected, risks, dependencies
- Pros and Cons - Honest trade-off assessment
Proposal Output Format
Write to 02-proposal.md:
# Refactoring Proposal: [Component Name]
**Generated**: [Date]
**Based on**: 01-research.md
## Executive Summary
[Overview of proposed changes, expected benefits, and effort distribution]
---
## Quick Wins
### 1. [Change Title]
**Problem**
[What's wrong - reference research findings]
**Solution**
[What to do]
**Before**
```typescript
// src/services/payment.ts:45-52
class PaymentService {
constructor() {
this.stripe = new Stripe(process.env.STRIPE_KEY);
}
}
After
// src/services/payment.ts:45-55
class PaymentService {
constructor(private readonly paymentGateway: PaymentGateway) {}
}
// src/ports/payment-gateway.ts (new)
interface PaymentGateway {
charge(amount: number, currency: string): Promise<ChargeResult>;
}
Impact
- Files modified:
src/services/payment.ts,src/composition-root.ts - Files created:
src/ports/payment-gateway.ts - Risk: Low - constructor signature change requires updating instantiation sites
Pros
- Enables unit testing without Stripe sandbox
- Prepares for potential payment provider switch
Cons
- Adds indirection layer
- Requires updating 3 instantiation sites
Medium Changes
2. [Change Title]
[Same structure as above]
Large Restructuring
3. [Change Title]
[Same structure - may be higher level for larger efforts]
Implementation Order
Recommended sequence considering dependencies:
- [Quick Win 1] - No dependencies
- [Quick Win 2] - No dependencies
- [Medium Change 1] - Depends on Quick Win 1
- [Large Restructuring] - Depends on Medium Change 1
Risk Assessment
| Change | Risk Level | Mitigation |
|---|---|---|
| [Change 1] | Low | Existing tests cover behavior |
| [Change 2] | Medium | Add integration tests first |
| [Change 3] | High | Feature flag, gradual rollout |
---
## Phase 3: Test Plan
**Goal**: Design tests that validate refactoring without regressions.
### Analysis Steps
1. **Audit existing tests** - What's already covered?
2. **Identify gaps** - What behavior lacks test coverage?
3. **Design new tests** - Focus on integration over unit tests
4. **Minimize mocks** - Prefer real dependencies when feasible
### Test Philosophy
- **Favor integration tests** - Test real interactions between components
- **Use mocks sparingly** - Only for external services, not internal dependencies
- **Test behavior, not implementation** - Tests should survive refactoring
- **Cover edge cases discovered in research** - Use Phase 1 findings
### Test Plan Output Format
Write to `03-test-plan.md`:
```markdown
# Test Plan: [Component Name] Refactoring
**Generated**: [Date]
**Based on**: 01-research.md, 02-proposal.md
## Executive Summary
[Overview of testing strategy and coverage goals]
## Current Test Coverage
### Existing Tests
| Test File | Type | Coverage | Notes |
|-----------|------|----------|-------|
| `payment.test.ts` | Unit | Core payment flow | Heavy mocking |
| `checkout.integration.ts` | Integration | Happy path only | No error cases |
### Coverage Gaps
- [ ] Error handling in `processPayment()`
- [ ] Concurrent payment attempts
- [ ] Retry behavior after transient failures
## Test Strategy
### Integration Tests (Preferred)
Tests that exercise real component interactions:
**Test: Payment processing end-to-end**
```typescript
// tests/integration/payment.test.ts
describe('PaymentService', () => {
let service: PaymentService;
let testDb: TestDatabase;
beforeEach(async () => {
testDb = await TestDatabase.create();
service = new PaymentService(
new StripeTestGateway(), // Test mode, real API
new PaymentRepository(testDb)
);
});
it('processes payment and persists record', async () => {
const result = await service.processPayment({
amount: 1000,
currency: 'usd',
customerId: 'cust_123'
});
expect(result.status).toBe('succeeded');
const record = await testDb.payments.findById(result.id);
expect(record).toBeDefined();
expect(record.amount).toBe(1000);
});
});
Why integration over unit: Tests real database interactions, actual service coordination, and catches integration bugs that mocks would hide.
Unit Tests (When Necessary)
Use unit tests with mocks only when:
- External API calls would be slow/costly
- Testing pure business logic in isolation
- Simulating error conditions difficult to reproduce
Test: Fraud detection logic
// tests/unit/fraud-detection.test.ts
describe('FraudDetector', () => {
it('flags high-risk transactions', () => {
const detector = new FraudDetector();
const result = detector.assess({
amount: 10000,
newCustomer: true,
internationalCard: true
});
expect(result.riskLevel).toBe('high');
expect(result.requiresReview).toBe(true);
});
});
Why unit here: Pure logic, no external dependencies, fast execution.
Tests Per Proposal Change
For Change 1: [Extract PaymentGateway Interface]
| Test | Type | Purpose |
|---|---|---|
gateway-contract.test.ts |
Contract | Verify adapter implements interface correctly |
payment-with-mock-gateway.test.ts |
Unit | Verify service works with any gateway |
New test:
// tests/contract/payment-gateway.test.ts
describe('PaymentGateway contract', () => {
const adapters = [
['Stripe', () => new StripePaymentAdapter(stripeTestClient)],
['Mock', () => new MockPaymentAdapter()],
];
test.each(adapters)('%s adapter implements contract', (name, createAdapter) => {
const adapter = createAdapter();
expect(adapter.charge).toBeDefined();
expect(adapter.refund).toBeDefined();
});
});
For Change 2: [Change Title]
[Similar structure]
Mock Usage Guidelines
Acceptable mocks:
- External payment APIs (Stripe, PayPal)
- Email services
- SMS/notification services
- Third-party APIs with rate limits
Avoid mocking:
- Internal services (use real implementations)
- Repositories (use test database)
- Domain logic (test directly)
Verification Checklist
Before considering refactoring complete:
- All existing tests pass
- New integration tests cover changed behavior
- Edge cases from research are tested
- Error handling paths have coverage
- No increase in mock usage
- CI pipeline green
---
## References
For detailed guidance on each phase:
- **Phase 1 deep dive**: See [references/research-phase.md](references/research-phase.md) for advanced research techniques
- **Phase 2 patterns**: See [references/proposal-phase.md](references/proposal-phase.md) for common refactoring patterns
- **Phase 3 testing**: See [references/test-plan-phase.md](references/test-plan-phase.md) for test design patterns
More from baggiponte/skills
codebase-librarian
Create a comprehensive inventory of a codebase. Map structure, entry points, services, infrastructure, domain models, and data flows. Pure documentation—no opinions or recommendations. Use when onboarding to an unfamiliar codebase, documenting existing architecture before changes, preparing for architecture reviews or migration planning, or creating a reference for the team. Triggers on requests like "map this codebase", "document the architecture", "create an inventory", or "what does this codebase contain".
20architecture-design-critique
Perform a codebase-wide architectural review through a Ports & Adapters (hexagonal architecture) lens. Assess boundary violations, coupling issues, and dependency direction. Produces a prioritized improvement roadmap. Use when reviewing architecture for testability/portability, assessing technical debt, planning refactoring efforts, or evaluating codebase health. Triggers on requests like "review the architecture", "assess coupling", "hexagonal analysis", "check boundary violations", or "architecture critique".
9build-python-dockerfiles
Build production-ready Dockerfiles for Python projects that use uv. Use when creating or refactoring Dockerfiles for reproducible installs, cache-efficient builds, bytecode compilation, small runtime images, and non-root execution. Follows the production patterns from Hynek Schlawack's article "Production-ready Python Docker Containers with uv" while staying flexible about base images and app type. Supports packaged and unpackaged applications, including web apps, workers, and CLI services. Triggers on requests like "write a Dockerfile for this Python project", "optimize this uv Dockerfile", "containerize this FastAPI/Django/Flask app", "containerize this worker", or "split this into build and runtime stages".
3oracle
Submit an implementation plan to GPT 5.2-xhigh for review by a senior engineer persona. The Oracle provides direct, honest critique focused on simplicity and pragmatic excellence. Use when you have a detailed implementation plan and want a second opinion before coding. Triggers on "review my plan", "get Oracle feedback", "consult the Oracle", "plan review", or when you want senior engineer critique on an implementation approach.
2context7
Retrieve up-to-date documentation for software libraries, frameworks, and components via the Context7 CLI using `bunx ctx7`. Use this whenever you need current docs, API references, code examples, migration details, or verification for a library or framework instead of relying on training data. Triggers on requests like "look up the docs for X", "find the latest API for Y", "show me examples from the docs", "check the current React/Next.js/FastAPI docs", or "verify this library usage against current documentation".
2spark-context-curator
Ultra-fast, read-only codebase exploration and context curation for GPT-5.3 Codex Spark. Use when you need deep repository understanding without modifying anything: architecture mapping, flow tracing, ownership discovery, incident/code-review prep, or implementation planning. Triggers on requests like \"explore this codebase\", \"curate context\", \"map where X happens\", \"investigate before editing\", or \"read-only deep dive\".
2