If my IaC setup slows down as teams and cloud resources grow, the fix is usually simple: split state, cut shared dependencies, lock down change flow, and standardise modules and checks.
From the article, I’d boil it down to this:
- Large state files slow delivery: once states pass roughly 500–1,000 resources or 10–15 MB,
planandapplyruns can stretch into 10+ minutes. - Shared resources cause collisions: DNS, IAM, networking, and other shared parts often become bottlenecks when several teams deploy at once.
- Version drift and weak controls add risk: mixed tool versions, uneven reviews, and missing policy checks can lead to failed releases and audit issues.
- The main fixes are clear: split state by environment, service, and domain; keep modules small; queue runs per workspace; and add policy checks before deployment.
- Tool choice matters: CloudFormation and AWS CDK suit AWS-only estates, while Terraform and Pulumi fit multi-cloud or hybrid setups.
- For Kubernetes at scale, Argo CD suits central oversight, while Flux CD suits cluster-level control.
The Next Generation of Cloud IaC: Terraform Stacks for Global Infrastructure
Need help optimizing your cloud costs?
Get expert advice on how to reduce your cloud expenses without sacrificing performance.
Quick comparison
| Area | Main problem | What I’d do |
|---|---|---|
| State | Slow plans, slow applies, lock contention | Split state into smaller units and use remote locked state |
| Shared resources | Teams changing the same resources at the same time | Isolate networking, IAM, and other shared parts |
| Modules | Large, mixed-purpose modules | Keep modules small and focused by domain |
| Governance | Drift, mixed versions, weak audit trail | Standardise versions, reviews, and policy-as-code checks |
| Pipelines | Long waits and failed concurrent runs | Queue per workspace and run only independent jobs in parallel |
| Tooling | Poor fit for estate shape | Match the tool to AWS-only, multi-cloud, or Kubernetes-heavy estates |
I see the main point as this: IaC scale is not just about more code. It is about setting firm boundaries so teams can change infrastructure without slowing each other down or adding risk.
Common scalability problems in IaC deployments
IaC estates often begin with one team and a handful of environments, then balloon before anyone quite notices. At that point, issues that barely registered at small scale start eating time, money, and attention. The three failure patterns below show up most often.
Large state files and slow plan or apply runs
Performance often starts to slip once state files grow past about 10–15 MB or 500–1,000 resources. When you get beyond 1,000 resources, plans and applies can drag on for tens of minutes. Terraform slows down hard as resource counts climb, because each run has to load the full state graph, refresh every resource, and make API calls to check the current state.
In CI/CD pipelines, that turns into stalled jobs, slower releases, and higher runner costs. Teams then start batching changes just to avoid the wait. That sounds harmless, but it weakens feedback loops and makes releases more risky.
The fix is simple in principle: cut down how much each run has to read, refresh, and change.
Tightly coupled code, shared resources, and unsafe parallel changes
Shared resources feel handy early on - a Route 53 DNS zone, a set of IAM policies, network security rules. Later, they become a headache when several teams deploy at the same time. One pipeline might update a DNS record while another refreshes the same zone, which can lead to state drift or a failed apply. IAM updates from one module can also strip permissions that another team’s workload still needs.
The root issue is shared dependencies across environments. If dev, test, and production all point at the same shared VPC or IAM role, a change meant for one environment can spill into the others. It gets worse when modules bundle too many concerns together, such as networking, security, and application provisioning. Without a clear boundary, isolating a change becomes much harder.
The result is predictable:
- More failed pipelines
- More manual coordination between teams
- A higher chance of a production incident caused by two unrelated changes colliding [6][8][11]
The answer is to set cleaner boundaries before teams begin changing the same resources in parallel.
Governance gaps, version drift, and skills bottlenecks
Once estates are split across teams, drift stops being just a technical issue and becomes a governance issue too. As IaC spreads from a central platform team to multiple application squads, governance often falls behind. Teams end up running different versions of Terraform, providers, or shared modules. One team moves to a new major version with breaking changes while another stays put, and failures then appear in only some environments. CloudFormation and CDK estates run into the same kind of trouble when templates depend on outdated resource types [7][12].
In regulated settings, version drift and patchy policy enforcement can make it all the way to production before audit gaps are found. Missing policy-as-code checks, uneven peer review, and loose access to state backends mean non-compliant resources can reach production without a clear audit trail. At the same time, a small group of senior engineers ends up carrying most of the reviews and fixes. That slows delivery, makes onboarding harder, and piles risk onto too few people [7][9].
At scale, these issues call for stronger boundaries, standardisation, and tighter control.
Practical solutions for scaling IaC safely
These problems have targeted fixes. Start with state boundaries, then modules, then controls. Boundaries come first because they shrink blast radius and make every other safeguard easier to apply.
Split state and workloads into clear boundaries
The single biggest change for teams dealing with slow plans or colliding pipelines is to split state into smaller, focused files. [5][16] A sensible way to do that is to partition across three axes: environment (dev, test, pre-prod, production), business service, and infrastructure domain.
Each one solves a different problem. Environment splits stop noisy development work from slowing production deployments and make access control simpler. Service splits limit blast radius to one product if something breaks. Domain splits cut contention on shared resources like VPCs or IAM policies and help contain high-risk changes. Put simply, fewer things live together, so fewer things can go wrong together.
Use locked, versioned remote state with rollback support so concurrent changes can't corrupt state. [17][18][3] Restrict write access to production state to authorised CI/CD identities and named platform engineers. Give developers read-only access for diagnostics.
Design reusable modules and tune pipelines for performance
Modules start to hurt when they try to do too much or depend on resources they never declare outright. Keep modules organised by infrastructure domain, and keep shared platform modules small. Pass IDs and names explicitly so dependencies stay visible. [1][16][3] That makes ownership clearer and cuts the review load on senior engineers, while also reducing version drift.
On the pipeline side, parallelise applies across independent workspaces such as different environments or unrelated components. But serialise changes to shared infrastructure like networking, shared data stores, or central identity systems. Set concurrency limits that make sense, so infrastructure pipelines don't crowd out application builds or monitoring jobs.
Reserve terraform -target for emergency fixes. It can skip dependencies and leave state inconsistent. [1][14]
Add guardrails for concurrency, compliance, and change control
State locking deals with the technical side of concurrency, but teams also need CI/CD queuing. Queue one run per workspace and alert on long-held locks. For high-risk changes, such as production networking updates, use a planned change window across teams to cut the odds of two unrelated pipelines crashing into each other. [13][17]
Policy-as-code tools let teams bake compliance rules straight into pipelines. For UK organisations, the most useful controls to embed are:
- mandatory tagging:
cost_centre,owner,environment,data_classification - encryption at rest and in transit for all data stores
- data residency checks that block deployments to unapproved regions
Pair those checks with static analysis before plans are generated, so teams catch misconfigurations early instead of finding them at apply time. [1][17][3]
GitOps reconciliation adds continuous drift detection on top of pipeline-led deployments. Instead of applying changes only at release time, a reconciler keeps comparing actual state with committed configuration and corrects drift caused by manual changes or failed applies. Argo CD fits central multi-cluster control. Flux fits cluster-level autonomy. Either way, all changes go through pull requests, which creates a clean audit trail for internal governance and regulatory inspection. [10][15][19] That stops manual changes from turning into hidden compliance drift.
With state, modules, and controls in place, the operating model becomes the next decision.
Choosing the right operating model for scale
::: @figure
{IaC Tools at Scale: Terraform vs Pulumi vs CloudFormation vs AWS CDK}
:::
With state and guardrails set, the next call is the operating model that carries them across the estate. This still matters a lot. Some platforms make scale simpler to run. Others add friction. Pick the wrong one and scale starts to mean delays, drift, and extra platform work.
Terraform, CloudFormation, Pulumi, and AWS CDK in larger estates

The biggest difference at scale is how state is handled. CloudFormation and AWS CDK hand state off to AWS, so there are no state files to store, lock, or back up. That cuts platform overhead for AWS-only estates. Terraform and Pulumi rely on external backends or managed state services. That brings extra work around storage, locking, and security, but it also gives you stronger support for multi-cloud and hybrid setups.
Abstraction style matters as well. Terraform’s declarative HCL is fairly easy to get into, but reuse at scale depends on careful module design. Pulumi uses general-purpose languages such as TypeScript, Python, and C#, which can cut duplication across large estates. The trade-off is that teams need tighter code review and testing from day one. AWS CDK gives developers richer constructs and then compiles them down to CloudFormation, so you still get AWS-managed state and rollback.
| Criteria | Terraform | Pulumi | CloudFormation | AWS CDK |
|---|---|---|---|---|
| State model | Self-managed or remote backend | Managed state by default, or self-managed backend | Fully managed by AWS | Delegates state to CloudFormation |
| Multi-cloud support | Strong | Strong | AWS only | AWS only |
| Reusability model | HCL modules | General-purpose language components | Nested stacks and registry extensions | Construct libraries |
| Scale limits | Large state files can slow plans and applies | No CloudFormation-style stack limit, but state and updates still need discipline | AWS service and template limits become important at very large scale | Inherits CloudFormation limits |
For AWS-only estates, CloudFormation or AWS CDK keeps state and rollback inside AWS. For multi-cloud or hybrid estates, Terraform is still the most mature choice, with broad provider coverage and a large community of shared modules. Pulumi fits teams already working in TypeScript or Python that want to build rich internal platforms, but only if code quality controls are in place from the start.
Where Kubernetes is the main platform, GitOps often becomes the control plane for change.
When GitOps improves control across many clusters and teams
In large Kubernetes estates, GitOps often stops being just a deployment method and becomes the operating model itself. Argo CD and Flux CD both support that model, but they fit different ways of working. Argo CD offers a central control plane with a web UI, which makes it easier for multiple product teams to check sync status, approve sensitive changes, and see differences before anything is applied. Flux takes a Git-led, CLI-based route. It installs lightweight controllers straight into each cluster and has no central UI by default. That means less platform overhead, but also less visual insight out of the box.
Use Argo CD for central visibility and approvals. Use Flux CD for lightweight, cluster-local reconciliation.
AWS prescriptive guidance notes that both tools handle most GitOps scenarios well, and recommends choosing based on your security model, UX needs, and operating model rather than treating one as universally superior. [20] For 100+ clusters, design for multi-cluster scale from the start. [2][20][4]
Conclusion: A practical roadmap for scaling IaC
IaC tends to hit a wall for four main reasons: state sprawl, slow plan and apply runs, tight coupling, and weak governance. These issues don’t just appear out of nowhere. They usually come from design choices, which means they can be fixed.
A practical rollout follows four steps: audit, isolate, standardise, and measure. Start with an estate audit to spot the largest state files, the slowest plan and apply runs, and the stacks that are most tightly coupled. Then split state across clear boundaries, such as environment, service, or lifecycle, and turn on remote state locking.[1][3]
From there, standardise reusable modules and guardrails so teams can use approved patterns instead of building bespoke infrastructure every time. After that, track deployment time, change failure rate, mean time to recover (MTTR), and monthly cloud spend in £. Those metrics help both engineering and finance see what’s changing, and whether speed and stability are moving in the right direction together.
The main shift is simple: treat IaC scalability as an operating discipline, not a one-off migration.
For UK organisations that want support with this work, Hokstad Consulting can help with the redesign.
FAQs
When should I split my IaC state?
Split your Infrastructure as Code (IaC) state once the root configuration starts to feel messy, especially when it grows past 100 resources. In most cases, a few dozen resources per state file is a good target.
It also helps to group resources by blast radius and how often they change. For example, keep stable network settings separate from application parts that get updated more often.
That makes the setup easier to manage, cuts down the impact of mistakes, and makes troubleshooting simpler.
How do I reduce deployment collisions between teams?
Use Git as the single source of truth, with a clear branching approach like GitFlow or trunk-based development. That gives everyone one place to track changes and makes day-to-day work far less messy.
It also helps to require pull requests and peer reviews before anything gets merged. A second set of eyes can catch issues early, keep standards consistent, and make changes safer.
On the infrastructure side, break things into smaller, modular parts. That way, teams can work more independently, and one change is less likely to ripple across everything else.
Hokstad Consulting can help tune DevOps workflows so teams work better together and ship updates faster.
Which IaC operating model suits my estate?
The right IaC operating model comes down to your cloud setup, your team’s day-to-day workflow and how much control you need. If you run a hybrid estate or use more than one cloud provider, Terraform or OpenTofu can help you keep things consistent across the board. If your team prefers to work in standard programming languages, Pulumi is often a better fit. And if you’re all-in on one provider, AWS CloudFormation gives you tighter integration.
Whatever tool you choose, keep the setup modular. Pair that with version control and automated policy enforcement to cut complexity, support compliance and keep costs in check as your estate grows.