Skip to content

Refactor Safely

Restructure code with confidence by using existing tests as a safety net and the spec-first workflow to define exactly what changes and what stays the same.

Your Flutter app works, but the code needs restructuring — extracting widgets, reorganizing files, splitting a large class, improving naming, or migrating to a different state management pattern. You want to change the structure without changing the behavior.

  • ACT installed and configured in your project (Project Setup)
  • An existing Flutter app with the code you want to refactor
  • Existing tests that cover the behavior you need to preserve (if you have no tests, write them first — see Common pitfalls below)
  • A Claude Code or OpenCode session open in your project directory

Write a spec that clearly states what to change and what to preserve:

Terminal window
/act:workflow:spec "refactor: extract the expense list screen into smaller widgets. The ExpenseListScreen in lib/src/features/expenses/expense_list_screen.dart is 400+ lines with inline logic for filtering, sorting, and formatting. Extract into: ExpenseFilterBar, ExpenseSortMenu, ExpenseListTile, and ExpenseAmountText. All existing behavior and tests must continue to pass unchanged."

ACT will examine the file, identify the boundaries for extraction, and generate a spec that documents:

  • Which code moves where
  • What the public API of each new widget looks like
  • Constraints: no behavior changes, no new features, all tests pass

The spec’s Boundaries section is especially important here — it defines what is explicitly out of scope.

Generate a plan that restructures incrementally:

Terminal window
/act:workflow:plan ai_specs/refactor-expense-list-spec.md

A good refactoring plan follows this pattern:

  • Phase 1: Run existing tests to confirm they pass (baseline)
  • Phase 2: Extract one component, run tests
  • Phase 3: Extract the next component, run tests
  • Phase N: Final cleanup and verification

Each phase should leave the app in a working state. If a phase breaks tests, the problem is isolated to that specific extraction.

Review the plan to confirm it does not introduce new features or change behavior. Refactoring means moving code, not rewriting it.

Run the refactoring:

Terminal window
/act:workflow:work ai_specs/refactor-expense-list-plan.md

ACT implements each phase, runs flutter analyze and flutter test after each extraction, and commits working code at every step. If a test fails, ACT stops and investigates before proceeding.

After ACT finishes, review the changes carefully:

  • The total behavior should be identical
  • Tests should pass without modification (unless test files reference internal details that moved)
  • The git history should show small, focused commits — one per extraction
Terminal window
/act:git:push-make-pr

After completing these steps, you should have:

  • A spec documenting what was refactored and the constraints
  • A plan with incremental extraction phases, all marked complete
  • Cleaner, more modular code
  • All existing tests passing without changes to test logic
  • A git history with small commits, each in a working state
  • A pull request that is easy to review because each commit is self-contained
  • Refactoring without tests. If the code you are refactoring has no tests, write them first. Use a separate spec to add tests for the current behavior, then use this playbook for the refactoring. Without tests, you have no way to verify behavior is preserved.
  • Combining refactoring with new features. Never add features in a refactoring PR. If you spot an improvement opportunity while refactoring, create a separate spec for it. Mixed PRs are hard to review and hard to revert.
  • Too-large phases. Each phase should extract or move one thing. If a phase touches 10+ files, it is too big. Ask ACT to break it into smaller steps.
  • Renaming without updating references. When moving files or renaming classes, every import and reference must update. ACT handles this automatically, but verify by running flutter analyze after each phase.