Skip to main content
Version: v0.24.0

Refactoring Guide

This guide explains how to plan and execute major refactorings in this project.

When to Plan a Refactoring

Not every code change needs a detailed plan. Create a refactoring plan when:

🔴 Major changes requiring planning:

  • Splitting modules into packages (>5 files affected, >500 lines moved)
  • Architectural changes (new packages, module restructuring)
  • Breaking changes (API changes, config format migrations)

🟡 Medium changes that might benefit from planning:

  • Complex features with multiple moving parts
  • Changes affecting many files (>3 files, unclear best approach)
  • Refactorings with unclear scope

🟢 Small changes - no planning needed:

  • Bug fixes (straightforward, <100 lines)
  • Small features (<3 files, clear approach)
  • Documentation updates
  • Cosmetic changes (formatting, renaming)

The Planning Process

1. Create a Planning Document

Create a file in the planning/ directory (git-ignored for free iteration):

# Example:
touch planning/my-feature-refactoring-plan.md

Note: The planning/ directory is git-ignored, so you can iterate freely without polluting git history.

2. Use the Planning Template

Every planning document should include:

# <Feature> Refactoring Plan

**Status**: 🔄 PLANNING | 🚧 IN PROGRESS | ✅ COMPLETED | ❌ CANCELLED
**Created**: YYYY-MM-DD
**Last Updated**: YYYY-MM-DD

## Problem Statement

- What's the issue?
- Why does it need fixing?
- Current pain points

## Proposed Solution

- High-level approach
- File structure (before/after)
- Module responsibilities

## Migration Strategy

- Phase-by-phase breakdown
- File lifecycle (CREATE/MODIFY/DELETE/RENAME)
- Dependencies between phases
- Testing checkpoints

## Risks & Mitigation

- What could go wrong?
- How to prevent it?
- Rollback strategy

## Success Criteria

- Measurable improvements
- Testing requirements
- Verification steps

See planning/README.md for detailed template explanation.

3. Iterate Freely

Since planning/ is git-ignored:

  • Draft multiple versions
  • Get AI assistance without commit pressure
  • Refine until the plan is solid
  • No need to clean up intermediate versions

4. Implementation Phase

Once plan is approved:

  • Follow the phases defined in the plan
  • Test after each phase (don't skip!)
  • Update plan if issues discovered
  • Track progress through phase status

5. After Completion

Option A: Archive in docs/development/ If the plan has lasting value (successful pattern, reusable approach):

mv planning/my-feature-refactoring-plan.md docs/development/
git add docs/development/my-feature-refactoring-plan.md
git commit -m "docs: archive successful refactoring plan"

Option B: Delete If the plan served its purpose and code is the source of truth:

rm planning/my-feature-refactoring-plan.md

Option C: Keep locally (not committed) For "why we didn't do X" reference:

mkdir -p planning/archive
mv planning/my-feature-refactoring-plan.md planning/archive/
# Still git-ignored, just organized

Real-World Example

The sensor/ package refactoring (Nov 2025) is a successful example:

Before:

  • sensor.py - 2,574 lines, hard to navigate

After:

  • sensor/ package with 5 focused modules
  • Each module <800 lines
  • Clear separation of concerns

Process:

  1. Created planning/module-splitting-plan.md (now in docs/development/)
  2. Defined 6 phases with clear file lifecycle
  3. Implemented phase by phase
  4. Tested after each phase
  5. Documented in AGENTS.md
  6. Moved plan to docs/development/ as reference

Key learnings:

  • Temporary _impl.py files avoid Python package conflicts
  • Test after EVERY phase (don't accumulate changes)
  • Clear file lifecycle (CREATE/MODIFY/DELETE/RENAME)
  • Phase-by-phase approach enables safe rollback

Note: The complete module splitting plan was documented during implementation but has been superseded by the actual code structure.

Phase-by-Phase Implementation

Why Phases Matter

Breaking refactorings into phases:

  • ✅ Enables testing after each change (catch bugs early)
  • ✅ Allows rollback to last good state
  • ✅ Makes progress visible
  • ✅ Reduces cognitive load (focus on one thing)
  • ❌ Takes more time (but worth it!)

Phase Structure

Each phase should:

  1. Have clear goal - What's being changed?
  2. Document file lifecycle - CREATE/MODIFY/DELETE/RENAME
  3. Define success criteria - How to verify it worked?
  4. Include testing steps - What to test?
  5. Estimate time - Realistic time budget

Example Phase Documentation

### Phase 3: Extract Helper Functions (Session 3)

**Goal**: Move pure utility functions to helpers.py

**File Lifecycle**:

- ✨ CREATE `sensor/helpers.py` (utility functions)
- ✏️ MODIFY `sensor/core.py` (import from helpers.py)

**Steps**:

1. Create sensor/helpers.py
2. Move pure functions (no state, no self)
3. Add comprehensive docstrings
4. Update imports in core.py

**Estimated time**: 45 minutes

**Success criteria**:

- ✅ All pure functions moved
-`./scripts/lint-check` passes
- ✅ HA starts successfully
- ✅ All entities work correctly

Testing Strategy

After Each Phase

Minimum testing checklist:

# 1. Linting passes
./scripts/lint-check

# 2. Home Assistant starts
./scripts/develop
# Watch for startup errors in logs

# 3. Integration loads
# Check: Settings → Devices & Services → Tibber Prices
# Verify: All entities appear

# 4. Basic functionality
# Test: Data updates without errors
# Check: Entity states update correctly

Comprehensive Testing (Final Phase)

After completing all phases:

  • Test all entities (sensors, binary sensors)
  • Test configuration flow (add/modify/remove)
  • Test options flow (change settings)
  • Test services (custom service calls)
  • Test error handling (disconnect API, invalid data)
  • Test caching (restart HA, verify cache loads)
  • Test time-based updates (quarter-hour refresh)

Common Pitfalls

❌ Skip Planning for Large Changes

Problem: "This seems straightforward, I'll just start coding..."

Result: Halfway through, realize the approach doesn't work. Wasted time.

Solution: If unsure, spend 30 minutes on a rough plan. Better to plan and discard than get stuck.

❌ Implement All Phases at Once

Problem: "I'll do all phases, then test everything..."

Result: 10+ files changed, 2000+ lines modified, hard to debug if something breaks.

Solution: Test after EVERY phase. Commit after each successful phase.

❌ Forget to Update Documentation

Problem: Code is refactored, but AGENTS.md and docs/ still reference old structure.

Result: AI/humans get confused by outdated documentation.

Solution: Include "Documentation Phase" at the end of every refactoring plan.

❌ Ignore the Planning Directory

Problem: "I'll just create the plan in docs/ directly..."

Result: Git history polluted with draft iterations, or pressure to "commit something" too early.

Solution: Always use planning/ for work-in-progress. Move to docs/ only when done.

Integration with AI Development

This project uses AI heavily (GitHub Copilot, Claude). The planning process supports AI development:

AI reads from:

  • AGENTS.md - Long-term memory, patterns, conventions (AI-focused)
  • docs/development/ - Human-readable guides (human-focused)
  • planning/ - Active refactoring plans (shared context)

AI updates:

  • AGENTS.md - When patterns change
  • planning/*.md - During refactoring implementation
  • docs/development/ - After successful completion

Why separate AGENTS.md and docs/development/?

  • AGENTS.md: Technical, comprehensive, AI-optimized
  • docs/development/: Practical, focused, human-optimized
  • Both stay in sync but serve different audiences

See AGENTS.md section "Planning Major Refactorings" for AI-specific guidance.

Tools and Resources

Planning Directory

  • planning/ - Git-ignored workspace for drafts
  • planning/README.md - Detailed planning documentation
  • planning/*.md - Active refactoring plans

Example Plans

  • docs/development/module-splitting-plan.md - ✅ Completed, archived
  • planning/config-flow-refactoring-plan.md - 🔄 Planned (1013 lines → 4 modules)
  • planning/binary-sensor-refactoring-plan.md - 🔄 Planned (644 lines → 4 modules)
  • planning/coordinator-refactoring-plan.md - 🔄 Planned (1446 lines, high complexity)

Helper Scripts

./scripts/lint-check   # Verify code quality
./scripts/develop # Start HA for testing
./scripts/lint # Auto-fix issues

FAQ

Q: When should I create a plan vs. just start coding?

A: If you're asking this question, you probably need a plan. 😊

Simple rule: If you can't describe the entire change in 3 sentences, create a plan.

Q: How detailed should the plan be?

A: Detailed enough to execute without major surprises, but not a line-by-line script.

Good plan level:

  • Lists all files affected (CREATE/MODIFY/DELETE)
  • Defines phases with clear boundaries
  • Includes testing strategy
  • Estimates time per phase

Too detailed:

  • Exact code snippets for every change
  • Line-by-line instructions

Too vague:

  • "Refactor sensor.py to be better"
  • No phase breakdown
  • No testing strategy

Q: What if the plan changes during implementation?

A: Update the plan! Planning documents are living documents.

If you discover:

  • Better approach → Update "Proposed Solution"
  • More phases needed → Add to "Migration Strategy"
  • New risks → Update "Risks & Mitigation"

Document WHY the plan changed (helps future refactorings).

Q: Should every refactoring follow this process?

A: No! Use judgment:

  • Small changes (<100 lines, clear approach): Just do it, no plan needed
  • Medium changes (unclear scope): Write rough outline, refine if needed
  • Large changes (>500 lines, >5 files): Full planning process

Q: How do I know when a refactoring is successful?

A: Check the "Success Criteria" from your plan:

Typical criteria:

  • ✅ All linting checks pass
  • ✅ HA starts without errors
  • ✅ All entities functional
  • ✅ No regressions (existing features work)
  • ✅ Code easier to understand/modify
  • ✅ Documentation updated

If you can't tick all boxes, the refactoring isn't done.

Summary

Key takeaways:

  1. Plan when scope is unclear (>500 lines, >5 files, breaking changes)
  2. Use planning/ directory for free iteration (git-ignored)
  3. Work in phases and test after each phase
  4. Document file lifecycle (CREATE/MODIFY/DELETE/RENAME)
  5. Update documentation after completion (AGENTS.md, docs/)
  6. Archive or delete plan after implementation

Remember: Good planning prevents half-finished refactorings and makes rollback easier when things go wrong.


Next steps:

  • Read planning/README.md for detailed template
  • Check docs/development/module-splitting-plan.md for real example
  • Browse planning/ for active refactoring plans