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
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
npx agent-skills-cli listSkill 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:
- Extract ONE function at a time
- Run tests after each extraction
- Commit each success
- 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:
- Create composable
- Write composable tests
- Migrate ONE component
- Test that component
- Commit
- 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:
- Extract to shared CSS file
- Import in main.ts/App.vue
- Remove from ONE component
- Visual test
- Commit
- 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:
- Create utility
- Write comprehensive tests
- Replace in ONE location
- Test
- Commit
- 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:
- Extract child components
- Extract composables for logic
- Extract utilities for helpers
- ONE responsibility per file
Scenario 2: Duplicated Code (3+ places)
Fix:
- Identify common pattern
- Extract to utility/composable
- Write tests
- Replace one by one
- Delete duplicates
Scenario 3: Hard to Test
Fix:
- Identify dependencies
- Extract to parameters
- Make functions pure
- Write tests with mocks
Scenario 4: Unclear Naming
Fix:
- Rename ONE identifier
- Use IDE refactor (F2 in VS Code)
- Run tests
- Commit
- 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
- developer-checklist - Pre-commit validation
- feature-implementation - Adding features
- CODEBASE_ESSENTIALS.md - Current patterns
- CODEBASE_CHANGELOG.md - Historical changes
- .archive/guides/DRY_REFACTORING_GUIDE.md - Past refactor example
More by arpa73
View allMaintain AI-optimized documentation and organize codebase history for gnwebsite project. Use when updating docs, organizing changelog, improving readability for AI agents, or when documentation becomes too large. Covers changelog archiving, AI-friendly writing patterns, semantic structure, and knowledge retrieval optimization. Ensures documentation stays readable and discoverable for both humans and AI systems.
Create new Agent Skills from existing project guides and documentation. Use when the user wants to create a skill, turn documentation into a skill, or make a guide into a reusable capability. Converts guides from docs/ into portable .github/skills/ format following VS Code Agent Skills standard.
Create new Agent Skills from existing project guides and documentation. Use when the user wants to create a skill, turn documentation into a skill, or make a guide into a reusable capability. Converts guides from docs/ into portable .github/skills/ format following VS Code Agent Skills standard.
Safe dependency update workflow for gnwebsite fullstack Django/Vue project. Use when updating packages, upgrading dependencies, fixing vulnerabilities, or when user asks to update dependencies. Covers backend Python (pyproject.toml), frontend npm packages, vulnerability audits, testing requirements, and rollback procedures. Ensures updates maintain compatibility and don't break existing functionality.
