skip to content

Pulumi lets you define infrastructure in TypeScript, Python, Go or C#, while Terraform uses HCL. What do you actually gain, and what do you give up, by choosing a general-purpose language?

level: middleimportance: must knowfreq 75%

answer

  1. a trade, not a winner
  2. code gives loops, types, tests
  3. documents give readable diffs
  4. program hides what it creates
  5. same providers under both

basics

~20 s

A general-purpose language buys real control flow, static types, IDE completion and unit tests over infrastructure logic. It costs reviewability: a pull request now shows the program that produces resources rather than the resources themselves.

solid answer

~50 s

With Pulumi you get the language's own loops, conditionals, functions and types, so patterns that need `count`, `for_each` or dynamic blocks in HCL are just code — and you can unit-test that code with the framework your application already uses, with provider calls mocked. The cost is reviewability and blast radius: an HCL diff shows roughly what will change, whereas a TypeScript diff shows logic, so the reviewer has to run a preview to know what it does. A program can also be non-deterministic — read an API, use a timestamp — in a way HCL structurally resists, and it raises the bar for whoever maintains it next. Underneath, both tools use the same providers and the same preview-then-apply loop, so neither choice removes state, drift or a bad apply. My rule: catalogue-shaped estates read by a broad ops team stay in HCL; genuinely logic-heavy infrastructure owned by application engineers is where Pulumi pays.

go deeper

for a junior

Know that both tools declare the same cloud resources and both show you a preview before changing anything; the difference is that Pulumi code is a program in a language you already know and Terraform code is HCL.

for a middle

Be ready to name the concrete gains — native loops and conditionals, static types and IDE support, unit tests with mocked providers — and the concrete cost, which is that a code diff no longer shows the reviewer which resources change.

for a senior

Show the production judgment: how you keep previews in front of reviewers, why non-determinism in a program is a real operational hazard, and why neither tool removes state, drift or blast radius.

for a principal

Own the criterion and the org consequence. Argue from team fluency, review culture, existing module investment and migration risk, and be willing to say a standard matters more than the marginally better tool.

## The two shapes Terraform configuration is a declarative document: you write HCL blocks, the tool builds a graph from them, diffs that desired state against what it has recorded, and calls providers. Pulumi is a program: your `main` runs, each resource constructor you call registers a resource with the Pulumi engine, and the engine then does the same diff-and-call. The end state is identical — a graph of provider-managed resources — but one is produced by parsing a document and the other by executing code. That single difference is the whole interview question. Everything below follows from it. ## What you actually gain **Control flow that is just control flow.** A `for` loop, an `if`, a `map`, a helper function. In HCL the same intent goes through `count`, `for_each`, `for` expressions and `dynamic` blocks, which are capable but are a second language you learn on top of the first. Deeply conditional shapes — this subnet layout for regions with three AZs, that one otherwise — read naturally as code. **Static types and tooling.** Resource arguments are typed, so a misspelled property is a red squiggle in the editor rather than an error surfaced when you run a plan. Go-to-definition lands in the provider SDK. Rename and find-usages work. On a large estate this is a real productivity difference, not a cosmetic one. **Abstraction with normal packaging.** Reusable pieces are classes and packages published to npm, PyPI or a Go module proxy, versioned by the same tooling your applications use, with interfaces expressing the contract. You can share a type between the infrastructure and the service that consumes it. **Genuine unit tests.** Because it is a program, you can run it in-process with the providers mocked and assert on the properties the program would submit — in Jest, pytest or `go test`, in milliseconds, with no cloud account. That is a different level from lint-and-validate. ```typescript import * as aws from "@pulumi/aws"; for (const env of ["dev", "staging", "prod"]) { const bucket = new aws.s3.BucketV2(`logs-${env}`, { tags: { Environment: env } }); if (env === "prod") { new aws.s3.BucketVersioningV2(`logs-${env}-versioning`, { bucket: bucket.id, versioningConfiguration: { status: "Enabled" }, }); } } ``` ## What you give up **Reviewability.** This is the honest cost. A change to an HCL file usually tells the reviewer what infrastructure changes. A change to a program tells the reviewer what the *logic* is now; deriving the infrastructure requires running a preview and reading its output. Teams that adopt Pulumi seriously post the preview into the pull request for exactly this reason. **Blast radius of cleverness.** A helper that loops over a list fetched at runtime can create or destroy resources based on data that appears nowhere in the diff. Programs can be non-deterministic — a timestamp, a random value, a network call — which a declarative document mostly cannot be. The discipline that HCL imposes by having no escape hatch, you must impose on yourself. **Onboarding and audience.** HCL is deliberately small; a network engineer or an SRE who does not write TypeScript can still read and review it. Pulumi asks the reader to know the cloud *and* the language *and* the codebase's own abstractions. **Ecosystem gravity.** Examples, blog answers, the public module registry and most static scanners target HCL and template files first, because scanning an arbitrary program is a much harder problem than scanning a document. Pulumi has its own policy tooling, but the third-party surface is thinner. ## What does not change Both tools drive the same provider ecosystem, both record what they manage, both preview before they mutate, both suffer drift when someone edits the console, and both can destroy a database if the diff says replace and nobody read it. Choosing Pulumi does not delete any of those problems; it changes the language you express the solution in. ## Taking a position Interviewers are testing whether you have one, so give it with a criterion attached. Mine: ask whether the infrastructure is *logic* or a *catalogue*. A broad, shallow estate — many similar accounts and services, reviewed by a large mixed-skill group, already sitting on a library of modules — is better in HCL, and the migration cost buys nothing. Infrastructure with real logic in it (per-tenant stamping, contracts shared with application code, heavy conditionality) owned by application engineers with a testing culture is where a general-purpose language earns its keep. Team fluency dominates: the tool your reviewers can read at 3am is the tool that keeps you safe. And whichever you pick, the guardrails are the same — preview on the pull request, policy checks before apply, and a least-privilege role doing the applying.

  • How would you keep pull-request review honest on a Pulumi codebase?
    Run `pulumi preview` in CI on every pull request and post the resource diff back as a comment, so reviewers see the effect and not only the logic. Beyond that, ban non-determinism in the program — no clock, no random, no live API lookups outside declared providers — and put policy checks on the preview so a surprising create or replace fails the build rather than relying on a human noticing.
  • Does using a typed language remove the need to read the preview before applying?
    No. Types catch shape errors — a misspelled or wrongly-typed argument — before you run anything. They say nothing about intent or consequence: a perfectly typed program can still replace a database, drop a subnet, or create three hundred resources because an input list changed. The preview is the only place the actual effect on live infrastructure appears, and it stays mandatory.
  • Where would you not migrate an existing Terraform estate to Pulumi even if you preferred the language?
    When the estate is large, stable and mostly declarative, when the team's reviewers are ops-leaning, or when you depend heavily on community modules and HCL-oriented scanners. Migration costs a full re-import of live resources with real risk, and buys little on infrastructure that has no logic in it. The stronger move is Pulumi for genuinely new, logic-heavy systems while the existing estate stays where it is.

HCL is a filled-in form: constrained, boring, anyone can check it. A Pulumi program is a form-filling machine: far more capable, and you have to run it to know what form comes out.

saying these in an interview costs you the question

  • Says Pulumi is imperative and therefore not real infrastructure as code
  • Claims HCL cannot loop or express conditionals at all
  • Assumes Pulumi supports fewer cloud resources because it is newer
  • Argues static types remove the need to review a preview
  • Presents it as a clear winner instead of a trade with criteria

context