Commit d67564b5 authored by Andrew Newdigate's avatar Andrew Newdigate
Browse files

decisions: Add ADRs 002-004 and a decisions index in the parent doc

Add three more Theseus ADRs alongside the existing ADR-001 (Protobuf):

- 002: One declarative interface, many Platform Bindings — the
  central architectural choice of the doc; rejects both the
  single-deployment-path and the per-target-bespoke alternatives.
- 003: Enterprise Platform Binding services must survive Cell
  outages — formalises the constraint in §4.3 that
  Enterprise-binding services cannot synchronously depend on the
  GitLab Application or Modular Components running in Cells.
- 004: Independent per-component deploys for GitLab.com; bundled
  releases for Self-Managed — codifies the position from §7.3,
  including the Cells (same model, Instrumentor) and Dedicated
  (bundled within maintenance window) special cases.

Add a Decisions section near the top of _index.md with a table
indexing all four ADRs. This matches the convention used by other
large design docs in the repo while placing the index near the top
rather than the bottom.
parent dda49442
Loading
Loading
Loading
Loading
+14
Changes for content/handbook/engineering/architecture/design-documents/theseus_platform_vision/_index.md: 14 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -29,6 +29,20 @@ and different audiences should review different sections.

---

## Decisions

Architectural decisions taken in service of this vision are recorded as ADRs in the [`decisions/`](decisions/) directory.
Each ADR captures the context, the decision, its consequences, and the alternatives considered.

| ADR | Title | Status |
|---|---|---|
| [001](decisions/001_protobuf_as_preferred_schema_language.md) | Protobuf as the preferred schema language | Proposed |
| [002](decisions/002_one_interface_many_bindings.md) | One declarative interface, many Platform Bindings | Proposed |
| [003](decisions/003_enterprise_binding_survives_cell_outages.md) | Enterprise Platform Binding services must survive Cell outages | Proposed |
| [004](decisions/004_independent_vs_bundled_releases.md) | Independent per-component deploys for GitLab.com; bundled releases for Self-Managed | Proposed |

---

## 0 — Executive summary

Theseus is GitLab's internal developer platform:
+129
Changes for content/handbook/engineering/architecture/design-documents/theseus_platform_vision/decisions/002_one_interface_many_bindings.md: 129 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
title: "Theseus ADR 002: One declarative interface, many Platform Bindings"
owning-stage: ""
description: "Decision to have components declare a single Fairway manifest and to translate it per target via a Platform Binding, rather than collapse all targets onto one deployment path or let each target reinvent its own contract."
toc_hide: true
---

<!-- Design Documents often contain forward-looking statements -->
<!-- vale gitlab.FutureTense = NO -->

## Status

**Proposed.**

## Context

GitLab ships to a set of targets with little in common operationally:
the developer workstation (Caproni),
GitLab.com,
Cells,
Dedicated,
Dedicated for Government,
the Enterprise binding (GitLab-Inc-operated services like `customers.gitlab.com`),
Self-Managed Advanced,
and Self-Managed Foundation (Omnibus, legacy).

Each target has its own orchestrator, storage layer, secrets backend, monitoring system, and release cadence.
The two end-points of the design space are clear and both are wrong:

- **Collapse the targets onto one deployment path.**
  Pick Kubernetes + Helm + ArgoCD and require every target to look like that.
  Impossible in practice — customer environments are non-uniform,
  Omnibus on a VM is non-negotiable for Self-Managed Foundation,
  and Dedicated's compliance posture differs from GitLab.com's.
- **Let each target reinvent its own contract.**
  The status quo.
  Component teams write target-specific deployment code, tooling fragments,
  and the developer journey is different for every destination.

The platform needs to sit between these.

## Decision

A component declares a single [`FairwayManifest`](../#42-the-single-declarative-interface) describing its abstract dependencies
(a Postgres database, a key-value store, object storage, and so on).
The manifest is the contract.

Each target has a **Platform Binding**
that resolves the manifest into target-native infrastructure:

- Enterprise binding → CloudSQL, GCS, Memorystore on Google Cloud.
- Cells / Dedicated binding → RDS, S3, ElastiCache on AWS, via Instrumentor.
- Caproni binding → in-cluster operators via [`gitlab-dev-stack`](https://gitlab.com/gitlab-org/cloud-native/charts/gitlab-dev-stack).
- Self-Managed binding → Helm values; the customer provides the infrastructure.

The same component runs on every binding that exists for it,
without source changes,
and a new target lands as a new binding rather than a new component.

## Consequences

### Positive

1. **Write once, deploy everywhere.**
   Component teams target the manifest, not the binding.
1. **Targets evolve independently.**
   A binding can change its dependency story
   without re-releasing every component.
1. **New targets are additive.**
   A new deployment model is a new binding implementation,
   not a fork of every chart in the catalogue.

### Negative

1. **Every binding is a sustained engineering commitment.**
   Bindings need owners, SLAs, and parity with the manifest contract.
1. **Target-native features don't appear automatically.**
   A new managed service on one cloud is invisible to components
   until the relevant binding exposes it through the manifest.
1. **Abstractions leak.**
   When a component needs something the manifest doesn't model,
   it must extend the schema rather than drop to target-specific code.
   The `infrastructure:` escape hatch exists for this
   ([Section 7.3](../#73-open-tensions)) but using it carries a cost.

## Alternatives Considered

### Alternative: One deployment path for every target

#### Approach

Mandate Kubernetes + Helm + ArgoCD (or similar) everywhere
and migrate any target that doesn't fit.

#### Why Not Chosen

Self-Managed Foundation is Omnibus on a VM
and will remain so as a deliberate product choice.
Dedicated for Government has FedRAMP requirements
that limit operational flexibility on the SAAS side.
Customer environments are not negotiable.
A single deployment path is not reachable from where we are.

### Alternative: Per-target bespoke implementations per service

#### Approach

Continue the status quo:
each component writes its own deployment plumbing for each target it cares about.

#### Why Not Chosen

The cost is paid per component per target,
and the result is fragmentation:
inconsistent tooling,
no shared developer journey,
no economies of scale on observability or release tooling.
The whole point of the platform is to amortise that work.

## References

- [Section 4 — One platform, many bindings](../#4--one-platform-many-bindings) —
  the position this ADR formalises.
- [Section 4.1 — The target matrix](../#41-the-target-matrix) —
  the canonical list of bindings and their roles.
- [Section 4.2 — The single declarative interface](../#42-the-single-declarative-interface) —
  the manifest at the centre of the contract.
- [Section 7.3 — Open tensions: the Runway "leaky abstraction" debate](../#73-open-tensions) —
  the known failure mode of this approach and how the `infrastructure:` escape hatch responds to it.
+109
Changes for content/handbook/engineering/architecture/design-documents/theseus_platform_vision/decisions/003_enterprise_binding_survives_cell_outages.md: 109 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
title: "Theseus ADR 003: Enterprise Platform Binding services must survive Cell outages"
owning-stage: ""
description: "Decision that services running on the Enterprise Platform Binding cannot synchronously depend on the GitLab Application or any Modular Component running in Cells, so that a Cell-level outage does not propagate to Enterprise-deployed services."
toc_hide: true
---

<!-- Design Documents often contain forward-looking statements -->
<!-- vale gitlab.FutureTense = NO -->

## Status

**Proposed.**

## Context

The [Enterprise Platform Binding](../#43-the-enterprise-platform-binding) hosts GitLab-Inc-operated services
that are not tied to a single Cell or Dedicated instance —
`customers.gitlab.com`,
license generation,
billing aggregation,
and other services that span the whole fleet.

These services operate *above* the tenant:
many Cells depend on them at once.

Cell failures happen, and recent history shows the upper bound is not minutes:

- In March 2026,
  drone strikes damaged three AWS data centres in the Gulf
  ([reporting](https://theconversation.com/why-iran-targeted-amazon-data-centers-and-what-that-does-and-doesnt-change-about-warfare-278642));
  AWS advised migration out of `me-central-1` and waived a month of charges.
- A follow-on strike four weeks later kept the region degraded.

The same shape of outage is reachable through power events,
network partitions,
regulatory action,
and sustained natural disasters.
The blast radius for an Enterprise service that hard-depends on a Cell is the *Cell's* blast radius.
"Operating above the tenant" is only true if the service stays available when a tenant doesn't.

## Decision

**Enterprise-binding services must have no synchronous dependency on the GitLab Application,
or on any other Modular Component running in Cells.**

Calling the GitLab API on the hot path,
or relying on Tier 3 application authorisation for a request-time decision,
disqualifies a service from the Enterprise binding.

The test product teams apply
([Section 4.3.1](../#431-isolation-boundaries)):

> *If the service goes down or becomes degraded when one Cell goes down,
> it isn't an Enterprise service.*

Services that fail the test go through the Cells, Dedicated, or Self-Managed binding —
the binding whose tenancy model already aligns with the failure domain the service is coupled to.

## Consequences

### Positive

1. **Cell outages don't propagate.**
   Billing and licensing continue working when any single Cell is unavailable,
   regardless of how long the outage lasts.
1. **The test is mechanical.**
   "Pick a Cell, take it offline for a week; does this service stay up?"
   The team building the service can apply the rule themselves.
1. **The binding's tenancy model is honest.**
   Enterprise services genuinely operate above tenants;
   the binding's design isn't quietly contradicted by service-level coupling.

### Negative

1. **Services that need real-time GitLab state can't use Enterprise.**
   Anything that has to read live application data at request time
   belongs on a tenant-aligned binding.
1. **More design work up front.**
   Async patterns — eventual consistency, periodic sync, change-data-capture, caches —
   replace the simpler "call the GitLab API" path.
1. **The boundary has to be re-litigated when scopes shift.**
   A service that started without GitLab dependencies might grow them.
   The constraint forces an explicit decision (rewrite, move binding, or refuse the dependency)
   rather than letting the coupling accrete.

## Alternatives Considered

### Alternative: Allow synchronous GitLab API calls, mitigate with retries and circuit breakers

#### Approach

Permit synchronous calls into Cells from Enterprise services
on the grounds that retries and circuit breakers handle short outages.

#### Why Not Chosen

The threat model is a multi-day regional outage, not a transient blip.
A circuit breaker that has been open for three days
means the feature has been unavailable for three days.

## References

- [Section 4.3 — The Enterprise Platform Binding](../#43-the-enterprise-platform-binding) —
  the binding this constraint protects.
- [Section 4.3.1 — Isolation Boundaries](../#431-isolation-boundaries) —
  the Cell-as-isolation-boundary primitive this builds on.
- [March 2026 AWS Gulf data-centre strikes](https://theconversation.com/why-iran-targeted-amazon-data-centers-and-what-that-does-and-doesnt-change-about-warfare-278642) —
  the concrete incident that informs the multi-day-outage threat model.
+81
Changes for content/handbook/engineering/architecture/design-documents/theseus_platform_vision/decisions/004_independent_vs_bundled_releases.md: 81 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
title: "Theseus ADR 004: Independent per-component deploys for GitLab.com; bundled releases for Self-Managed"
owning-stage: ""
description: "Decision that GitLab.com SAAS migrates incrementally to independent per-component deploys via the Release Framework, while Self-Managed retains bundled monthly meta-packages by design."
toc_hide: true
---

<!-- Design Documents often contain forward-looking statements -->
<!-- vale gitlab.FutureTense = NO -->

## Status

**Proposed.**

## Context

GitLab.com today ships a single meta-package
containing changes from many components.
The SREs operating GitLab.com want the opposite shape:
independent rollback,
per-component cadence,
small blast radius per change.

Self-Managed customers operate their own GitLab installations,
on their own schedule,
with their own change-management processes.
A bundled monthly release is what they ask for:
one upgrade, one version, one set of release notes.
N component versions to track is not a feature — it's a burden.

The [Release Framework](https://internal.gitlab.com/handbook/engineering/architecture/design-documents/release-platform/) aims to make both models possible.

## Decision

**GitLab.com SAAS** migrates to independent per-component deploys via the Release Framework.
The migration is incremental:
components onboard one at a time,
starting with Artifact Registry,
ending with the Rails monolith.
Until the monolith onboards,
GitLab.com retains the train-style cadence as the fallback for everything else.

**GitLab Cells** follows the same release model as GitLab.com SAAS —
independent per-component deploys via the Release Framework —
but the deployment mechanism is Instrumentor.

**Self-Managed** retains bundled monthly meta-packages.
Components are pinned to a release version
and ship together indefinitely.

**GitLab Dedicated** is a special case:
deployments use the newer rollout architecture
(potentially [Argo Rollouts](https://argoproj.github.io/rollouts/))
but the updates are bundled during the maintenance window.

## Consequences

### Positive

1. GitLab.com gains independent rollback.
1. Self-Managed keeps operational simplicity.

### Negative

1. **Two release models maintained in parallel, indefinitely.**
   Tooling, docs, release notes, and CI all carry the dual shape.
1. **Components must work in both shapes.**
   A component shipping independently on GitLab.com
   must also ship as part of the bundle on Self-Managed,
   with version compatibility maintained across both.

## References

- [Section 7.3 — The GitLab.com meta-package transition](../#73-open-tensions) —
  the tension this ADR resolves.
- [Release Framework design doc](https://internal.gitlab.com/handbook/engineering/architecture/design-documents/release-platform/) —
  the mechanism enabling per-component deploys on GitLab.com.
- [Section 5.5 — Deployment: Fairway and the Release Framework](../#55-deployment--fairway-and-the-release-framework) —
  the platform integration point.
- [Argo Rollouts](https://argoproj.github.io/rollouts/) —
  the candidate rollout machinery for GitLab Dedicated's bundled-but-modern path.