← All articles
Published Sep 10, 20253 min read

Terraform Directory Patterns: Organize by Application, Not Resource Type

Why stuffing all DNS or networking into one folder slows teams down—and how to restructure around the applications that use them.

Gabriel Levasseur

Gabriel Levasseur

Founder

Terraform Directory Patterns: Organize by Application, Not Resource Type

Terraform's "just a folder of .tf files" flexibility tempts teams to create directories like dns/, networking/, or sensitive/ so every similar resource lives together. It feels tidy—until the folders balloon, plans slow to a crawl, and engineers edit two distant places just to ship a feature. The sustainable alternative is to group resources by the application or product surface they serve so every related change lives in one place.

The resource-bucket anti-pattern

  • Huge folders, tiny intent: A dns/ directory owning every record buries critical entries for production APIs next to staging experiments. Reviewers have to scan massive diffs to understand a simple change.
  • Split-brain workflows: Shipping a new endpoint means updating the app stack and the central DNS folder. Every deploy is a scavenger hunt across the repo.
  • Slow, risky plans: Terraform must evaluate hundreds of unrelated resources because they're co-located. What should be a quick plan for orders-service now touches unrelated records.
  • Hidden ownership debt: When everything "networking" sits under one team, product squads stop feeling responsible for the infrastructure their features need.
Smell check

If shipping a feature requires touching two directories "because that's where DNS lives," you've organized around technology, not the customer experience.

Organize directories around applications

Group Terraform code by the slice of the platform a team ships: an application, a product surface, or a lifecycle boundary. Each directory owns the infrastructure that feature needs—compute, storage, DNS, queues, certificates, observability.

infra/
  orders-service/
    main.tf
    dns.tf
    autoscaling.tf
    outputs.tf
  accounts-service/
    main.tf
    dns.tf
  shared/
    vpc/
    logging/

With this shape:

  • Engineers change one directory per feature.
  • Plans stay scoped to the infrastructure the feature owns.
  • Reviews align with customer impact, not provider taxonomy.
  • Ownership is obvious: the team behind orders-service maintains its full stack.

Keep shared building blocks deliberate

You still need shared primitives—VPCs, base networking, central logging. Isolate them in clear shared/ or platform/ directories that rarely change. Application directories depend on them through outputs, not by editing shared code in place.

Migrating away from resource buckets

  1. Inventory consumers: List which apps rely on the central dns/ or networking/ folders.
  2. Carve out vertical slices: For each app, move the records, load balancer listeners, and IAM policies into a new app-name/ directory, wiring outputs back to shared networking as needed.
  3. Introduce app-level pipelines: Run plans and applies per app directory so teams deploy autonomously.
  4. Leave a rump module: Keep a thin dns/ wrapper that exposes shared zones or top-level records, but forbid app-specific changes from landing there.

Guardrails that keep the pattern healthy

  • Require a README.md in every app directory describing context, owners, and dependencies.
  • Lint for cross-app references that bypass outputs.
  • Visualize relationships in Cora to ensure apps consume only the shared primitives they truly need.

By structuring Terraform around the applications customers touch, you shorten review cycles, reduce coordination overhead, and keep plans focused. When every feature can ship from a single directory, your platform scales with your product instead of fighting it.

Model your Terraform graph in Cora

Ready to see Cora in action? Jump into the product experience tailored to this article.

Model your Terraform graph in Cora

Keep reading

View all