Ruslan Akchurin · Sydney
AboutWriting

Start with the shape

Ownership and tier boundaries.

The hardest single part of building a software system is deciding precisely what to build.

Frederick P. Brooks, "No Silver Bullet" · 1986

By the time anyone reviews an IaC codebase, its shape is already decided: what changes together, who can apply it, and which layer depends on which.

If that shape is not chosen on purpose, resource-first growth chooses it instead. State boundaries become hard to move, shared modules lose owners, and environments start mirroring account layouts, team boundaries, and whatever the first migration made convenient.

A common version is the environment stack becoming the place for workload exceptions. A service needs a one-off subnet route, an extra record, or a special role binding; the environment tier absorbs it because the resource lives nearby. Six months later, unrelated workload deploys need platform review, environment applies can break application behaviour, and the boundary has stopped matching ownership.

The two cuts that matter

Shape is mostly two decisions. Ownership decides who applies a tier, takes the page, and has authority to change the contract consumers rely on. Tiers decide what must exist before anything else can be applied, and which direction dependency is allowed to flow.

Ownership comes first because a tier no team owns cannot carry production safely. Applying day-to-day changes and deciding when a published contract can break are different authorities, and the second is the one usually missing. Without it, every consumer gets an informal veto and the tier freezes around its first shape.

The dependency graph sets the tier boundary. Lower tiers publish capabilities and apply first; higher tiers consume them through the published contract only. When foundation reads workload outputs, or environment accumulates workload exceptions, the boundary is already wrong. The repair is to re-cut the system so the thing that changes with the workload lives with the workload.

The symptom is review scope widening for the wrong reason: an application release drags an environment apply with it, or a foundation change has to account for workload-specific behaviour it should never have known about.

Re-cut by ownership Two cuts of the same resources. On the wrong cut, workload-specific items sit inside the environment tier, ringed by the accent border; the workload tier below holds only its service. On the clean cut, the environment publishes a network contract and the workload tier owns its service, DNS record, and role binding. WRONG CUT CLEAN CUT environment network workload dns record workload role binding workload-owned workload service environment network contract workload service dns record role binding
Re-cut by ownership

The clean cut moves workload-specific behaviour back to the owner responsible for releasing and repairing it; folder layout is only the visible symptom.

Contracts make cuts durable

A consumer that did not change should remain valid when a lower tier changes internally. The interface between tiers is : output names, subnet semantics, IAM primitives. The implementation behind that contract can move; the contract cannot quietly change underneath consumers. Renaming an output, repurposing a subnet under the same name, or changing a role's trust assumption invalidates downstream stacks without their consent. Breaking changes are allowed, but they need to be named.

Destructive cross-tier changes need staging. Consumers detach first, the provider removes the object second, and the gap has to be visible in review and release planning. The protection should be layered because any single guard can usually be edited by the same person performing the destroy.

Strictness depends on change cadence and blast radius. A foundation tier changes rarely and underpins many workloads, so its contracts should be nearly immutable and its migrations planned. A workload tier can move faster because its contracts are mostly internal to the team and its intended blast radius is narrow. Lifecycle and state size shape the internal split after that, but both are secondary.

Every stack shrinks the blast radius of an apply and adds orchestration across a new boundary. A new stack earns its place when the existing dependency graph cannot hold, or when ownership, cadence, and blast radius line up strongly enough to pay the cost. Folder size, resource importance, existing module boundaries, and cleaner-looking plans are weak reasons on their own.

A default three-tier cut

The durable starting cut is three tiers: organisation, environment, workload.

Apply order Three-tier apply order: organisation publishes first, then environment, then workload as the consumer. The environment-to-workload edge is a published contract, drawn as the single accent edge. APPLY ORDER dependency order organisation publishes company-wide roots 1 environment publishes landing-zone capabilities 2 contract workload consumes the environment contract 3
Apply order

Organisation contains things that exist once for the company before any environment exists: account roots, users, groups, source-control administration, SaaS configuration, domain ownership, bootstrap access, policy roots. The cloud provider's Organization object belongs here only when its lifecycle matches the tier; it is one resource, not the tier. Membership here requires something to exist once with everything downstream of it; resources that are merely important rather than genuinely company-wide do not belong here.

Environment describes the landing zone for services in a given environment. In cloud terms, that usually means the projects or accounts those services run inside, plus environment-scoped resources such as network and federated access. But the boundary is not limited to cloud resources: Vercel environments, GitHub environment configuration, and any other platform settings that exist once per environment belong here too. This tier changes less often than workloads and fails wider than any single application. Its contract says what a workload can attach to or consume, not how a specific workload happens to be deployed today. Workload exceptions either belong with the workload or prove the boundary needs to move.

Workload is application-owned. It consumes everything below it and should have the highest change cadence and the narrowest intended blast radius. A workload owns what it can break, repair, and migrate without forcing unrelated consumers to move. If applying a workload requires changing environment or organisation, the contract is missing or the cut is wrong.

DNS tests the boundary

DNS has more than one ownership surface. The domain at the registrar belongs with organisation because it exists before any environment and everything downstream depends on it. Parent delegation belongs there too: it is the organisation-owned contract that says which lower layer is authoritative for a zone.

Hosted zone authority and record authoring should follow cadence and ownership. A per-environment zone can live with environment when it mostly publishes landing-zone names. Service records, validation records, and routing records that change with releases belong closer to the workload, even when they are written into a delegated zone.

The boundary is wrong when organisation has to read live environment outputs before it can apply, or when environment becomes the dumping ground for . Delegation values are a reviewed contract between tiers, not a reverse dependency from organisation into environment.

Edge usually sits closer to workloads because listeners and routing policy change near application releases. It belongs with the workload when the same team owns it at the same cadence; a separate cut is warranted when it is shared, security-sensitive, or reused across workloads.

If an environment tier changes every time a workload ships, workload behaviour is stored in the wrong place. The next part moves from shape to contracts: what each tier publishes upward, and what makes that contract stable enough for release planning.

In the companion repoGitHuborganisation · environment · workloads · edge (opens in new tab)