Initial commit
This commit is contained in:
541
skills/refactoring-safely/SKILL.md
Normal file
541
skills/refactoring-safely/SKILL.md
Normal file
@@ -0,0 +1,541 @@
|
||||
---
|
||||
name: refactoring-safely
|
||||
description: Use when refactoring code - test-preserving transformations in small steps, running tests between each change
|
||||
---
|
||||
|
||||
<skill_overview>
|
||||
Refactoring changes code structure without changing behavior; tests must stay green throughout or you're rewriting, not refactoring.
|
||||
</skill_overview>
|
||||
|
||||
<rigidity_level>
|
||||
MEDIUM FREEDOM - Follow the change→test→commit cycle strictly, but adapt the specific refactoring patterns to your language and codebase.
|
||||
</rigidity_level>
|
||||
|
||||
<quick_reference>
|
||||
| Step | Action | Verify |
|
||||
|------|--------|--------|
|
||||
| 1 | Run full test suite | ALL pass |
|
||||
| 2 | Create bd refactoring task | Track work |
|
||||
| 3 | Make ONE small change | Compiles |
|
||||
| 4 | Run tests immediately | ALL still pass |
|
||||
| 5 | Commit with descriptive message | History clear |
|
||||
| 6 | Repeat 3-5 until complete | Each step safe |
|
||||
| 7 | Final verification & close bd | Done |
|
||||
|
||||
**Core cycle:** Change → Test → Commit (repeat until complete)
|
||||
</quick_reference>
|
||||
|
||||
<when_to_use>
|
||||
- Improving code structure without changing functionality
|
||||
- Extracting duplicated code into shared utilities
|
||||
- Renaming for clarity
|
||||
- Reorganizing file/module structure
|
||||
- Simplifying complex code while preserving behavior
|
||||
|
||||
**Don't use for:**
|
||||
- Changing functionality (use feature development)
|
||||
- Fixing bugs (use hyperpowers:fixing-bugs)
|
||||
- Adding features while restructuring (do separately)
|
||||
- Code without tests (write tests first using hyperpowers:test-driven-development)
|
||||
</when_to_use>
|
||||
|
||||
<the_process>
|
||||
## 1. Verify Tests Pass
|
||||
|
||||
**BEFORE any refactoring:**
|
||||
|
||||
```bash
|
||||
# Use test-runner agent to keep context clean
|
||||
Dispatch hyperpowers:test-runner agent: "Run: cargo test"
|
||||
```
|
||||
|
||||
**Verify:** ALL tests pass. If any fail, fix them FIRST, then refactor.
|
||||
|
||||
**Why:** Failing tests mean you can't detect if refactoring breaks things.
|
||||
|
||||
---
|
||||
|
||||
## 2. Create bd Task for Refactoring
|
||||
|
||||
Track the refactoring work:
|
||||
|
||||
```bash
|
||||
bd create "Refactor: Extract user validation logic" \
|
||||
--type task \
|
||||
--priority P2
|
||||
|
||||
bd edit bd-456 --design "
|
||||
## Goal
|
||||
Extract user validation logic from UserService into separate Validator class.
|
||||
|
||||
## Why
|
||||
- Validation duplicated across 3 services
|
||||
- Makes testing individual validations difficult
|
||||
- Violates single responsibility
|
||||
|
||||
## Approach
|
||||
1. Create UserValidator class
|
||||
2. Extract email validation
|
||||
3. Extract name validation
|
||||
4. Extract age validation
|
||||
5. Update UserService to use validator
|
||||
6. Remove duplication from other services
|
||||
|
||||
## Success Criteria
|
||||
- All existing tests still pass
|
||||
- No behavior changes
|
||||
- Validator has 100% test coverage
|
||||
"
|
||||
|
||||
bd update bd-456 --status in_progress
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Make ONE Small Change
|
||||
|
||||
The smallest transformation that compiles.
|
||||
|
||||
**Examples of "small":**
|
||||
- Extract one method
|
||||
- Rename one variable
|
||||
- Move one function to different file
|
||||
- Inline one constant
|
||||
- Extract one interface
|
||||
|
||||
**NOT small:**
|
||||
- Extracting multiple methods at once
|
||||
- Renaming + moving + restructuring
|
||||
- "While I'm here" improvements
|
||||
|
||||
**Example:**
|
||||
|
||||
```rust
|
||||
// Before
|
||||
fn create_user(name: &str, email: &str) -> Result<User> {
|
||||
if email.is_empty() {
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
if !email.contains('@') {
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
|
||||
let user = User { name, email };
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
// After - ONE small change (extract email validation)
|
||||
fn create_user(name: &str, email: &str) -> Result<User> {
|
||||
validate_email(email)?;
|
||||
|
||||
let user = User { name, email };
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
fn validate_email(email: &str) -> Result<()> {
|
||||
if email.is_empty() {
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
if !email.contains('@') {
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Run Tests Immediately
|
||||
|
||||
After EVERY small change:
|
||||
|
||||
```bash
|
||||
Dispatch hyperpowers:test-runner agent: "Run: cargo test"
|
||||
```
|
||||
|
||||
**Verify:** ALL tests still pass.
|
||||
|
||||
**If tests fail:**
|
||||
1. STOP
|
||||
2. Undo the change: `git restore src/file.rs`
|
||||
3. Understand why it broke
|
||||
4. Make smaller change
|
||||
5. Try again
|
||||
|
||||
**Never proceed with failing tests.**
|
||||
|
||||
---
|
||||
|
||||
## 5. Commit the Small Change
|
||||
|
||||
Commit each safe transformation:
|
||||
|
||||
```bash
|
||||
Dispatch hyperpowers:test-runner agent: "Run: git add src/user_service.rs && git commit -m 'refactor(bd-456): extract email validation to function
|
||||
|
||||
No behavior change. All tests pass.
|
||||
|
||||
Part of bd-456'"
|
||||
```
|
||||
|
||||
**Why commit so often:**
|
||||
- Easy to undo if next step breaks
|
||||
- Clear history of transformations
|
||||
- Can review each step independently
|
||||
- Proves tests passed at each point
|
||||
|
||||
---
|
||||
|
||||
## 6. Repeat Until Complete
|
||||
|
||||
Repeat steps 3-5 for each small transformation:
|
||||
|
||||
```
|
||||
1. Extract validate_email() ✓ (committed)
|
||||
2. Extract validate_name() ✓ (committed)
|
||||
3. Extract validate_age() ✓ (committed)
|
||||
4. Create UserValidator struct ✓ (committed)
|
||||
5. Move validations into UserValidator ✓ (committed)
|
||||
6. Update UserService to use validator ✓ (committed)
|
||||
7. Remove validation from OrderService ✓ (committed)
|
||||
8. Remove validation from AccountService ✓ (committed)
|
||||
```
|
||||
|
||||
**Pattern:** change → test → commit (repeat)
|
||||
|
||||
---
|
||||
|
||||
## 7. Final Verification
|
||||
|
||||
After all transformations complete:
|
||||
|
||||
```bash
|
||||
# Full test suite
|
||||
Dispatch hyperpowers:test-runner agent: "Run: cargo test"
|
||||
|
||||
# Linter
|
||||
Dispatch hyperpowers:test-runner agent: "Run: cargo clippy"
|
||||
```
|
||||
|
||||
**Review the changes:**
|
||||
|
||||
```bash
|
||||
# See all refactoring commits
|
||||
git log --oneline | grep "bd-456"
|
||||
|
||||
# Review full diff
|
||||
git diff main...HEAD
|
||||
```
|
||||
|
||||
**Checklist:**
|
||||
- [ ] All tests pass
|
||||
- [ ] No new warnings
|
||||
- [ ] No behavior changes
|
||||
- [ ] Code is cleaner/simpler
|
||||
- [ ] Each commit is small and safe
|
||||
|
||||
**Close bd task:**
|
||||
|
||||
```bash
|
||||
bd edit bd-456 --design "
|
||||
... (append to existing design)
|
||||
|
||||
## Completed
|
||||
- Created UserValidator class with email, name, age validation
|
||||
- Removed duplicated validation from 3 services
|
||||
- All tests pass (verified)
|
||||
- No behavior changes
|
||||
- 8 small transformations, each tested
|
||||
"
|
||||
|
||||
bd close bd-456
|
||||
```
|
||||
</the_process>
|
||||
|
||||
<examples>
|
||||
<example>
|
||||
<scenario>Developer changes behavior while "refactoring"</scenario>
|
||||
|
||||
<code>
|
||||
// Original code
|
||||
fn validate_email(email: &str) -> Result<()> {
|
||||
if email.is_empty() {
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
if !email.contains('@') {
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// "Refactored" version
|
||||
fn validate_email(email: &str) -> Result<()> {
|
||||
if email.is_empty() {
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
if !email.contains('@') {
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
// NEW: Added extra validation
|
||||
if !email.contains('.') { // BEHAVIOR CHANGE
|
||||
return Err(Error::InvalidEmail);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
</code>
|
||||
|
||||
<why_it_fails>
|
||||
- This changes behavior (now rejects emails like "user@localhost")
|
||||
- Tests might fail, or worse, pass and ship breaking change
|
||||
- Not refactoring - this is modifying functionality
|
||||
- Users who relied on old behavior experience regression
|
||||
</why_it_fails>
|
||||
|
||||
<correction>
|
||||
**Correct approach:**
|
||||
|
||||
1. Extract validation (pure refactoring, no behavior change)
|
||||
2. Commit with tests passing
|
||||
3. THEN add new validation as separate feature with new tests
|
||||
4. Two clear commits: refactoring vs. feature addition
|
||||
|
||||
**What you gain:**
|
||||
- Clear history of what changed when
|
||||
- Easy to revert feature without losing refactoring
|
||||
- Tests document exact behavior changes
|
||||
- No surprises in production
|
||||
</correction>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<scenario>Developer does big-bang refactoring</scenario>
|
||||
|
||||
<code>
|
||||
# Changes made all at once:
|
||||
- Renamed 15 functions across 5 files
|
||||
- Extracted 3 new classes
|
||||
- Moved code between 10 files
|
||||
- Reorganized module structure
|
||||
- Updated all import statements
|
||||
|
||||
# Then runs tests
|
||||
$ cargo test
|
||||
... 23 test failures ...
|
||||
|
||||
# Now what? Which change broke what?
|
||||
</code>
|
||||
|
||||
<why_it_fails>
|
||||
- Can't identify which specific change broke tests
|
||||
- Reverting means losing ALL work
|
||||
- Fixing requires re-debugging entire refactoring
|
||||
- Wastes hours trying to untangle failures
|
||||
- Might give up and revert everything
|
||||
</why_it_fails>
|
||||
|
||||
<correction>
|
||||
**Correct approach:**
|
||||
|
||||
1. Rename ONE function → test → commit
|
||||
2. Extract ONE class → test → commit
|
||||
3. Move ONE file → test → commit
|
||||
4. Continue one change at a time
|
||||
|
||||
**If test fails:**
|
||||
- Know exactly which change broke it
|
||||
- Revert ONE commit, not all work
|
||||
- Fix or make smaller change
|
||||
- Continue from known-good state
|
||||
|
||||
**What you gain:**
|
||||
- Tests break → immediately know why
|
||||
- Each commit is reviewable independently
|
||||
- Can stop halfway with useful progress
|
||||
- Confidence from continuous green tests
|
||||
- Clear history for future developers
|
||||
</correction>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<scenario>Developer refactors code without tests</scenario>
|
||||
|
||||
<code>
|
||||
// Legacy code with no tests
|
||||
fn process_payment(amount: f64, user_id: i64) -> Result<PaymentId> {
|
||||
// 200 lines of complex payment logic
|
||||
// Multiple edge cases
|
||||
// No tests exist
|
||||
}
|
||||
|
||||
// Developer refactors without tests:
|
||||
// - Extracts 5 methods
|
||||
// - Renames variables
|
||||
// - Simplifies conditionals
|
||||
// - "Looks good to me!"
|
||||
|
||||
// Deploys to production
|
||||
// 💥 Payments fail for amounts over $1000
|
||||
// Edge case handling was accidentally changed
|
||||
</code>
|
||||
|
||||
<why_it_fails>
|
||||
- No tests to verify behavior preserved
|
||||
- Complex logic has hidden edge cases
|
||||
- Subtle behavior changes go unnoticed
|
||||
- Breaks in production, not development
|
||||
- Costs customer trust and emergency debugging
|
||||
</why_it_fails>
|
||||
|
||||
<correction>
|
||||
**Correct approach:**
|
||||
|
||||
1. **Write tests FIRST** (using hyperpowers:test-driven-development)
|
||||
- Test happy path
|
||||
- Test all edge cases (amounts over $1000, etc.)
|
||||
- Test error conditions
|
||||
- Run tests → all pass (documenting current behavior)
|
||||
|
||||
2. **Then refactor with tests as safety net**
|
||||
- Extract method → run tests → commit
|
||||
- Rename → run tests → commit
|
||||
- Simplify → run tests → commit
|
||||
|
||||
3. **Tests catch any behavior changes immediately**
|
||||
|
||||
**What you gain:**
|
||||
- Confidence behavior is preserved
|
||||
- Edge cases documented in tests
|
||||
- Catches subtle changes before production
|
||||
- Future refactoring is also safe
|
||||
- Tests serve as documentation
|
||||
</correction>
|
||||
</example>
|
||||
</examples>
|
||||
|
||||
<refactor_vs_rewrite>
|
||||
## When to Refactor
|
||||
|
||||
- Tests exist and pass
|
||||
- Changes are incremental
|
||||
- Business logic stays same
|
||||
- Can transform in small, safe steps
|
||||
- Each step independently valuable
|
||||
|
||||
## When to Rewrite
|
||||
|
||||
- No tests exist (write tests first, then refactor)
|
||||
- Fundamental architecture change needed
|
||||
- Easier to rebuild than modify
|
||||
- Requirements changed significantly
|
||||
- After 3+ failed refactoring attempts
|
||||
|
||||
**Rule:** If you need to change test assertions (not just add tests), you're rewriting, not refactoring.
|
||||
|
||||
## Strangler Fig Pattern (Hybrid)
|
||||
|
||||
**When to use:**
|
||||
- Need to replace legacy system but can't tolerate downtime
|
||||
- Want incremental migration with continuous monitoring
|
||||
- System too large to refactor in one go
|
||||
|
||||
**How it works:**
|
||||
|
||||
1. **Transform:** Create modernized components alongside legacy
|
||||
2. **Coexist:** Both systems run in parallel (façade routes requests)
|
||||
3. **Eliminate:** Retire old functionality piece by piece
|
||||
|
||||
**Example:**
|
||||
|
||||
```
|
||||
Legacy: Monolithic user service (50K LOC)
|
||||
Goal: Microservices architecture
|
||||
|
||||
Step 1 (Transform):
|
||||
- Create new UserService microservice
|
||||
- Implement user creation endpoint
|
||||
- Tests pass in isolation
|
||||
|
||||
Step 2 (Coexist):
|
||||
- Add routing layer (façade)
|
||||
- Route POST /users to new service
|
||||
- Route GET /users to legacy service (for now)
|
||||
- Monitor both, compare results
|
||||
|
||||
Step 3 (Eliminate):
|
||||
- Once confident, migrate GET /users to new service
|
||||
- Remove user creation from legacy
|
||||
- Repeat for remaining endpoints
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Incremental replacement reduces risk
|
||||
- Legacy continues operating during transition
|
||||
- Can pause/rollback at any point
|
||||
- Each migration step is independently valuable
|
||||
|
||||
**Use refactoring within components, Strangler Fig for replacing systems.**
|
||||
</refactor_vs_rewrite>
|
||||
|
||||
<critical_rules>
|
||||
## Rules That Have No Exceptions
|
||||
|
||||
1. **Tests must stay green** throughout refactoring → If they fail, you changed behavior (stop and undo)
|
||||
2. **Commit after each small change** → Large commits hide which change broke what
|
||||
3. **One transformation at a time** → Multiple changes = impossible to debug failures
|
||||
4. **Run tests after EVERY change** → Delayed testing doesn't tell you which change broke it
|
||||
5. **If tests fail 3+ times, question approach** → Might need to rewrite instead, or add tests first
|
||||
|
||||
## Common Excuses
|
||||
|
||||
All of these mean: **Stop and return to the change→test→commit cycle**
|
||||
|
||||
- "Small refactoring, don't need tests between steps"
|
||||
- "I'll test at the end"
|
||||
- "Tests are slow, I'll run once at the end"
|
||||
- "Just fixing bugs while refactoring" (bug fixes = behavior changes = not refactoring)
|
||||
- "Easier to do all at once"
|
||||
- "I know it works without tests"
|
||||
- "While I'm here, I'll also..." (scope creep during refactoring)
|
||||
- "Tests will fail temporarily but I'll fix them" (tests must stay green)
|
||||
</critical_rules>
|
||||
|
||||
<verification_checklist>
|
||||
Before marking refactoring complete:
|
||||
|
||||
- [ ] All tests pass (verified with hyperpowers:test-runner agent)
|
||||
- [ ] No new linter warnings
|
||||
- [ ] No behavior changes introduced
|
||||
- [ ] Code is cleaner/simpler than before
|
||||
- [ ] Each commit in history is small and safe
|
||||
- [ ] bd task documents what was done and why
|
||||
- [ ] Can explain what each transformation did
|
||||
|
||||
**Can't check all boxes?** Return to process and fix before closing bd task.
|
||||
</verification_checklist>
|
||||
|
||||
<integration>
|
||||
**This skill requires:**
|
||||
- hyperpowers:test-driven-development (for writing tests before refactoring if none exist)
|
||||
- hyperpowers:verification-before-completion (for final verification)
|
||||
- hyperpowers:test-runner agent (for running tests without context pollution)
|
||||
|
||||
**This skill is called by:**
|
||||
- General development workflows when improving code structure
|
||||
- After features are complete and working
|
||||
- When preparing code for new features
|
||||
|
||||
**Agents used:**
|
||||
- test-runner (runs tests/commits without polluting main context)
|
||||
</integration>
|
||||
|
||||
<resources>
|
||||
**Detailed guides:**
|
||||
- [Common refactoring patterns](resources/refactoring-patterns.md) - Extract Method, Extract Class, Inline, etc.
|
||||
- [Complete refactoring session example](resources/example-session.md) - Minute-by-minute walkthrough
|
||||
|
||||
**When stuck:**
|
||||
- Tests fail after change → Undo (git restore), make smaller change
|
||||
- 3+ failures → Question if refactoring is right approach, consider rewrite
|
||||
- No tests exist → Use hyperpowers:test-driven-development to write tests first
|
||||
- Unsure how small → If it touches more than one function/file, it's too big
|
||||
</resources>
|
||||
78
skills/refactoring-safely/resources/example-session.md
Normal file
78
skills/refactoring-safely/resources/example-session.md
Normal file
@@ -0,0 +1,78 @@
|
||||
## Example: Complete Refactoring Session
|
||||
|
||||
**Goal:** Extract validation logic from UserService
|
||||
|
||||
**Time: 60 minutes**
|
||||
|
||||
### Minutes 0-5: Verify Tests Pass
|
||||
```bash
|
||||
Dispatch hyperpowers:test-runner: "Run: cargo test"
|
||||
Result: ✓ 234 tests pass
|
||||
```
|
||||
|
||||
### Minutes 5-10: Create bd Task
|
||||
```bash
|
||||
bd create "Refactor: Extract user validation" --type task
|
||||
bd edit bd-456 --design "Extract validation to UserValidator class..."
|
||||
bd update bd-456 --status in_progress
|
||||
```
|
||||
|
||||
### Minutes 10-15: Step 1 - Extract email validation function
|
||||
```rust
|
||||
// Extract validate_email()
|
||||
```
|
||||
```bash
|
||||
Dispatch hyperpowers:test-runner: "Run: cargo test"
|
||||
Result: ✓ 234 tests pass
|
||||
git commit -m "refactor(bd-456): extract email validation"
|
||||
```
|
||||
|
||||
### Minutes 15-20: Step 2 - Extract name validation function
|
||||
```rust
|
||||
// Extract validate_name()
|
||||
```
|
||||
```bash
|
||||
Dispatch hyperpowers:test-runner: "Run: cargo test"
|
||||
Result: ✓ 234 tests pass
|
||||
git commit -m "refactor(bd-456): extract name validation"
|
||||
```
|
||||
|
||||
### Minutes 20-25: Step 3 - Create UserValidator struct
|
||||
```rust
|
||||
struct UserValidator { /* empty */ }
|
||||
impl UserValidator { /* empty */ }
|
||||
```
|
||||
```bash
|
||||
Dispatch hyperpowers:test-runner: "Run: cargo test"
|
||||
Result: ✓ 234 tests pass
|
||||
git commit -m "refactor(bd-456): create UserValidator struct"
|
||||
```
|
||||
|
||||
### Minutes 25-35: Steps 4-6 - Move validations to UserValidator
|
||||
Each step: move one method, test, commit
|
||||
|
||||
### Minutes 35-45: Step 7 - Update UserService to use validator
|
||||
```rust
|
||||
// Use UserValidator instead of inline validation
|
||||
```
|
||||
```bash
|
||||
Dispatch hyperpowers:test-runner: "Run: cargo test"
|
||||
Result: ✓ 234 tests pass
|
||||
git commit -m "refactor(bd-456): use UserValidator in UserService"
|
||||
```
|
||||
|
||||
### Minutes 45-55: Step 8 - Remove duplication from other services
|
||||
Each service: one change, test, commit
|
||||
|
||||
### Minutes 55-60: Final verification and close
|
||||
```bash
|
||||
Dispatch hyperpowers:test-runner: "Run: cargo test"
|
||||
Result: ✓ 234 tests pass
|
||||
|
||||
Dispatch hyperpowers:test-runner: "Run: cargo clippy"
|
||||
Result: ✓ No warnings
|
||||
|
||||
bd close bd-456
|
||||
```
|
||||
|
||||
**Result:** Refactoring complete, 8 safe commits, all tests green throughout.
|
||||
121
skills/refactoring-safely/resources/refactoring-patterns.md
Normal file
121
skills/refactoring-safely/resources/refactoring-patterns.md
Normal file
@@ -0,0 +1,121 @@
|
||||
## Common Refactoring Patterns
|
||||
|
||||
### Extract Method
|
||||
|
||||
**When:** Duplicated code or long function
|
||||
|
||||
```rust
|
||||
// Before: Long function
|
||||
fn process(data: Vec<i32>) -> i32 {
|
||||
let mut sum = 0;
|
||||
for x in data {
|
||||
sum += x * x;
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
// After: Extracted method
|
||||
fn process(data: Vec<i32>) -> i32 {
|
||||
data.iter().map(|x| square(x)).sum()
|
||||
}
|
||||
|
||||
fn square(x: &i32) -> i32 {
|
||||
x * x
|
||||
}
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Extract square() function
|
||||
2. Run tests
|
||||
3. Commit
|
||||
4. Replace loop with iterator
|
||||
5. Run tests
|
||||
6. Commit
|
||||
|
||||
### Rename Variable/Function
|
||||
|
||||
**When:** Name is unclear or misleading
|
||||
|
||||
```rust
|
||||
// Before
|
||||
fn calc(d: Vec<i32>) -> f64 {
|
||||
let s: i32 = d.iter().sum();
|
||||
s as f64 / d.len() as f64
|
||||
}
|
||||
|
||||
// After - Step by step
|
||||
// Step 1: Rename function
|
||||
fn calculate_average(d: Vec<i32>) -> f64 { ... } // Test, commit
|
||||
|
||||
// Step 2: Rename parameter
|
||||
fn calculate_average(data: Vec<i32>) -> f64 { ... } // Test, commit
|
||||
|
||||
// Step 3: Rename variable
|
||||
fn calculate_average(data: Vec<i32>) -> f64 {
|
||||
let sum: i32 = data.iter().sum(); // Test, commit
|
||||
sum as f64 / data.len() as f64
|
||||
}
|
||||
```
|
||||
|
||||
### Extract Class/Struct
|
||||
|
||||
**When:** Class has multiple responsibilities
|
||||
|
||||
```rust
|
||||
// Before: God object
|
||||
struct UserService {
|
||||
db: Database,
|
||||
email_validator: Regex,
|
||||
name_validator: Regex,
|
||||
}
|
||||
|
||||
// After: Single responsibility
|
||||
struct UserService {
|
||||
db: Database,
|
||||
validator: UserValidator, // EXTRACTED
|
||||
}
|
||||
|
||||
struct UserValidator {
|
||||
email_pattern: Regex,
|
||||
name_pattern: Regex,
|
||||
}
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Create empty UserValidator struct
|
||||
2. Test, commit
|
||||
3. Move email_validator field
|
||||
4. Test, commit
|
||||
5. Move name_validator field
|
||||
6. Test, commit
|
||||
7. Update UserService to use UserValidator
|
||||
8. Test, commit
|
||||
|
||||
### Inline Unnecessary Abstraction
|
||||
|
||||
**When:** Abstraction adds no value
|
||||
|
||||
```rust
|
||||
// Before: Pointless wrapper
|
||||
fn get_user_email(user: &User) -> &str {
|
||||
&user.email
|
||||
}
|
||||
|
||||
fn process() {
|
||||
let email = get_user_email(&user); // Just use user.email!
|
||||
}
|
||||
|
||||
// After: Inline
|
||||
fn process() {
|
||||
let email = &user.email;
|
||||
}
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Replace one call site with direct access
|
||||
2. Test, commit
|
||||
3. Replace next call site
|
||||
4. Test, commit
|
||||
5. Remove wrapper function
|
||||
6. Test, commit
|
||||
|
||||
Reference in New Issue
Block a user