Hello and welcome to this edition of Ctrl+Alt+Deploy! I’m Lauro Müller, and I’m super happy to have you here with me 😄 Let’s spend the next few minutes sharpening our skills and learning something new and relevant for running production systems 💪 Today we're going to work through six ways of organizing Terraform projects and splitting state across teams and environments, and what each one gives you in return.
Most Terraform projects begin as one directory holding one state file, and while this may work for a long time, sooner or later we start seeing some nasty symptoms as projects grow: a plan that took twenty seconds takes six minutes; two engineers start messaging each other about who is applying; someone reviewing IAM notices that everyone who can deploy the application can also write to the VPC.
Each of those is a reason to want a second state file, and a first, naive attempt might look like this:

We soon learn that Terraform rejects it, since a backend block cannot refer to variables, locals or data source attributes, and a configuration can only contain one backend block. Terraform loads the backend before it evaluates the rest of the configuration, so at the moment the block is read there is nothing available to interpolate. One configuration gets one state file, and a second state file requires either a second configuration or some way of filling the backend in from outside the code. The six patterns discussed in this edition are, in their essence, variations on those two options.
Each of the symptoms we listed in the first paragraph points at a different property of a state file, which is why they push teams toward different patterns and solutions:
The engineers waiting on each other are hitting the lock. Terraform locks state for any operation that could write it, and the lock belongs to the state object rather than to the configuration, appearing on the S3 backend as a
.tflockobject beside the state itself. Two state files mean two locks, and two people can runterraform applyat the same time.The slow plan is about the dependency graph. Terraform builds that graph from the configuration in front of it together with the state attached to it. It reads no other state file while planning. Splitting state makes plans faster, since there is less to refresh and a smaller graph to construct. On the flipside, it can no longer sequence work around all resources or notice that a change here has broken something somewhere else.
The IAM review is about access. State is stored as an object and IAM is written against object paths, so a state file is the smallest thing you can grant or refuse someone. A policy can be scoped to a single state object and its lock file, which is how production state gets restricted to the people trusted to change it.
These three do not come apart. Splitting state for any one of them splits it for the other two as well, so the pattern to adopt is often the simplest that solves the problem you have.
AI can build faster. Can your team decide better?
AI can draft the PRD and prototype the idea. Jira Product Discovery helps teams decide whether it belongs on the roadmap. Bring feedback and ideas together, prioritize as a team, and keep your roadmap connected to delivery in Jira.
Pattern 1: A workspace per environment
Workspaces keep a single configuration directory and let Terraform choose the state file when it runs.

Each workspace writes to a state object of its own. The default workspace uses the key from the backend block, and every other workspace is stored under <workspace_key_prefix>/<workspace_name>/<key>, where the prefix defaults to env:. A workspace is, mechanically, a renamed state file.

