Skip to content

Define, Build, Deploy

Why Alis Build DBD splits shipping a service into three steps, what each step guarantees, and why the steps run from pushed commits rather than from your working tree. Read this to form a mental model before you automate the loop.

One loop, three guarantees

Every change to a service on AlisX goes through the same loop, which alisx.com calls Alis Build DBD:

flowchart TB
  P[protobuf in the<br/>definitions repo] -->|alis define| C[contract version<br/>+ language packages]
  C --> I[implementation in the<br/>build repo]
  I -->|alis build| V[service version<br/>+ Docker images]
  V -->|alis deploy| E[deployment in a<br/>deployment environment via Terraform]

Define compiles a service's protobuf files into a versioned contract and generates packages for Go, Node, Python, Dart, .NET, Swift and Rust. It guarantees that a given contract version means one thing everywhere: the server that implements it and every client that calls it compile against the same generated code.

Build turns one pushed commit of the product's build repo into a service version with its Docker images. It guarantees that a version is a fixed, reproducible artefact: the images are tagged with the commit, and the version records the commit, the branch and who built it.

Deploy applies a version's Terraform into a deployment environment. It guarantees that what runs in a deployment environment is exactly one known version, and that the infrastructure around it (the Cloud Run service, its permissions, its queues and buckets) is described in code next to the service and changes only through a deploy.

Splitting the loop lets each step be small, checkable and repeatable on its own. You can define a contract before a line of the implementation exists, build a version once and deploy it to three deployment environments, or redeploy an old version without rebuilding it.

Why the contract comes first

A protobuf contract is the boundary between a service and everything that talks to it. Defining it as a separate step, in a separate repository, means clients and servers can be written in parallel against generated code, and means a contract change is a visible, reviewable event with its own version number. Contract versions keep the patch number at zero (1.4.0); build versions fill it in (1.4.7), so you can read from a version number which contract an implementation was built against.

The dry run (alis define --dry-run) exists because a define compiles a pushed commit. Validating your uncommitted files with the same compiler first keeps broken protos out of the shared repository.

Why builds run from a pushed commit

Build reads the remote repository, never your working tree. This is what makes a version trustworthy: the commit it names is the code that was built, anyone can check it out, and nothing that only existed on one laptop can end up in production.

The cost is that you push before you build. The CLI chooses the latest pushed commit touching the service folder on your checked-out branch, so in daily work the push is the only extra step. Automation passes --branch explicitly.

Versions are immutable

A service version is never rebuilt or changed. Its images are tagged with the commit, its record names the commit and branch, and a deleted version number is never reused. When only infrastructure changed, --retag creates a new version that points at the previous images rather than rebuilding them. Immutability is what makes "deploy version 1.4.7 to production" a precise statement, and what makes a rollback a deploy of an older version rather than a new build.

Terraform is the deploy step

Deploy runs terraform apply (or plan) on the service's infra/ folder with a state backend the platform provides and a set of injected variables that identify the deployment environment, the product and the version. The service's own infrastructure lives with the service, so a change to memory limits, a new queue or a new secret is committed, built (or retagged) and deployed like any code change, and the Terraform plan shows exactly what will happen before it does.

Because the deploy is Terraform, --plan-only is a first-class step: a plan changes nothing, needs no production confirmation, and is the right way to preview a production deploy or an infrastructure change.

Long-running by design

Each step is a long-running operation on the platform. The CLI waits and streams progress by default, but the operation does not depend on the CLI: you can start it with --async, close the laptop and re-attach later with alis operations wait. Interrupting the CLI never cancels the work. This is what lets coding agents drive the loop safely: they start operations, read one final JSON result, and never have to poll git or sleep.