Architecture One-Pager
Solution Architecture v1.0 · Microsoft Azure · Platform Architecture · 2026-09 · 21 views · 16 architecture decision records
Internal Developer Platform · Solution Architecture v1.0 · Microsoft Azure · Platform Architecture · 2026-09 · 21 views · 16 architecture decision records
The paved road is a product teams may decline; the guardrails are controls they cannot. Generation and enforcement are separate planes, and they meet only at the runtime.
Creating a new service at this organisation takes nineteen working days and touches seven teams: a repository request, a pipeline request, a namespace request, a database request, a DNS and certificate request, a secrets onboarding request, and a security review that arrives after the architecture is already built. Every step is a form filled in by an engineer who does not want to be an expert in the thing the form is about, and read by a specialist who is the bottleneck for four hundred other engineers. The cost is not only the nineteen days. It is the services that never get split because splitting is expensive, the security review that lands too late to change anything, and the gradual accumulation of workloads nobody can attribute to a team. The platform's job is to make the distance between an engineer's intent and a production-grade running system as short as it can honestly be — without making the result less safe, and without becoming a ticket queue with a nicer interface.
An engineer picks a golden path in the portal or the CLI. The scaffolder creates the repository with its catalog descriptor, deployment intent, infrastructure intent, a reference to the shared pipeline, and the observability and on-call wiring already in place. Everything the engineer declares lives in their own repository; the platform's surfaces write intent and then stop. Two reconcilers converge the world on that declaration — one for workloads into a shared AKS fleet, one for infrastructure through Terraform against Azure Resource Manager — and both are idempotent, resumable and drift-detecting. Alongside them, and sharing no component with them, runs the enforcement plane: policy authored as code, signed into a bundle, cached locally at every evaluation point, advisory in the pull request and binding at admission into the cluster. A component that never touched a golden path meets exactly the same binding checks. Everything that results — what is running, who owns it, what it cost, what it is failing — is projected into a catalog that is rebuildable rather than authoritative, and into the adoption and DORA metrics by which the platform judges itself.
What it is, and what it is not
- A product engineers choose because it is faster than the alternative — not a mandated standard whose adoption figure is 100% by definition and therefore says nothing.
- A set of controls enforced where workloads run — not a template that happens to contain the right settings until someone forks it.
- A mechanism that reconciles towards intent the team owns — not a database of other people's configuration with a form in front of it.
- A system that may fail without production noticing — not a portal in the path of anyone's deploy, rollback or running traffic.
- An answer that arrives in minutes and explains its own refusals — not a ticket queue with better typography.
The decisions that are the architecture
- Guardrails live in the runtime, not the template (ADR-05) — Enforcement is a property of admission into the cluster, authored and signed separately from any golden path. A fork removes the template and never the control.
- One policy source, two evaluation points (ADR-06) — Advisory in the pull request to teach, binding at admission to hold. A CI-pass-then-admission-fail is defined as a platform defect and measured as one.
- Intent lives in the team's repository (ADR-01) — The portal writes intent and owns nothing. The platform's catalog is a projection, which is what lets it be rebuilt rather than protected at RPO 0.
- Provision by reconciliation (ADR-02) — Declared state converged on continuously, so a partial failure resumes and a console edit becomes a detected event rather than invisible drift.
- Track the template by default; forking is recorded (ADR-03) — The fork ratio is the number that predicts migration cost, and the pipeline is consumed by reference so the most change-prone artefact is not part of the fork surface.
- Attribution at creation (ADR-11) — Owner, cost centre, tier and data classification are enforced properties applied when a thing is made, because nothing acquires an owner afterwards.
- Entitlement derives from ownership (ADR-10) — Catalog ownership resolves to a live identity group, so a reorganisation moves access with the components and there is no second list to decay.
- The platform is never in the critical path (ADR-12) — A portal outage costs the ability to change, never the ability to serve. Running workloads are never evicted because the platform cannot evaluate them.
- Exceptions are named, approved by another, expiring and published (ADR-08) — Relief always exists; making it visible is what stops it becoming a quiet policy edit for the loudest team.
- A thin abstraction with a designed escape hatch (ADR-07) — Every concept the golden path hides is met for the first time at 03:00, so the substrate stays addressable and access to it is an entitlement rather than an incident favour.
Why this should still be right in ten years
An internal platform outlives the tools it was built from — the portal, the reconcilers, probably the cloud. These are the properties that should survive all of that, and the ones a future team would most easily discard without noticing what they were for.
- The plane separation names no technology. ADR-05 says that evidence of a control must come from where the workload runs, not from how it was created. That is true of Kubernetes admission, of whatever replaces it, and of a substrate that has no admission point at all — in which case the architecture must say honestly that its coverage claim has weakened, rather than quietly keep making it.
- Ownership of truth decides ownership of the system. Keeping intent in the team's repository is not a Git preference. It is the reason the platform can be rebuilt without asking 140 teams what they had, and the reason the platform team is never asked to act as the owner of somebody else's configuration on the worst day.
- Optional adoption keeps the feedback signal alive. A mandate would raise coverage and destroy the only honest measurement of whether the paved road is good. The shadow estate is uncomfortable to report and is the most valuable number the platform produces.
- Cognitive load is a budget that will be spent if it is not defended. Every golden path will want one more concept, and each addition is individually reasonable. Stating the budget as a design constraint is what makes the tenth addition arguable rather than inevitable.
- The control plane stays out of the data path. The rule that a platform outage may cost the ability to change but never the ability to serve is the one that decides whether the platform survives its first bad day. It is also the first rule broken under deadline, which is why the SLOs are published to tenants.
- What is rebuildable and what is not is a permanent question. The storage classification in ADR-16 is a method rather than a configuration: ask of every store what it could be reconstructed from, and protect only the ones with no answer. That survives any change of database, cloud or retention regime.
Non-functional targets
Every number here is a stated assumption from the requirement, chosen to be argued with rather than believed. The right-hand column names the view where the mechanism that meets it is drawn.
| Quality | Target | How it is met | View |
|---|---|---|---|
| Time to first production deploy | ≤ 30 min p50, ≤ 2 h p95 for a new component | One scaffold action produces repository, pipeline reference, manifests, observability wiring and catalog entry; provisioning runs in parallel with the engineer writing code. | 04 |
| Self-service provisioning latency | ≤ 12 min p95 for the golden-path resource set | Declared intent admitted against quota, then reconciled by a runner pool with idempotent, resumable applies. | 14 |
| Commit to production | ≤ 25 min p95 on the paved road | Shared pipeline consumed by reference, one artefact promoted unchanged, reconciler applies without a second build. | 17 |
| Binding guardrail evaluation | ≤ 250 ms p99, ≥ 99.95% monthly | Three admission webhook replicas per cluster evaluating a locally cached signed bundle — no network call to a central service on the admission path. | 15 |
| Advisory guardrail feedback | ≤ 90 s p95 in the pull request | Same bundle, evaluated in the pipeline; a divergence from the admission verdict is instrumented as a defect. | 15 |
| Rollback | ≤ 5 min p99 to serving the previous version | Reconciler-level revert to the previously converged state, independent of the pipeline and of a fresh build. | 17 |
| Reconciliation lag | ≤ 5 min p95, ≤ 20 min p99 | Sharded reconcilers with bounded worst-case lag, plus drift detection on resource-graph change events. | 14 |
| Catalog freshness | ≤ 10 min p95 after a deploy or ownership change | Webhook-driven descriptor ingestion with a periodic full reconcile as the backstop. | 10 |
| Platform availability | Portal ≥ 99.5%, build and deploy control plane ≥ 99.9%, admission ≥ 99.95% | Availability is graded by what each component is in the path of; the portal holds no truth and is in no production path. | 16 |
| Guardrail coverage | ≥ 99% of production workloads, off-road included | Enforcement at admission rather than in templates, so coverage describes what is running rather than how it was made. | 15 |
| Adoption | ≥ 70% on a current golden path by month 12; shadow estate ≤ 10% | Measured from the catalog's projection of declared and observed state, and reported as the platform's own scoreboard. | 18 |
| Recovery | Provisioning state RPO 0 / RTO 1 h; catalog RPO 15 min / RTO 4 h; portal RPO 24 h | Storage classified by what a loss would cost, with a full control-plane rebuild exercised twice a year. | 11 |
Scope
In scope
- A software catalog with enforced ownership, lifecycle state and tier, reconciled against the runtime in both directions.
- Golden paths that scaffold repository, pipeline reference, manifests, observability wiring and catalog descriptor in one action, with drift measurement and automated update pull requests.
- Self-service provisioning by declared intent for a bounded catalogue of resource kinds, admitted against quota, budget and data classification.
- A shared build and deploy path producing signed, attested, content-addressed artefacts promoted unchanged through environments, with progressive delivery and pipeline-independent rollback.
- Policy-as-code guardrails evaluated advisorily in the pull request and bindingly at admission, with a named, expiring exception register.
- Portal, CLI and API over one behaviour surface, with a single component page carrying health, deploys, dependencies, on-call, cost and open findings.
- Workload identity everywhere, brokered secrets, and an append-only audit of every privileged action.
- Adoption, DORA and control-coverage reporting, including the shadow estate.
Explicitly out of scope
- Product code, product architecture and product on-call. The platform paves the road; the teams drive on it.
- The landing zone: subscription vending, network topology and the organisation's Azure Policy floor. The platform is its tenant.
- Being an approval workflow. A request the platform accepts and then queues for a human has already failed at its only job.
- Owning tenant multi-region strategy — the platform's paired region carries its own recovery, not an active copy of every tenant.
- Per-pull-request ephemeral environments and self-service golden-path authoring, both deferred to Phase 3.
What a six-week prototype should prove
Four of this architecture's claims are cheap to test and expensive to be wrong about. A prototype that proves these can be built on; one that only demonstrates scaffolding a repository has proved the easy half.
- Plane separation end to end: scaffold a component, fork its template so every generated control is deleted, deploy it, and confirm the binding admission checks still refuse it for exactly the same reasons. Then deploy a vendor Helm chart that was never scaffolded and confirm it meets the same checks.
- One policy source, two points: take a real control, evaluate it in the pull request and at admission from the same signed bundle, then deliberately desynchronise the bundle and confirm the divergence is detected and reported rather than silently tolerated.
- Reconciliation under failure: kill a provisioning run halfway through a multi-resource set, confirm it resumes from the declaration without a human reconstructing state, then change a resource in the Azure console and confirm the drift is detected, attributed and reported within the lag budget.
- The migration lever: scaffold twenty components, publish a template change, and measure what fraction land automatically, how long the tail takes, and what it costs a platform engineer per forked component. That ratio, more than any latency number, decides whether twelve people can serve nine hundred.
- Fork a scaffolded component's template so every generated control is deleted, deploy it, and confirm admission refuses it for exactly the same reasons — then deploy a vendor Helm chart that was never scaffolded and confirm it meets the same checks.
- Desynchronise the policy bundle between the pipeline evaluator and the admission webhook, and confirm the divergence is detected and reported rather than experienced by an engineer on a Friday.
- Kill a provisioning run halfway through a multi-resource set and confirm it resumes from the declaration, with no human reconstructing which half exists.
- Change a provisioned resource in the Azure console and confirm the drift is detected, attributed to the owning team and reported inside the lag budget.
- Take the portal down entirely and confirm a deploy, a rollback and a reconciliation all still complete.
- Delete the identity group that owns a running component and confirm it is quarantined and reported rather than continuing to run unattributed.
- Publish a template change across twenty scaffolded components and measure what lands automatically, how long the tail takes, and the platform-engineer cost per forked component.
Open risks, carried rather than hidden
| Risk | If it lands | Response |
|---|---|---|
| Adoption stalls below the threshold where the platform pays for itself | Twelve engineers maintaining a paved road most teams decline is a worse outcome than no platform, because the guardrails still have to be run and the shadow estate still has to be supported. | Adoption and satisfaction reported per path from day one, with a declining path treated as a signal about the path. The requirement leaves the mandate question explicitly open, and the answer changes the business case rather than the architecture. |
| Advisory and binding verdicts diverge in practice | Engineers stop believing the pull-request verdict, and every control is met for the first time at admission — the experience that makes platforms hated and routed around. | One signed bundle for both points, the divergence rate instrumented as a defect class in view 18, and a narrower advisory set if identical evaluation proves impossible at some stage. |
| The forked tail consumes the platform team | If the fork ratio is materially above the assumed 16%, migrations stop being automatable and the 1:75 staffing ratio fails quietly — as unshipped fixes rather than as an incident. | Measure the ratio in the prototype. The lever is moving more of the template into artefacts consumed by reference, so that forking the repository does not fork the pipeline. |
| The abstraction is wrong for the on-call population | Too thin and every engineer pays learning costs the platform was meant to remove; too thick and the person paged at 03:00 meets the substrate for the first time under pressure. | Escape-hatch usage is measured, and a spike names the abstraction that is failing. This is the requirement's most openly unresolved question, and the substrate is kept addressable so the line can move in either direction without a rewrite. |
| Shared-cluster upgrades become the platform's dominant risk | One upgrade can affect every tenant in a cluster, and a bad policy bundle can stop deploys fleet-wide. | Staged fleet rollout with automated tenant smoke checks and abort-and-hold, a canary component per golden path walking the whole path continuously, and staged reversible policy publication. |
| Provisioning state is lost or corrupted | The platform stops knowing what it created; recovery becomes reconciliation by hand across an estate of 1,400 components. | RPO 0 with versioning and locking, restore exercised twice a year rather than assumed, and a rebuild path from declared intent as the backstop of last resort. |
The reasoning behind every component and technology choice is in the Architecture Decision Record: 16 records across 6 areas, each with the alternatives that lost and what the choice costs.