
Refactor Module
- 24 installs
- 22 repo stars
- Updated May 28, 2026
- acedergren/agentic-tools
refactor-module is a Claude Code skill that guides when to extract Terraform code into a reusable module and how to migrate state safely.
About
refactor-module is a Claude Code skill for deciding whether and how to extract Terraform code into a reusable module. It covers the modularize-vs-inline decision, module boundaries, anti-patterns like leaky abstraction and version sprawl, and safe state migration with terraform state mv. A developer uses it when Terraform code is repeated and they are weighing a module or need to migrate state safely. It bundles an error-recovery reference for state migrations gone wrong.
- Decides when to extract Terraform code into a reusable module versus keep it inline
- Warns about the state-migration trap and requires terraform state mv with backups
- Flags leaky-abstraction and module-version anti-patterns that cause module sprawl
Refactor Module by the numbers
- 24 all-time installs (skills.sh)
- Ranked #893 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
refactor-module capabilities & compatibility
- Capabilities
- terraform refactoring · module design · state migration · iac review
- Works with
- terraform · aws
- Use cases
- devops · refactoring
- Pricing
- Free
What refactor-module says it does
Use when deciding whether to extract Terraform code into a reusable module, determining module boundaries, or migrating state after modularization.
Never refactor inline resources to a module without running `terraform state mv` first — Terraform will plan to destroy and recreate every resource.
npx skills add https://github.com/acedergren/agentic-tools --skill refactor-moduleAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 24 |
|---|---|
| repo stars | ★ 22 |
| Last updated | May 28, 2026 |
| Repository | acedergren/agentic-tools ↗ |
What it does
Decide whether to extract Terraform into a reusable module and migrate state safely without destroy/recreate.
Who is it for?
Deciding module boundaries for repeated Terraform code and migrating state without destroy/recreate.
Skip if: Terraform syntax help or modularizing code used only once or twice.
When should I use this skill?
Deciding whether to extract Terraform code into a reusable module, determining module boundaries, or migrating state after modularization.
What you get
A well-scoped Terraform module decision with intent-based interfaces and a safe, backed-up state migration.
- modularize-vs-inline decision
- module boundary design
- safe state migration steps
By the numbers
- Break-even at 4+ identical usages with stable API
- Modularize only after the third real instance
- Six-step refactoring checklist
Files
Terraform Module Refactoring - Decision Expert
Assumption: You know Terraform syntax. This covers when to modularize vs keep inline.
NEVER
- Never modularize on first usage — wait for the third real instance; premature abstraction locks in the wrong seam.
- Never expose module variables 1:1 with resource arguments — that's not abstraction, it's indirection (the Leaky Abstraction trap).
- Never refactor inline resources to a module without running
terraform state mvfirst — Terraform will plan to destroy and recreate every resource. - Never create a module for frequently-changing code — module API changes cascade across all consumers.
- Never skip
terraform state pull > backup.tfstatebefore any state migration.
The Core Decision
Considering creating a module?
│
├─ Used once → NEVER modularize (keep inline, wait for third)
│ WHY: Premature abstraction = wrong seam baked in early
│
├─ Used 2–3 times → MAYBE
│ ├─ >80% identical config → modularize
│ ├─ <50% identical → use locals instead
│ └─ Different teams → DON'T (coordination overhead > benefit)
│
├─ Used 4+ times → Modularize IF config stable (not changing every sprint)
│ WHY: Module changes = N consumer PRs; unstable API kills teams
│
└─ Compliance/security requirement → Modularize immediately
WHY: Module = single enforcement point across all consumersBreak-even: Module worth it at 4+ identical usages + stable API + compliance need. Time cost: Simple module = 2 hours. Complex with state migration = 2 days planning + 4 hours execution.
Before Extracting: Strategic Check
| Question | Threshold | Decision |
|---|---|---|
| How many usages? | <3 | Keep inline |
| How identical? | <50% same | Use locals, not module |
| Change frequency | Weekly | DON'T (unstable API) |
| Test coverage | <50% | TOO RISKY (breaking changes uncaught) |
| Consumer count | 10+ | Every change = 10 PRs, plan migration carefully |
Anti-Patterns
Leaky Abstraction (most common)
Signal: Module variables match resource arguments 1:1 (50 variables for a VPC module).
Fix: Expose intent, not resource config:
// Instead of 50 variables:
variable "network_config" {
type = object({ cidr = string, azs = list(string), public_subnets = number, private_subnets = number })
}Test: "Does the module consumer need AWS VPC knowledge to use this?" YES = leaky abstraction.
State Migration Trap (most dangerous)
Moving inline resources into a module changes state addresses: aws_vpc.main → module.network.aws_vpc.main
Terraform reads this as "destroy old, create new" — no warning, identical config, production outage.
# Always: backup → move → verify
terraform state pull > backup-$(date +%s).tfstate
terraform state mv aws_vpc.main module.network.aws_vpc.main
terraform plan # MUST show: No changesLoad `references/error-recovery.md` if already applied and resources were destroyed.
Module Version Hell
Signal: grep -r 'source.*?ref=' . | sort | uniq -c shows 3+ active versions.
Fix: Breaking changes require major version + 6-month deprecation + migration guide. Or eliminate versions entirely with monorepo workspace protocol.
Module Boundaries
Where to draw the boundary?
│
├─ By lifecycle → GOOD (VPC rarely changes vs EC2 often changes)
├─ By team ownership → GOOD (clear responsibility)
├─ By technology type → BAD ("database module" cuts across concerns)
└─ By resource type → BAD (aws_vpc module alone loses cohesion)Good: VPC + subnets + route tables + NAT gateway (one cohesive networking unit) Bad: Just VPC (consumer must wire subnets manually)
Prefer composition (small focused modules wired together) over monolithic (one module creates everything). Exception: compliance modules that must enforce standards together.
Refactoring Checklist
1. grep -r "resource \"aws_s3_bucket\"" . — confirm 3+ usages before touching anything 2. diff app1/s3.tf app2/s3.tf — confirm >80% identical, not superficially similar 3. Design interface around intent (bucket_type = "data"|"logs"|"artifacts"), not resource args 4. terraform state pull > backup.tfstate — always before state moves 5. terraform state mv <old-address> <new-address> — one resource at a time 6. terraform plan — must show "No changes" before proceeding
When to Load References
Load `references/error-recovery.md` when:
- State migration already applied and caused destroy/recreate
- Module has grown to 10+ boolean toggles and needs redesign
- Multiple module versions causing maintenance coordination problems
Do NOT load for:
- Basic modularization decisions (use Core Decision tree above)
- Single resource state moves (use Refactoring Checklist above)
- Terraform syntax help (see official docs)
refactor-module - Terraform Module Decision Expert
Version: 3.0.0 Grade: F → C (31/120 → ~70/120) Token Reduction: 538 lines → ~280 lines (48% reduction)
What This Skill Does
Terraform module extraction decision framework. Focuses on when to modularize vs keep inline and avoiding over-abstraction. NOT a Terraform tutorial.
TDD Improvements Applied
1. Description Quality (RED → GREEN)
Problem: Description said "transform monolithic Terraform" without decision context Test Failed: Didn't explain WHEN to use skill
Fix:
- Added 5 specific decision scenarios
- Clear focus: "when to create module vs keep inline"
- Keywords: module boundaries, when to modularize, module sprawl
Result: ✅ Agent loads for refactoring decisions, not Terraform tutorials
2. Knowledge Delta (RED → GREEN)
Problem: 70% generic Terraform tutorial (variable syntax, resource blocks) Test Failed: Basic Terraform knowledge, not expertise
Removed (260+ lines):
- Terraform syntax tutorials
- HCL formatting examples
- Generic variable validation patterns
Added (280 lines of expert insights):
- Module vs inline decision tree (1 usage = inline, 2-3 = maybe, 4+ = yes)
- 4 anti-patterns (leaky abstractions, premature modularization, state migration, version hell)
- Module boundary patterns (by lifecycle, by team, NOT by resource type)
- State migration procedures
Result: ✅ 80% expert knowledge (was 30%)
3. Anti-Patterns Added
1. Leaky Abstractions - 50 variables, just wrapping resources 2. Premature Modularization - Extract after first usage, wrong abstraction 3. State Migration Nightmare - Destroy/recreate without planning 4. Module Version Hell - 20 consumers on different versions
Result: ✅ Prevents months of refactoring pain
4. Decision Frameworks
1. Module vs Inline - Based on usage count, stability, compliance needs 2. Module Boundaries - By lifecycle (good), by technology (bad) 3. When to Extract - First time inline, second time copy, third time abstract
Result: ✅ Clear criteria for every refactoring decision
Key Features
Critical Decision Tree
1 usage → Keep inline (wait for second)
2-3 usages → Maybe (if stable and identical)
4+ usages → Modularize (if API stable)
Compliance → Modularize immediatelyAnti-Patterns
Leaky Abstraction: 50 variables = not abstracting Premature: Extract after first usage = wrong abstraction State Migration: Plan moves, backup state first Version Hell: Maintain 3 versions or force upgrades
Module Boundaries
✅ Good: By lifecycle, by team ownership ❌ Bad: By Terraform resource type, by technology
Rule of Three
First time: Write inline
Second time: Copy-paste
Third time: Abstract into moduleState Migration Safety
# Always backup first
terraform state pull > backup.tfstate
# Move resources
terraform state mv old_address module.new.address
# Verify no changes
terraform plan # Should show: No changesInstallation
cp -r refactor-module ~/.agents/skills/Resources
- Official Docs: https://developer.hashicorp.com/terraform/language/modules (syntax)
- This Skill: Refactoring decisions, when to modularize, anti-patterns
Error Recovery: Terraform Module Refactoring
When State Migration Causes Destroy/Create Plan
Recovery steps: 1. STOP: Do NOT apply. Run terraform state pull > emergency-backup.tfstate immediately 2. Diagnose: Run terraform state list to see current addresses vs expected module addresses 3. Fix state: Run terraform state mv for each resource that moved into module 4. Fallback: If already applied and destroyed, terraform state push emergency-backup.tfstate then terraform import module.network.aws_vpc.main vpc-xxxxx
Why this is deceptively hard to debug: NO ERROR MESSAGE. terraform apply shows plan to destroy VPC, create "new" VPC with identical config. Looks like Terraform bug or state corruption. Developers approve the plan thinking it's safe, then production VPC gets destroyed. The fix (state mv) is 2 minutes, but discovering that's the problem takes 20–30 minutes—and by then you may have already caused an outage.
---
When Module Has Too Many Variables (Leaky Abstraction)
Recovery steps: 1. Audit usage: Survey all consumers—which variables do they actually use? (Often 20% of variables) 2. Identify patterns: Group consumers by usage pattern (data buckets, log buckets, artifact buckets) 3. Redesign interface: Replace 50 variables with bucket_type enum + sensible defaults per type 4. Fallback: If redesign too risky, create v2 module with clean interface, deprecate v1 over 6 months
Why this is deceptively hard to debug: Module works perfectly—tests pass, apply succeeds. The problem emerges slowly over months: every resource argument becomes a module variable, consumers still need to know AWS internals to use the module (defeating abstraction purpose). Takes 3–6 months of maintenance hell before team realizes. By then, you have 10+ consumers and unwinding is more painful than living with it.
---
When Premature Module Needs Different Features Per Consumer
Recovery steps: 1. Count feature flags: If >10 boolean toggles, module is wrong abstraction 2. Split by usage: Create separate modules per use case (simple-bucket, versioned-bucket, replicated-bucket) 3. Migrate incrementally: New consumers use new modules, old consumers stay on v1 (deprecate over time) 4. Fallback: If splitting too complex, add advanced_config escape hatch allowing raw HCL passthrough for edge cases
Why this is deceptively hard to debug: Each variable addition seems reasonable in isolation. After 6 months, module has 30 variables, 20 boolean flags, complex conditional logic. Nobody uses all features. Every change risks breaking someone. The wrong abstraction was baked in before patterns emerged—reversing it requires coordinating 10+ teams, a 2-month project nobody has time for.
---
When Multiple Module Versions Cause Maintenance Hell
Recovery steps: 1. Audit versions: grep -r 'source.*?ref=' . | sort | uniq -c 2. Create migration path: Write automated migration script (sed/awk) to update HCL from v1 → v2 3. Coordinate upgrades: Schedule "module upgrade week" where all teams migrate together 4. Fallback: If coordination impossible, use monorepo with workspace protocol (source = "../../modules/vpc") to eliminate versions—all consumers use same code, breaking changes impossible
Why this is deceptively hard to debug: Version divergence happens slowly over weeks/months. After 3–6 months you have 5 major versions to support, or you force painful migrations that break production apps.
Related skills
FAQ
When should I create a Terraform module?
Wait for the third real instance; a module is worth it at 4+ identical usages with a stable API or a compliance requirement, never on first usage.
Why can modularizing cause an outage?
Moving inline resources into a module changes state addresses, which Terraform reads as destroy old and create new with no warning; run terraform state mv after a backup first.