Agent SkillsAgent Skills
arpa73

code-refactoring

@arpa73/code-refactoring
arpa73
2
0 forks
Updated 4/1/2026
View on GitHub

Safe code refactoring guide for gnwebsite project. Use when refactoring components, removing duplication, improving code structure, or simplifying complex functions. Covers test-driven refactoring, incremental changes, extract function/composable patterns, and rollback procedures. Ensures refactoring preserves behavior while improving code quality.

Installation

$npx agent-skills-cli install @arpa73/code-refactoring
Claude Code
Cursor
Copilot
Codex
Antigravity

Details

Path.github/skills.backup/code-refactoring/SKILL.md
Branchmain
Scoped Name@arpa73/code-refactoring

Usage

After installing, this skill will be available to your AI coding assistant.

Verify installation:

npx agent-skills-cli list

Skill Instructions


name: code-refactoring description: Safe code refactoring guide for gnwebsite project. Use when refactoring components, removing duplication, improving code structure, or simplifying complex functions. Covers test-driven refactoring, incremental changes, extract function/composable patterns, and rollback procedures. Ensures refactoring preserves behavior while improving code quality.

Code Refactoring Guide

Step-by-step guide for safely refactoring code without breaking functionality in the gnwebsite fullstack project.

When to Use This Skill

Use when:

  • User asks "How do I refactor this code?" or "Can I simplify this?"
  • Reducing code duplication (DRY violations)
  • Simplifying complex functions (>50 lines)
  • Extracting reusable logic to composables
  • Improving code structure or naming
  • Addressing technical debt
  • User mentions: "refactor", "clean up", "simplify", "reduce duplication"

Do NOT use for:

  • Style preferences without measurable benefit
  • Just before deadlines
  • Code without existing tests
  • Changes that alter behavior (that's feature work, not refactoring)

Decision Tree: Proposal or Direct Refactoring?

Need to refactor?
β”‚
β”œβ”€ Breaking changes? (API contracts, schemas, core patterns)
β”‚  └─ YES β†’ Create OpenSpec proposal
β”‚
β”œβ”€ Just reorganizing code? (internal structure, no external impact)
β”‚  └─ YES β†’ Follow safe refactoring steps
β”‚
└─ Unsure?
   └─ Create proposal (better safe than sorry!)

Prerequisites (CRITICAL)

NEVER refactor without tests!

# Check test coverage
cd frontend && npm run test:run -- --coverage
cd backend && docker exec backend pytest --cov

# If coverage < 80% for code being refactored:
# 1. STOP
# 2. Write tests FIRST
# 3. THEN refactor

Minimum requirements:

  • Unit tests exist for all functions being changed
  • Integration tests exist for workflows being changed
  • All tests currently passing βœ…

Phase 1: Preparation

Step 1: Document Current Behavior

Create temporary documentation:

# Refactoring: [Component Name]

## Current Behavior
- Input: X
- Output: Y
- Side effects: Z
- Edge cases: A, B, C

## Existing Tests
- test_case_1: normal flow
- test_case_2: error handling
- test_case_3: edge case

## Success Criteria
After refactor: all tests pass, same behavior

Step 2: Create Safety Backup

# Create backup branch
git checkout -b backup-before-refactor
git checkout -b refactor-my-feature

Step 3: Create Refactoring Checklist

## Refactoring Checklist

### Before
- [ ] All existing tests pass
- [ ] Coverage documented
- [ ] Behavior documented
- [ ] Backup branch created

### During
- [ ] ONE change at a time
- [ ] Run tests after EACH change
- [ ] Commit after EACH success

### After
- [ ] All tests still pass
- [ ] No console errors
- [ ] Manual testing complete
- [ ] Performance unchanged/better
- [ ] Documentation updated

Phase 2: Refactoring Patterns

Pattern A: Extract Function (Reduce Complexity)

When: Function >50 lines or multiple responsibilities

Before:

async function processArticle(article: Article) {
  // Validation (10 lines)
  if (!article.title) throw new Error('Title required')
  if (!article.content) throw new Error('Content required')
  
  // Image processing (15 lines)
  const images = []
  for (const img of article.images || []) {
    const url = img.image?.fileUrl || img.image?.file_url
    if (url) images.push({ url, caption: img.caption })
  }
  
  // Save (20 lines)
  const response = await api.create({ title, content, images })
  return response
}

After:

async function processArticle(article: Article) {
  validateArticle(article)
  const images = processImages(article.images)
  return await saveArticle(article, images)
}

function validateArticle(article: Article) {
  if (!article.title) throw new Error('Title required')
  if (!article.content) throw new Error('Content required')
  if (article.title.length > 200) throw new Error('Title too long')
}

function processImages(images?: ArticleImage[]) {
  return (images || [])
    .map(img => ({ url: getImageUrl(img.image), caption: img.caption }))
    .filter(img => img.url && img.url !== '/placeholder-image.png')
}

async function saveArticle(article: Article, images: ProcessedImage[]) {
  return await api.create({ 
    title: article.title, 
    content: article.content, 
    images 
  })
}

Steps:

  1. Extract ONE function at a time
  2. Run tests after each extraction
  3. Commit each success
  4. Add tests for new functions:
describe('validateArticle', () => {
  it('should throw on missing title', () => {
    expect(() => validateArticle({ content: 'test' }))
      .toThrow('Title required')
  })
})

describe('processImages', () => {
  it('should extract URLs', () => {
    const imgs = [{ image: { fileUrl: 'test.jpg' }, caption: 'Test' }]
    expect(processImages(imgs)).toEqual([{ url: 'test.jpg', caption: 'Test' }])
  })
  
  it('should filter placeholders', () => {
    expect(processImages([{ image: null }])).toEqual([])
  })
})

Pattern B: Extract Composable (Reuse Logic)

When: Same logic duplicated across 3+ components

Before (duplicated in BlogView, ArticleView, CategoryView):

<script setup>
const items = ref([])
const loading = ref(false)
const error = ref('')

const loadItems = async () => {
  loading.value = true
  try {
    const response = await blogService.getAll()
    items.value = response.results
  } catch (err) {
    error.value = 'Failed to load'
  } finally {
    loading.value = false
  }
}

onMounted(loadItems)
</script>

After:

// composables/useDataLoader.ts
export function useDataLoader<T>(
  loadFn: () => Promise<{ results: T[] }>
) {
  const items = ref<T[]>([])
  const loading = ref(false)
  const error = ref('')
  
  const load = async () => {
    loading.value = true
    error.value = ''
    try {
      const response = await loadFn()
      items.value = response.results || []
    } catch (err) {
      error.value = 'Failed to load items'
      console.error(err)
    } finally {
      loading.value = false
    }
  }
  
  onMounted(load)
  return { items, loading, error, reload: load }
}
<!-- All components now -->
<script setup>
import { useDataLoader } from '@/composables/useDataLoader'

const { items, loading, error, reload } = useDataLoader(() => 
  blogService.getAll()
)
</script>

Steps:

  1. Create composable
  2. Write composable tests
  3. Migrate ONE component
  4. Test that component
  5. Commit
  6. Repeat for remaining components

Pattern C: Consolidate Styles

When: Same CSS in 3+ components

Before (duplicated in 6 form components):

<style scoped>
.form-group { margin-bottom: 1.5rem; }
.form-control { width: 100%; padding: 0.75rem; }
.btn-primary { background: #007bff; color: white; }
</style>

After:

/* styles/admin-forms.css */
.form-group { margin-bottom: 1.5rem; }
.form-control { width: 100%; padding: 0.75rem; }
.btn-primary { background: #007bff; color: white; }
<!-- Components keep only unique styles -->
<style scoped>
.special-field { /* component-specific */ }
</style>

Steps:

  1. Extract to shared CSS file
  2. Import in main.ts/App.vue
  3. Remove from ONE component
  4. Visual test
  5. Commit
  6. Repeat for remaining

Pattern D: Replace with Utility

When: Same calculation in 5+ places

Before (in 5 files):

const imageUrl = img.image?.fileUrl || img.image?.file_url || '/placeholder-image.png'

After:

// utils/imageData.ts
export function extractImageUrl(
  imageData: any, 
  fallback = '/placeholder-image.png'
): string {
  if (!imageData) return fallback
  return imageData.fileUrl || imageData.file_url || fallback
}

// All files:
const imageUrl = extractImageUrl(img.image)

Steps:

  1. Create utility
  2. Write comprehensive tests
  3. Replace in ONE location
  4. Test
  5. Commit
  6. Repeat for each location

Phase 3: Incremental Execution

CRITICAL WORKFLOW: One change β†’ Test β†’ Commit

# 1. Create working branch
git checkout -b refactor-my-feature

# 2. Make ONE small change
# ... edit code ...

# 3. Run tests
npm run test:run

# 4. If pass, commit
git add .
git commit -m "refactor: extract validateArticle function

- Moved validation from processArticle
- All tests passing
- No behavior changes"

# 5. Repeat for next change
# ... edit code ...
npm run test:run
git commit -m "refactor: extract processImages"

# Continue until complete

Testing After EVERY Change

# After each change:

# 1. Unit tests
npm run test:run

# 2. Type check
npm run type-check

# 3. Pattern check
npm run test:patterns

# 4. Manual spot check
npm run dev
# Test the specific feature

# If ANY fail:
git reset --hard HEAD  # Undo
# OR fix before committing

Phase 4: Validation

Comprehensive Testing Checklist

Automated:

  • All unit tests pass
  • All integration tests pass
  • Type check passes
  • Pattern enforcement passes
  • No new linting errors

Manual:

  • Feature works exactly as before
  • No console errors
  • No visual regressions
  • Performance unchanged/better
  • Works in all browsers

Code Quality:

  • More readable
  • More testable
  • Less duplication
  • Lower complexity
  • Reasonable file sizes

Performance Verification

# Before refactor
npm run build
# Note: size, time

# After refactor
npm run build
# Compare: should be similar or better

Phase 5: Documentation

Update Project Docs

If new patterns introduced:

CODEBASE_ESSENTIALS.md:

- **Image URL extraction:** Always use `extractImageUrl()` from `@/utils/imageData`. Prevents silent failures.

CODEBASE_CHANGELOG.md:

### Session: Refactor Image URL Handling (Jan 13, 2026)

**Goal**: Eliminate duplicated image URL logic

**Changes**:
- Created `extractImageUrl()` utility
- Replaced 12 instances
- Added tests (8 unit, 6 integration)

**Impact**:
- Reduced duplication by ~80 lines
- Improved testability

**Validation**:
- βœ… All 227 tests pass
- βœ… No behavior changes
- βœ… Build size unchanged

**Commit**: abc123

Anti-Patterns (DON'T DO THIS)

❌ Big Bang Refactor

# WRONG: Change 50 files at once
git commit -m "refactor: everything"

# CORRECT: Incremental commits
git commit -m "refactor: extract validation"
# test
git commit -m "refactor: extract image processing"
# test

❌ Refactor Without Tests

// WRONG: No tests exist
function refactoredFunction() {
  // Hope this works! 🀞
}

// CORRECT: Write tests first
test('refactoredFunction works', () => { ... })
function refactoredFunction() { ... }

❌ Change Behavior

// WRONG: Added new validation during refactor
function validateArticle(article: Article) {
  if (!article.title) throw new Error('Title required')
  if (!article.excerpt) throw new Error('Excerpt required')  // NEW!
}

// CORRECT: Preserve exact behavior
function validateArticle(article: Article) {
  if (!article.title) throw new Error('Title required')
  // Same validation as before, just extracted
}

❌ Premature Optimization

// WRONG: No measured problem, making code complex
// Replacing simple readable code with "faster" code

// CORRECT: Measure first, optimize if needed
// Keep code simple unless profiling shows issue

❌ Refactor Under Pressure

// WRONG: "Production deploy tomorrow, let me refactor today!"
// CORRECT: Refactor when you have time to test properly

Common Scenarios

Scenario 1: Component Too Large (>300 lines)

Fix:

  1. Extract child components
  2. Extract composables for logic
  3. Extract utilities for helpers
  4. ONE responsibility per file

Scenario 2: Duplicated Code (3+ places)

Fix:

  1. Identify common pattern
  2. Extract to utility/composable
  3. Write tests
  4. Replace one by one
  5. Delete duplicates

Scenario 3: Hard to Test

Fix:

  1. Identify dependencies
  2. Extract to parameters
  3. Make functions pure
  4. Write tests with mocks

Scenario 4: Unclear Naming

Fix:

  1. Rename ONE identifier
  2. Use IDE refactor (F2 in VS Code)
  3. Run tests
  4. Commit
  5. Repeat

Emergency Rollback

If refactoring breaks something:

# Option 1: Revert last commit
git revert HEAD

# Option 2: Restore from backup
git checkout backup-before-refactor
git checkout -b refactor-my-feature-v2

# Option 3: Stash and investigate
git stash
npm run test:run  # Pass now?
git stash pop     # Re-apply and fix

# Option 4: Nuclear
git reset --hard origin/development
# Start over with smaller changes

Success Metrics

Refactoring succeeds when:

βœ… All tests pass (no behavior changes)
βœ… Code more readable (clear improvement)
βœ… Complexity reduced (fewer lines, simpler logic)
βœ… Duplication removed (DRY)
βœ… Test coverage maintained/improved
βœ… Performance unchanged/better
βœ… No regressions (manual testing confirms)

Key Commands

# Before refactoring
npm run test:run -- --coverage        # Check coverage
docker exec backend pytest --cov      # Backend coverage
git checkout -b backup-before-refactor # Safety backup

# During refactoring (after EACH change)
npm run test:run                      # Frontend tests
npm run type-check                    # TypeScript
npm run test:patterns                 # Pattern enforcement
docker exec backend pytest -v         # Backend tests
git commit -m "refactor: [change]"    # Commit success

# After refactoring
npm run build                         # Verify build
npm run dev                          # Manual test
git push origin refactor-my-feature   # Push when complete

Examples

Example 1: Extract Function

# 1. Initial state: 80-line function
# 2. Extract validation (commit)
git commit -m "refactor: extract validateArticle"
# 3. Extract image processing (commit)
git commit -m "refactor: extract processImages"
# 4. Simplify main function (commit)
git commit -m "refactor: simplify processArticle"
# Result: 3 small focused functions

Example 2: Extract Composable

# 1. Create useDataLoader composable
# 2. Write tests for composable
git commit -m "refactor: add useDataLoader composable"
# 3. Migrate BlogView (test, commit)
git commit -m "refactor: BlogView uses useDataLoader"
# 4. Migrate ArticleView (test, commit)
git commit -m "refactor: ArticleView uses useDataLoader"
# 5. Migrate CategoryView (test, commit)
git commit -m "refactor: CategoryView uses useDataLoader"
# Result: Eliminated 60 lines of duplication

When to Stop

Stop refactoring when:

  • Tests start failing frequently (too aggressive)
  • Code is "good enough" (perfect is enemy of done)
  • Deadline approaching (commit what you have)
  • No measurable benefit (diminishing returns)
  • You're just tweaking style (not improving structure)

Related Resources

More by arpa73

View all