Why We Chose Terraform Over Pulumi, And Might Switch Back
Like many engineering teams, we continually reevaluate our infrastructure stack, not to keep up with trends, but because our work involves multiple clients, cloud providers, and deployment styles.
Last year, we made the call to standardize on Terraform across most of our infrastructure-as-code (IaC) projects. It was familiar, stable, and widely supported across AWS, GCP, and Azure.
But before that, we had been exploring Pulumi, especially in projects that required tight coupling between application logic and infrastructure provisioning. Pulumi’s appeal was clear: use real programming languages (like TypeScript or Python) to write infra code, rather than learning HashiCorp Configuration Language (HCL).
Six months later, we’re still on Terraform. But recently, we’ve started wondering if we made the right long-term choice.
The Problem
Infrastructure configuration was becoming a tax on velocity.
We were seeing friction in two common areas:
Code drift between app and infra repos
Application teams would request new resources (like an S3 bucket or a new queue), but the provisioning would happen in a different repo, written in a different language, and owned by a different team. This introduced delays, review friction, and context loss.Onboarding was inconsistent
New developers understood TypeScript. Very few were fluent in HCL. Even simple things like conditional logic or loops in Terraform felt unintuitive to people with software engineering backgrounds.
At the same time, Terraform offered something Pulumi didn’t: battle-tested community modules, consistent state management, and familiarity with DevOps hiring pools.
So we had a tension: developer experience vs. infrastructure maturity.
The Task
Our goal was to define a standard infrastructure approach that:
Worked across all major cloud platforms
Reduced onboarding friction
Enabled better collaboration between app and infra teams
Didn’t slow us down when projects scaled
We weren’t looking for “the best tool.” We were looking for the least painful default that could work across most engagements.
The Actions We Took
1. Standardized on Terraform, but created a wrapper
We chose Terraform because of its maturity, wide cloud support, and the fact that most DevOps professionals already knew it.
But to address onboarding pain, we created internal wrappers and templates:
Prebuilt Terraform modules for common use cases (S3, CloudFront, VPCs)
Helper scripts to simplify init and apply
Annotated examples and README-first repos
This helped developers avoid writing raw HCL and gave them a clear starting point.
2. Introduced Pulumi for hybrid use cases
For teams building serverless apps, internal tools, or systems tightly integrated with application logic, we allowed Pulumi as an opt-in alternative.
This gave teams the ability to provision infra and deploy app code from the same repo, using the same language. It also meant fewer context switches and more confidence during deploys.
We used Pulumi with TypeScript, tied into GitHub Actions, and managed state through the Pulumi Cloud backend.
3. Defined usage boundaries
To avoid tool sprawl, we created a decision tree:
If the infra is reusable and broadly applicable, use Terraform
If the infra is tightly bound to app code and lives in the same repo, use Pulumi
Avoid mixing the two within the same project unless absolutely necessary
This gave us flexibility without chaos.
The Results
Terraform remained the default for infrastructure teams
Pulumi adoption grew in app-heavy projects where infra was tightly scoped
Developer onboarding improved, especially for full-stack engineers
We reduced PR delays, since app and infra logic often lived side by side in Pulumi-based repos
But we also saw state management confusion in Pulumi at scale, especially when teams didn’t fully understand how resources were tracked
We didn’t pick a winner. We picked tools that made sense depending on the shape of the work.
What’s Next
We’re not dogmatic. In fact, that’s kind of the point.
Right now, Terraform works well for most of our needs. But as our product and engineering teams get closer, and as application teams take more ownership of deployment, we’re seeing more situations where Pulumi might be the better fit.
We’ve started experimenting with Pulumi templates in our internal CLI. We’re testing how to abstract provisioning for app developers without locking them into rigid Terraform pipelines.
We might not “switch back.” But we might shift.
Takeaway
Choosing an IaC tool isn’t just about syntax or cloud support. It’s about who’s writing the code, how often it changes, and how closely it ties to product delivery.
If your infrastructure is mostly reusable and shared across projects, Terraform is probably the right choice.
If your teams want more control, faster iteration, and app-level ownership, Pulumi deserves a look.
Just don’t let the tool drive the architecture. Let the team and context decide.