Commit ab4dcf67 authored by Natalia Tepluhina's avatar Natalia Tepluhina
Browse files

Add MFE Platform design document and first ADR

parent bf7f1d5a
Loading
Loading
Loading
Loading
+128 −0
Changes for content/handbook/engineering/architecture/design-documents/mfe_platform/_index.md: 128 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
title: "Microfrontend Platform"
status: proposed
creation-date: "2026-10-07"
authors: ["@ntepluhina"]
coaches: []
dris: ["@xanf", "@fredericcaplette", "@ntepluhina"]
owning-stage: "Plan"
participating-stages: []
toc_hide: true
---

<!-- vale gitlab.FutureTense = NO -->

{{< engineering/design-document-header >}}

## Summary

GitLab ships as a monolith behind a single domain (`gitlab.company.com`).
Product surfaces such as Duo Chat or Work Items
are tightly coupled to the monolith build and release cycle. This design
document proposes a micro-frontend (MFE) platform that lets product surfaces
be developed, built, versioned, and deployed independently while remaining
invisible to the end user: everything stays behind one domain and one
login.

## Motivation

### Problems

- **Monolith coupling.** Every frontend change, no matter how small,
  requires a full monolith release. A one-line fix to Duo Chat waits for
  the same pipeline as a database migration.
- **Team coupling.** All teams share one Webpack config, one dependency
  tree, and one CI pipeline. A broken lint check in one area blocks
  every deployment.
- **No independent versioning.** There is no mechanism to upgrade or
  downgrade a single product surface. Rollback means rolling back the
  entire monolith.
- **External modules have no UI path.** LabBench modules (separately
  deployed services) have no standard way to inject UI into the GitLab
  shell. Each module invents its own integration.

### Goals

1. Product teams can deploy a UI change to their surface without a
   monolith release.
2. Multiple versions of an MFE can coexist. Upgrade and downgrade are
   version picks.
3. Self-managed and air-gapped installs receive MFEs through the same
   release channel they already use.

### Non-Goals

- **Single-domain routing (GATE).** The routing layer that puts modules
  behind one domain is owned by the GATE project and has its own timeline.
  Until GATE is available, the monolith is configured with the marketplace
  URL and that origin is added to the CSP.
- **Module discovery.** The monolith cannot yet discover which LabBench
  modules are deployed. The marketplace location is monolith configuration
  for now.
- **Migrating specific product surfaces.** Each surface remains a consumer
  of the platform and is tracked with its owning team. This document
  covers the platform, not the migrations.

### Constraints today

- **No single domain yet.** Until GATE delivers routing for all deployment
  types (Omnibus through Kubernetes), the monolith is configured with the
  marketplace URL and that origin is added to the CSP so MFEs can be
  embedded from it.
- **No module discovery.** The monolith cannot discover which modules are
  deployed. The marketplace location is monolith configuration, and the
  manifest must work without discovery.

## Decisions

- [ADR 001: Tooling repository strategy](decisions/001_tooling_repository_strategy.md) -
  Monorepo for tightly coupled tooling, polyrepo for standalone utilities.
  Also captures the open question of where MFE application code lives.

## Prior art and existing work

- [Architecture RFC](https://gitlab.com/gitlab-org/frontend/gitlab-mfe/-/blob/main/docs/architecture-rfc.md)
  (Rafa Frederico, 2026-04-23): validates Module Federation 2.0 runtime
  integration with the Webpack 4 host, the `mount`/`unmount` ABI, and
  same-origin serving.
- [Architecture counter-RFC](https://gitlab.com/gitlab-org/frontend/gitlab-mfe/-/blob/main/docs/architecture-counter-rfc.md)
  (Illya Klymov, 2026-07-12): proposes versioned and integrity-verified
  delivery, layered contract ownership, rspack as the build tool, and
  registry-first phasing. Accompanied by a working implementation
  (!245014).
- [Frontend Decomposition design document](../modular_monolith/frontend/_index.md):
  covers the monolith-side modularization that MFEs consume from outside.
- [MFE registry](https://gitlab.com/gitlab-org/frontend/gitlab-mfe-registry):
  existing prototype for publishing MFE assets to GCS with SHA256 checksums
  and version manifests.

## Alternative solutions

### Do nothing

Product surfaces remain coupled to the monolith release cycle. Teams
cannot deploy independently. External modules have no standard UI path.
Vue 3 adoption is blocked on a monolith-wide migration.

**Pros:** No new infrastructure to build or operate.

**Cons:** Coupling cost grows
with the number of teams and surfaces.

### iframes

Each surface is loaded in an iframe pointed at the module's own domain.

**Pros:** Strong isolation. No shared JavaScript scope.
**Cons:** Poor UX (no shared navigation, double scrollbars, no
shared authentication without postMessage). Accessibility and
performance penalties. Does not meet the "one domain" requirement.

### Build-time integration (npm packages)

Each surface is published as an npm package and compiled into the monolith
Webpack build.

**Pros:** No runtime federation overhead. Single build graph.
**Cons:** Does not solve independent deployment or rollback. Every surface change
still requires a monolith release. Does not help external modules.
+153 −0
Changes for content/handbook/engineering/architecture/design-documents/mfe_platform/decisions/001_tooling_repository_strategy.md: 153 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
title: "MFE Platform ADR 001: Tooling repository strategy"
creation-date: "2026-10-07"
authors: ["@ntepluhina"]
toc_hide: true
---

## Context

The UI Platform team owns shared tooling packages (TypeScript config,
ESLint config, Vitest setup, build helpers, publish scripts) and
standalone utility libraries (`@gitlab/frontend-utils`, `@gitlab/frontend-tests`).
These packages are consumed by the monolith, by LabBench modules that
carry their own MFE, and by MFEs that have no module of their own.

We need to decide where these packages live on the spectrum between:

- **(A) Polyrepo:** each package is its own repository, small and
  self-contained.
- **(B) Monorepo:** a single repository with multiple packages managed
  via workspaces.

This decision is about **tooling packages only**. Where MFE application
code lives is a [separate question](001_tooling_repository_strategy.md#mfe-application-code-open).

### Monorepo pros

- **Atomic cross-package changes.** A change that touches multiple
  packages is one MR, not a chain of releases and Renovate bumps.
- **Shared CI improvements.** Pipeline configuration, caching, and
  tooling upgrades happen once and apply to every package.
- **Shorter feedback loop.** Changing a shared package immediately
  surfaces breakage in downstream packages within the same repo.
- **Less infrastructure.** No Renovate chains between packages, no
  project templates, no per-repo CI duplication.
- **Discoverability.** Contributors find all tooling in one place
  instead of searching across repositories.

### Monorepo cons

- **CI complexity.** Pipelines need change-detection to avoid running
  every package's tests on every MR. Tools like Turborepo and Nx help
  but add their own maintenance cost.
- **Maintainer / CODEOWNERS mismatch.** GitLab project maintainers have
  access to the entire project. CODEOWNERS provides granular approval
  but not granular merge permissions.
- **CODEOWNERS upkeep.** Keeping the CODEOWNERS file correct in a
  multi-package repo is painful and error-prone.
- **Larger agent context.** AI agents need to load more context to
  understand the repo, though this can be mitigated with
  per-package AGENTS.md files.

### Polyrepo pros

- **Clear boundaries.** Each repo has exactly one owner, one pipeline,
  one set of maintainers.
- **Small surface for agents.** The smaller the repo, the cheaper it is
  to get an agent up to speed and the more likely it is to propose
  patterns that match.
- **Fast, focused pipelines.** No change-detection logic needed; every
  push runs the full pipeline and it finishes quickly.
- **No permission sprawl.** Maintainer access is scoped to exactly the
  code that person owns.

### Polyrepo cons

- **Renovate chains.** A change in package A that affects package B
  becomes: release A, Renovate bumps B, release B. Auto-merge reduces
  the cost but the chain remains.
- **CI duplication.** Each repo needs its own pipeline configuration,
  linting setup, and publish workflow.
- **Cross-package testing.** Verifying that a change in one package
  does not break consumers requires multi-project pipelines or manual
  testing.

## Decision

**Tightly coupled tooling packages** (TypeScript config, ESLint config,
Vitest setup, build and publish helpers) go in a **single monorepo**
with pnpm workspaces. These packages have strong interdependencies:
changes frequently span multiple packages, and the cost of Renovate
chains outweighs the CI complexity of a monorepo.

**Standalone utility packages** (`@gitlab/frontend-utils`,
`@gitlab/frontend-tests` etc.) remain in **separate repositories**.
These packages have
minimal dependencies on each other, change independently, and gain
little from co-location. The polyrepo cons (Renovate chains,
cross-package testing) are near zero for these packages.

The dividing line: if a package regularly changes together with other
packages, it belongs in the monorepo. If it changes independently and
has a stable public API consumed by many, it stays standalone.

## Consequences

- The UI Platform team creates a single tooling monorepo under
  `gitlab-org/frontend/` with CODEOWNERS covering each package.
- Standalone packages keep their own repositories under the same group.
- Both the monorepo and standalone repos follow the same versioning and
  publishing conventions (changesets, npm publish, Renovate for
  consumers).

---

## MFE application code (open)

Where MFE application code lives is a **separate decision** that has
not been made. The constraints differ from tooling:

- LabBench modules **must** carry their own MFE in the module's
  repository. This is polyrepo by construction and the UI Platform
  team must support it regardless.
- MFEs with no module of their own (Duo Chat, Compliance Center) could
  live in a shared monorepo or in separate repositories.

### Monorepo for MFE apps: pros

- Shared infrastructure (federation config, dev proxy, Storybook) is
  maintained once.
- Cross-MFE changes (shared layout, navigation integration) are atomic.
- The existing
  [`gitlab-mfe`](https://gitlab.com/gitlab-org/frontend/gitlab-mfe)
  repo demonstrates this approach with working CI, Turborepo, and
  Module Federation.

### Monorepo for MFE apps: cons

- The team must support polyrepo anyway (LabBench modules). Maintaining
  both a monorepo and polyrepo developer experience doubles the
  infrastructure work.
- MFE-specific concerns (bundle size, Vue version drift, security
  backports) apply to shipped code, unlike dev-time tooling.
- An MFE that ships with a module changes together with that module's
  backend. Putting it in a separate monorepo adds a cross-repo
  coordination step.

### Polyrepo for MFE apps: pros

- Each MFE is owned and released by the team that owns the surface.
- Aligns with LabBench's module-per-repo model, so the same tooling
  works everywhere.
- Pipelines are scoped to one MFE; no cross-MFE blast radius.

### Polyrepo for MFE apps: cons

- More infrastructure per MFE (CI, federation config, dev proxy).
- Cross-MFE changes require coordinated releases.
- Discoverability: no single place to see all MFEs.

This decision will be revisited once we have more experience with the
first MFE deliveries and a clearer picture of how many surfaces will
not have a LabBench module.