Infrastructure-as-Code Refactor Workflow
Refactor IaC with the same discipline as code: behavior-preserving steps, plan-diff review as the test suite, and staged applies with rollback.
Objective
Improve infrastructure code structure without changing deployed behavior, using plan diffs as the verification at every step.
Environment
- Existing IaC with structural debt (copy-paste modules, magic values)
- A plan/apply pipeline with diff review
- A sandbox environment for intermediate states
Steps
1. Capture the behavioral baseline
Run the plan against current state and save it: the empty or known-only diff is the invariant the refactor must preserve.
Expected state: A baseline plan exists showing exactly what the current code would change.
2. Refactor in behavior-preserving steps
Extract modules, parameterize magic values, and split files one step at a time, running the plan after each step.
Expected state: Each refactor step's plan matches the baseline with no new actions.
3. Review the plan diff as the test
Treat any diff beyond the baseline as a test failure: either the refactor changed behavior (revert) or exposed dead code (delete deliberately).
Expected state: No refactor step merges with an unexplained plan diff.
4. Apply to sandbox, then production
Apply the refactored code to a sandbox to catch runtime differences plans miss, then production with the same reviewed plan.
Expected state: Sandbox apply completes with identical state; production follows the reviewed path.
5. Bank the structure for the next change
With modules extracted and parameters named, the next infrastructure change is a small diff against clean code.
Expected state: The following feature change lands as a minimal, readable diff.
Expected outcome
Infrastructure code refactored to modular structure with behavior provably preserved, verified by plan diffs and sandbox applies.
Verification
The final plan equals the baseline; the sandbox state is identical pre and post; the next infrastructure change diff is demonstrably smaller.
The plan is the refactor’s test suite
Refactoring infrastructure code without changing behavior is possible, and the plan diff is the test that proves it. Extract a module, re-run the plan: any new action is either a bug in the refactor or dead code discovered.
baseline plan (saved): no changes
(state matches code exactly)
step 1: extract compute module
plan: identical to baseline OK
step 2: parameterize the magic 443->var.https_port
plan: identical OK
step 3: split env files (staging/prod)
plan: identical OK
step 4: remove unused security group
plan: DIFFERS -- destroy sg-044
-> dead code since 2024 (verified)
-> deliberate deletion, documented
sandbox apply: state identical to pre-refactor
next change (add region): 34-line diff
(was: 180-line copy-paste edit)
A clean plan is the invariant
The refactoring loop is: change code, run plan, compare to baseline. Zero new actions means structure changed and behavior did not — the same guarantee unit tests give application refactors, available for infrastructure at every step.
Diffs that differ are discoveries
When a refactor step produces a new destroy or create, it usually is not a mistake: it is dead code the structure was hiding (an unused resource, an orphaned rule). These get deleted deliberately with the evidence attached, which is how refactors pay for themselves.