---
title: "Appendix A: The shape, runnable"
canonical_url: https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a
description: "A companion repository makes the series architecture executable: four tiers, typed cross-tier contracts, and resolver probes that refuse bad composition before any provider call."
datePublished: 2026-06-25
dateModified: 2026-06-25
series: making-iac-boring
seriesName: "Making IaC boring"
tags: ["infrastructure as code","typescript","node.js","reference implementation","demo"]
about: [{"name":"Infrastructure as code","@type":"Thing","sameAs":"https://www.wikidata.org/wiki/Q24964334"}]
mentions: [{"name":"TypeScript","@type":"SoftwareApplication","sameAs":"https://www.wikidata.org/wiki/Q978185"},{"name":"Node.js","@type":"SoftwareApplication","sameAs":"https://www.wikidata.org/wiki/Q756100"}]
---
# Appendix A: The shape, runnable

_The companion repository._

The series named the shape in prose and diagrams. This companion makes it executable: four tiers cut by ownership, typed cross-tier contracts, and resolver probes that refuse bad composition before any provider call.

It runs as an isolated demo, all placeholder. No provider SDKs, credentials, live domains, or production configuration. Domains use the reserved `.invalid` suffix, and every name is a generic fixture.

[Read the code](https://github.com/ruslanakchurindev/making-iac-boring).

## Run it

No install. Native TypeScript on Node 22.6 or newer:

```sh
node demo/run.ts
```

The walkthrough applies the tiers in order, resolves every contract, prints the resource plan and the release waves, then fires the refusal probes. The same walkthrough is split into three short screen recordings in the repo.

## What a consumer looks like

[Fail before apply](/blog/making-iac-boring/04-fail-before-apply) showed the resolver: the helper that refuses. This is the other side of that seam, a workload consuming the contract. It asks `serviceProjects` for its assigned project by name and attaches to the shared network by key. It never creates its own project; that membership line belongs to the environment tier ([Define tier membership](/blog/making-iac-boring/03-membership)).

```ts path="workloads/workload-api/index.ts"
// A workload consumes the environment
// contract. It never creates its project.
const assigned =
  getServiceProject(envRef, 'workload-api')

const subnet = getNetwork(envRef, 'primary')
  .apply((n) => n.primarySubnetId)

// ...then builds its service, runtime
// identity, and prefix-scoped secret
// access inside the assigned project,
// and publishes backends for edge.
```

## What the demo refuses

The walkthrough ends by composing the system wrong on purpose. Each probe is refused by name before any apply:

```svg path="making-iac-boring/diagrams/appendix-refusals.svg"
validated helpers
  unknown service in serviceProjects
    'unknown-worker' not found.
    Available: workload-api, web-app
  capability never declared
    service 'web-app' has no
    artifactRegistry capability

domain topology
  duplicate zone owner
    'api.staging' claimed twice in
    the same environment

shared-surface binding rules
  DNS write outside the app zone
    'api.staging.example.invalid' is
    outside '*.app.staging...invalid'
```

## Map it back to the series

The repo reads alongside the parts:

- `organisation/`, `environment/`, `workloads/`, and `edge/` are the tier cut from [Start with the shape](/blog/making-iac-boring/01-shape).
- `lib/*/types.ts` and the `build*Output` producers are what each tier publishes, from [The contract](/blog/making-iac-boring/02-contract).
- The environment-owned `ServiceProject` boundary is the membership rule from [Define tier membership](/blog/making-iac-boring/03-membership).
- `lib/*/consumer.ts`, `assertDnsRequest`, and `assertSecretRequest` are the resolver gates from [Fail before apply](/blog/making-iac-boring/04-fail-before-apply).
- `MIGRATION.md` maps the deletion order from [Re-cut the system](/blog/making-iac-boring/05-re-cut-the-system).

The repo carries its own README and a `MIGRATION.md` that maps the deletion order onto the code. Composed wrong on purpose, every bad path stops at a named refusal before any apply.
