Skip to content

How chant compares

chant is a synthesis compiler: TypeScript in, spec-native output out. You declare infrastructure as typed TypeScript objects, chant validates them against a lexicon’s semantic rules, and emits the target format — CloudFormation, GitLab CI, or whatever the lexicon targets.

chant doesn’t manage deployment or drift. chant’s core scope is synthesis: parsing, type-checking, linting, and emitting artifacts. The architecture is designed for opt-in operational extensions — observational lifecycle, structured agent workflows — that add context without touching the build pipeline.

This isn’t a reaction to other tools’ failures — it’s an approach that was always available. Nobody built it because the industry was racing to own the full lifecycle. chant does one thing and stops.

For the model behind the rows below — the three axes of state and where each tool sits — see Lifecycle Models.

The agentic angle: the operational layer — deploying, monitoring, remediating — is increasingly handled by agents and automation. chant handles the part that should be deterministic and auditable: turning typed declarations into spec-native output. The two layers are complementary.

Every infrastructure tool sells you a lifecycle. Synthesis, state, apply, drift, retire — owned end to end. A decade of investment goes into that lifecycle, into a better way to own it. The ownership is the product. That coupling is why a pure synthesis tool never existed. You could not get the compiler without the lifecycle welded to it.

chant sells nothing. Synthesis is pure and the output is native spec — standard CloudFormation, GitLab CI, whatever the lexicon targets. Walk away and there is nothing to unwind. That is the test the claim has to pass, and it is the one most tools fail. You can leave a thing you were never sold.

The lifecycle is still there when you want it. You write your own. Ops are declared as code in *.op.ts files and run on a local executor for one-shot work, or on Temporal when an apply needs to outlive a single process — approval gates, rollback, crash-resume. The durable version of what the others sell, for the lifecycle you chose rather than one chosen for you.

Decoupled first. Durable when you ask.

A pure synthesis core also owns less machinery. Because the build path makes no API calls, holds no state, and emits native spec, it needs little of the apparatus a full lifecycle pulls in at plan time — no provider SDKs, no state backend, no credentials, no network. What runs to turn source into output is a type-checker, an evaluator over a static subset of TypeScript, and the lexicon packages for the formats you target. That is the whole of it.

A smaller build-time surface is a smaller thing to audit, and a smaller blast radius if something upstream is compromised: fewer packages execute during synthesis, and none of them reach the network or your cloud credentials. This is a property of the architecture, not a security feature — it falls out of synthesis being side-effect-free. The same boundary is described value-by-value in Where Values Come From.

The scope is exact. This is the build path. The apply side is whatever lifecycle you brought — the CloudFormation CLI, a CI pipeline, an agent, Temporal — and it carries its own dependencies and its own surface. chant shrinks the build-time surface; it does not remove the apply-time one. Owning less is a build-time property, and only that.

These tools make different design decisions. Each decision has consequences — some favorable for certain workflows, some not. This isn’t a feature matrix; it’s a comparison of architectural choices.

DecisionTerraformCDKPulumichant
LanguageHCL (custom DSL)TypeScript, Python, Java, Go, C# (executed)TypeScript, Python, Go, C#, Java, YAML (executed)TypeScript (static subset)
Spec fidelityProvider-mappedAbstracted (L2/L3)Provider-mappedSpec-true
OutputState + API callsCloudFormationState + API callsNative spec output
Lifecycle scopeFullFull (via CloudFormation)FullSynthesis + opt-in observability
State modelAuthoritativeAuthoritative (via CloudFormation)AuthoritativeNo authoritative state file; precise change set via live projection + cloud-side ownership
Reconciliationcode → cloudcode → cloudcode → cloudnone / code → cloud / cloud → code (chosen per environment)
DeterminismMostlyNo (code executes)No (code executes)Yes (for lint-conforming source)
Walk-away costHigh (state, HCL)Medium (CloudFormation remains)High (state, SDK)Zero (output is native)
Semantic validationNone built-inNone built-inNone built-inBuilt-in (lexicon rules)
Drift detectionBuilt-in (plan / refresh)Via CloudFormation drift detectionBuilt-in (refresh / preview)Built-in (lifecycle diff --live against any of 5 cloud lexicons + 4 artifact lexicons)

Spec fidelity means whether the tool’s resource model matches the underlying spec 1:1. CDK’s L2 and L3 constructs deliberately abstract away from CloudFormation — that’s a feature for productivity and a cost for transparency. Terraform providers and Pulumi map closely but not identically to provider APIs. chant lexicons are generated directly from the target spec.

Walk-away cost is what happens if you stop using the tool. Terraform and Pulumi leave you with state files and tool-specific code. CDK leaves CloudFormation templates that continue to work. chant leaves native spec output — standard CloudFormation templates, GitLab CI files, whatever the lexicon targets. There’s nothing chant-specific in the output.

The core architectural difference is what happens when you “run” each tool:

Terraform: HCL is parsed, a dependency graph is built, the provider checks current state against desired state, and changes are applied. The state file is the source of truth.

CDK: Your TypeScript executes. Constructors run, methods are called, side effects happen. The result is captured as a CloudFormation template, which is then deployed. The execution can be nondeterministic — same code, different environment, potentially different output.

Pulumi: Your TypeScript executes similarly to CDK, but the output is state + direct API calls rather than a CloudFormation template. Same nondeterminism concern applies.

chant: chant imports your TypeScript and reads the resource objects it exports. Your constructors run. Synthesis does nothing else. No API calls, no state, no deployment. A separate evaluability lint pass (EVL) restricts source to literals, constants, and cross-resource references, so the executed result is equivalent to static data. Same conforming source always produces the same output.

This matters for:

  • Determinism — chant’s output is a pure function of its input. No environment variables, no network calls, no runtime state.
  • Auditability — every value in the output traces to a specific line in the source.
  • Speed — no interpreter, no runtime, no provider calls. Synthesis is fast.
  • IDE support — because the API surface is defined by types (not runtime behavior), autocompletion, inline errors, and go-to-definition work without executing anything.
  • Agent-friendliness — agents can generate chant source files and know exactly what output they’ll produce. No surprises from execution-time behavior.

The most consequential design difference after execution model is the lifecycle model.

Terraform and Pulumi treat state as authoritative — the state file IS the source of truth for what’s deployed. This enables precise plan/apply workflows: the tool knows exactly what exists, what changed, and what to do. The cost is operational burden: state must be locked during operations, stored in a backend with access controls, and protected from corruption. Sensitive data (database passwords, API keys) ends up in state because the tool needs it for diffing.

CDK inherits CloudFormation’s authoritative lifecycle model, which AWS manages as a service. Less operational burden, but the same architectural commitment: state is truth.

chant is designed with an observational lifecycle model — snapshots record what was observed at a point in time, not what must be true now. It does not give up the precise change set, though: chant computes a create/update/delete plan against the live system using cloud-side ownership markers rather than a hosted state file. What it drops is the authoritative file, and with it the costs:

  • No locking — a stale snapshot is inconvenient, not dangerous
  • No sensitive data — scrubbing is a design requirement, not a best-effort guideline
  • No corruption risk — a bad snapshot doesn’t break deployments. Agents verify against live APIs regardless
  • Minimal overhead — state is designed to live in git alongside source code

Trade-offs:

  • Ownership is answered by a live marker on the resource, so the projection must query live APIs; a resource with no marker is foreign and escalates to human review rather than being auto-deleted
  • Git-based snapshots give versioning, portability, and offline access, but no locking, no encryption at rest, and no access control separation from source code

Terraform optimizes for multi-cloud lifecycle management with a unified workflow. One tool, one language, one lifecycle model across AWS, Azure, GCP, and hundreds of providers. The trade-off: HCL is a custom DSL with limited expressiveness, and the state file creates operational overhead.

CDK optimizes for AWS-native development with familiar languages and high-level abstractions. L2/L3 constructs encode AWS best practices and reduce boilerplate. The trade-off: abstractions obscure what’s actually deployed, and code execution means builds can be nondeterministic.

Pulumi optimizes for general-purpose language IaC with state management and multi-cloud support. Real TypeScript/Python/Go with the full language available. The trade-off: code execution brings nondeterminism, and state management has the same operational burden as Terraform.

chant optimizes for typed, linted, deterministic synthesis that’s true to the underlying spec. The output is native to the target platform with zero lock-in. The trade-off: chant doesn’t deploy anything — you need something else for that.

chant’s design decisions have real consequences:

  • chant doesn’t deploy. You need a deployment mechanism: the CloudFormation CLI, a CI pipeline, an agent, or whatever your workflow uses. chant produces the artifact; something else applies it.

  • Static evaluation means no runtime lookups at build time. You can’t call an API during synthesis to get a VPC ID or read a secret. Cross-resource references within a project work (via attribute references). For values that need to be resolved at deploy time, lexicons provide intrinsics. For values that need to be resolved before synthesis, agents or scripts can populate source files.

  • Lexicon ecosystem is young, but covering. Nine lexicons ship today (AWS CloudFormation, Azure ARM, GCP Config Connector, Kubernetes, Helm, Docker, GitHub Actions, GitLab CI, Temporal). The architecture supports any spec-based operational domain — see Lexicons for the current set and Lexicon Authoring for adding new ones.

  • The static subset of TypeScript is a constraint. No function calls, no control flow around resources, no dynamic property access. This is deliberate — it’s what makes synthesis deterministic — but it means some patterns that work in CDK or Pulumi don’t work in chant. See TypeScript as Data for exactly what’s supported.