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.
Scenario
Section titled “Scenario”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.
Prerequisites
Section titled “Prerequisites”- 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
1. Document the refactoring in a spec
Section titled “1. Document the refactoring in a spec”Write a spec that clearly states what to change and what to preserve:
/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.
2. Create the plan
Section titled “2. Create the plan”Generate a plan that restructures incrementally:
/act:workflow:plan ai_specs/refactor-expense-list-spec.mdA 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.
3. Execute the plan
Section titled “3. Execute the plan”Run the refactoring:
/act:workflow:work ai_specs/refactor-expense-list-plan.mdACT 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.
4. Review the diff
Section titled “4. Review the diff”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
5. Push the refactoring
Section titled “5. Push the refactoring”/act:git:push-make-prExpected output
Section titled “Expected output”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
Common pitfalls
Section titled “Common pitfalls”- 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 analyzeafter each phase.
Next steps
Section titled “Next steps”- Add a Feature — now that the code is cleaner, add the next feature
- Bugfix Workflow — fix any issues surfaced during refactoring
- Command Map — quick reference for all ACT commands