Coming from Terraform, Pulumi or CDK

Atlantide occupies the same slot as these tools: declare infrastructure, diff it against recorded state, reconcile the difference. What differs is where the determinism guarantee comes from, and how much of the engine is visible to you.

Vocabulary

You know Here it is called Notes
terraform plan / apply atlantide plan / apply plan needs no credentials.
HCL / TypeScript program Atlas-lang config (infra.py) Real Python, executed by a bounded interpreter.
Provider Provider Python packages, discovered by entry point.
Module / ComponentResource / Construct Component Python; config uses them but cannot define them.
terraform init atlantide component add Pins to a commit + content hash.
Backend / remote state State backend SQLite, S3+DynamoDB, or Postgres.
terraform import atlantide import Refuses to import a resource that differs from config.
terraform refresh atlantide refresh Read-only unless --write.
depends_on depends_on Rarely needed; reading a field creates the edge.
lifecycle { prevent_destroy } Lifecycle(prevent_destroy=True) Plus create_before_destroy, ignore_changes, aliases.
moved block Lifecycle(aliases=(...)) Rename without replace.
terraform output atlantide output Sensitive values need --reveal.
Workspace Stack and/or profile Stacks are in-config; profiles are in atlantide.toml.
Sentinel / OPA Policies Native Python, evaluated at plan time.
Terraform Registry No central registry Components are git URLs, pinned by commit.

The substantive differences

A real language, but a bounded one. Pulumi and CDK give you a full programming language and, with it, the ability to write a config whose plan depends on the clock, the environment or a network call. Terraform avoids that by not being a programming language. Atlantide takes a third route: configs are Python — typed, mypy-checkable, refactorable — but they run under Atlas-lang, an interpreter with no clock, randomness, environment, network or filesystem. Determinism is not a convention authors must observe; the interpreter provides no way to break it.

The diff is a Merkle comparison, not a per-resource read. Each node's hash folds in its dependencies' hashes, so an unchanged subgraph is skipped without a single provider call. See The Merkle hash.

plan needs no credentials. Everything up to the diff is pure computation over the config and the recorded state. validate is cheap enough for a pre-commit hook, and plan can run on a pull request without being handed a state backend it should not have.

Per-field mutability is in the type. Every field is declared mutable(), immutable() or computed(), so whether a change costs an in-place update or a recreate is visible before anything runs — not discovered from the plan output.

Build once, deploy anywhere. atlantide build compiles a config into a content-hashed .atlas artifact with provider versions pinned. deploy applies the artifact without re-executing the config, so the bytes promoted from staging to production are identical.

Where atlantide is behind

Worth knowing before you migrate anything real:

  • Provider coverage is small. Three built-in providers and a couple of dozen AWS resource types, against Terraform's thousands. atlantide resources lists exactly what exists. The plugin interface is open, but there is no ecosystem to draw on yet.
  • No central registry. Components are git URLs you pin yourself.
  • No HCL interoperability. There is no path that reads Terraform configuration or state. Adoption goes through atlantide import, resource by resource.
  • The state format is versioned and still moving. It migrates forward automatically and an older atlantide refuses a newer store, but it can change between minor releases.
  • Beta. The classifier says so.

What migration actually looks like

There is no converter. The realistic path:

  1. Write the config for a slice of existing infrastructure.
  2. atlantide validate, then atlantide plan — everything will show as CREATE, because state knows nothing yet.
  3. atlantide import each resource. Anything whose live values differ from config is refused rather than adopted, so this doubles as a check that the config you wrote matches what is actually deployed.
  4. atlantide plan again. The slice should now be entirely NOOP.

Step 3 is the honest part of the work, and --dry-run makes it free to attempt.