skills/refactor-module/SKILL.md
Use when deciding whether to extract Terraform code into a reusable module, determining module boundaries, or migrating state after modularization. Covers the refactoring decision (modularize vs inline), anti-patterns causing module sprawl, and state migration risk. Keywords: terraform module, refactor terraform, module boundaries, terraform abstraction, module sprawl, state migration, terraform state mv.
npx skillsauth add acedergren/agentic-tools refactor-moduleInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
4 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Assumption: You know Terraform syntax. This covers when to modularize vs keep inline.
terraform state mv first — Terraform will plan to destroy and recreate every resource.terraform state pull > backup.tfstate before any state migration.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 consumers
Break-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.
| 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 |
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.
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 changes
Load references/error-recovery.md if already applied and resources were destroyed.
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.
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.
grep -r "resource \"aws_s3_bucket\"" . — confirm 3+ usages before touching anythingdiff app1/s3.tf app2/s3.tf — confirm >80% identical, not superficially similarbucket_type = "data"|"logs"|"artifacts"), not resource argsterraform state pull > backup.tfstate — always before state movesterraform state mv <old-address> <new-address> — one resource at a timeterraform plan — must show "No changes" before proceedingLoad references/error-recovery.md when:
Do NOT load for:
development
--- name: api-audit description: "Use when auditing API routes for schema drift, missing auth, or validation gaps. Scans routes against shared TypeScript types to find mismatches, missing middleware, and undocumented endpoints. Read-only — produces a severity-grouped report. Keywords: audit routes, schema drift, auth gaps, missing validation, type mismatch, orphaned schemas. Triggers on "audit API routes" or "find schema drift"." --- # API Route & Type Audit Skill ## When to Use Load this skil
development
Use when drafting, translating, polishing, or reviewing Swedish text so it sounds natural, fluent, contemporary, and appropriate for its audience. Triggers include "write better Swedish", "make this sound natural in Swedish", "translate into Swedish", "polish this Swedish", "tech company Swedish", "contemporary Swedish words", "Swedish developer docs", and "avoid Anglicisms".
development
Use when working with shadcn-svelte components, TanStack Table in Svelte 5, or Tailwind v4.1. Covers non-obvious reactivity bugs, library selection trade-offs, and migration pitfalls not in the official docs. Keywords: shadcn-svelte, TanStack Table, Tailwind v4.1, Svelte 5 runes, bits-ui, superforms, data table, svelte-check.
data-ai
Use when mapping IDCS claims to org membership after OAuth login succeeds. Covers mapProfileToUser, session.create.before, session.create.after hooks, MERGE INTO upserts, tenant-org mapping, and first-admin bootstrap. Keywords: IDCS groups, org_members, provisioning, session hooks, tenant map, MERGE INTO.