Skip to content
Ayhan Sipahi Ayhan Sipahi

The Paved Road: A Frontend Platform Feature Teams Actually Drive On

How a frontend platform team makes the right way the easy way: golden-path scaffolding, versioned shared packages, and a task CLI that removes per-team drift.

When an organization runs many small frontend apps, each team starts from a different scaffold and the build config, lint rules, and deploy shape drift apart within a few sprints. The drift is not laziness: there is no single place where the standard way to start an app is also the fastest way to start one, so every team rebuilds the basics and reimplements observability, i18n, and deployment slightly differently. The fix is a thin paved road built from three primitives (a golden-path skeleton, versioned shared packages, and a task CLI) where the only metric that proves it works is voluntary adoption, not a coverage mandate.

Why Per-Team Drift Accumulates

The drift has a structural cause, not a discipline one. When the standard setup lives in documentation rather than in tooling, every new app pays the full setup cost again, so teams copy the closest existing repo and edit until it builds. Specifically, four costs compound:

  • Build config, lint rules, and test thresholds diverge because each is copied, not extended from a shared source. A rule change then needs N edits across N repos.
  • Observability, i18n, and deploy manifests are reimplemented per app, so a platform-wide change becomes N independent migrations instead of one.
  • Shared UI and design tokens get forked or vendored into each app, so a design change never lands everywhere at once.
  • The steps to fetch credentials, exchange service tokens, and deploy live in someone’s shell history rather than in a tool, so onboarding a new app or engineer takes days of copy-paste-and-debug.

The fix is not more documentation or a stricter review checklist. The fix is to make the standard path the path of least resistance, so a team reaches for it because rolling their own is slower. That is the paved road idea, and Netflix states it plainly: it does not mandate adoption of its paved-road tools but earns it “by ensuring that development and operations using those technologies is a far better experience than not using them” (Full Cycle Developers at Netflix). Spotify frames the same idea as a Golden Path that reduces fragmentation and lets teams “make fewer decisions” so creativity goes to higher objectives (How We Use Golden Paths).

owns

drives on

drives on

drives on

Platform Team

Paved Road: Skeleton + Packages + CLI

Lane: app A

Lane: app B

Lane: app C

Feature Team A

Feature Team B

Feature Team C

The platform team owns the road; feature teams drive on it. Consistency in observability, i18n, deploy shape, and design becomes the default state rather than something each team reinvents. The road is a product, in the Team Topologies sense: a platform team builds “a compelling internal product to accelerate delivery” by the teams that consume it (Team Topologies Key Concepts). The three primitives below are the thinnest version of that product.

The Golden-Path Skeleton

The first primitive is a starter repository that is production-ready on the first clone. A team clones it, renames the app, and has a working build, lint, test, component workshop, and deploy manifest before writing any feature code. The skeleton is a clone-and-go monorepo, not a generator that emits a frozen snapshot, because what it wires together is more important than the file tree it produces.

What lives in a useful skeleton:

  • Monorepo workspace tooling. This is a live choice, not a settled one. lerna now bills itself as “the original tool for JavaScript monorepos” and a “fast, modern build system powered by Nx” (lerna.js.org), so the modern story is lerna plus Nx caching, not “drop lerna.” The alternatives are Nx standalone, a smart build system with a task graph and computation caching (Nx intro), and Turborepo, another caching monorepo build system (Turborepo docs). Pick one task runner and one versioning tool; the failure mode is running two that both claim to own publishing.
  • Shared TypeScript and lint/format config, extended not copied. The skeleton extends a config published from the shared registry rather than inlining rules, so a rule change ships once and apps pull it on upgrade.
  • Test setup with coverage thresholds baked in. The road enforces the floor; the team owns what they test above it.
  • A component workshop pre-wired. Storybook is “a frontend workshop for building UI components and pages in isolation” (Storybook docs); its current major line is Storybook 10, scaffolded with npm create storybook@latest. Verify the exact patch in your toolchain before pinning.
  • A standardized deployment manifest. Generalize it as a delivery.yaml that every app ships, so observability, i18n, and routing are wired in by default rather than per team.

A scaffold command looks like an ordinary clone plus a rename step, not a bespoke generator:

git clone <skeleton-repo> my-app && cd my-app
node ./scripts/rename-app.mjs my-app
npm install
npm run dev

A delivery manifest carries the deploy shape that used to differ per app. Generalized, it names the app, its runtime, and the cross-cutting concerns the road wires in by default:

# delivery.yaml
app: my-app
runtime: node-20
observability:
  tracing: enabled
  logs: structured
i18n:
  default: en
  locales: [en, tr]
routing:
  base_path: /my-app

The manifest is the contract between the app and the road. Because every app declares the same shape, a platform-wide change to how observability or routing is provisioned edits the road, not every app.

A Registry of Versioned Shared Packages

The second primitive is a registry of shared packages that apps consume by semver: a UI component library, a design-tokens package, and small shared utilities such as a request client and server middleware. The key property is independent versioning. The platform ships a new version; teams pull it on their own cadence. This is what keeps autonomy intact, because the road is versioned rather than a forced global rebuild.

Independent versioning needs changelog automation. changesets is “a tool to manage versioning and changelogs with a focus on monorepos” (changesets); a contributor records an intent and the tool computes versions and changelogs at release time. Verify which release line is GA in your setup before adopting, since the project maintains more than one branch. lerna also ships lerna version and lerna publish for the same job. Pick one and do not run both, or version bumps fight each other.

A changesets step is a short, runnable part of the release flow:

npx changeset            # record an intent: which packages changed, at what bump
npx changeset version    # apply the bumps and write changelogs
npx changeset publish     # publish the changed packages to the registry

The semver contract is the load-bearing part. Decoupled adoption means a fix to the shared UI library is published once, and apps adopt it when they upgrade, rather than every app rebuilding in lockstep. The cost is version sprawl, which the next section addresses, but the autonomy is the point: a versioned road is one a team chooses to advance along, not one that moves under their feet.

The Task CLI

The third primitive is a task CLI that wraps the repetitive incantations: fetching credentials, exchanging service tokens, interacting with the cluster, and running the common build and deploy steps. The CLI is the road’s on-ramp, the place where “the right way is the easy way” becomes literal, one command instead of a memorized shell sequence.

Clone skeleton

Pull versioned packages

CLI: creds, tokens, build, deploy

Production ready

Roll your own scaffold

Config drift

Slow onboarding

The solid path is the road; the dashed path is what happens without it. The CLI matters because it moves tribal knowledge out of shell history and into a tool that the whole organization shares and the platform team maintains. When the way to deploy is a documented deploy subcommand rather than a sequence someone remembers, a platform-wide change to that sequence ships in the CLI, and every team gets it on the next pull.

This mirrors the Netflix ergonomic precisely: the experience of using the road is better than the experience of not using it, so adoption follows without a memo. The CLI is where that experience is felt on day one.

When to Override the Thin Road

The thin road is the default, not the only shape. Two override cases are worth naming.

Add a developer portal when discovery itself becomes a cost. Backstage is an “open source framework for building developer portals,” built on a software catalog, with Software Templates to “spin up new projects and standardize tooling” and TechDocs for documentation (What is Backstage?). A portal earns its weight once finding the right app, owner, or template across many apps is a problem in itself. Below that threshold, a skeleton repo plus a CLI is lighter and faster, and a portal is overhead nobody asked for. The trigger is discovery cost, not app count alone: ten apps with clear ownership may not need one, while a sprawling estate with unclear ownership does.

Enforce, rather than incentivize, where the stakes are regulatory or safety-critical. The thesis sides with incentivize, but enforcement is legitimate in some contexts. Writing on standardization strength distinguishes paved roads and golden paths from guardrails and railroads, where the stronger forms constrain rather than merely encourage (mia-platform: Paved Roads, Golden Paths, Guardrails and Railroads). In a regulated domain, a mandated deploy shape or a required audit hook is a guardrail worth its rigidity. The rule of thumb: enforce coverage floors and deploy shape where divergence creates real risk; leave component composition and business logic to teams.

yes

no

incentivize

mandate

Thin paved road

Discovery cost high across many apps?

Add a developer portal

Stay thin

Mandate or incentivize?

Make it the better experience

Risk: shadow scaffolds

Incentivize is the recommended branch because a mandatory road produces shadow scaffolds when it is not also the easier road. Mandate is the risk branch, legitimate only where the stakes justify the rigidity.

This is a frontend-specific paved road: scaffolding, versioned UI packages, and a task CLI, not a general infrastructure self-service platform. The infra platform concerns of provisioning, namespaces, and cluster access are adjacent and may already exist; the frontend road sits on top of them and standardizes how apps are built and shipped, not how compute is provisioned.

Trade-offs

Each primitive carries a cost worth naming up front.

PrimitiveMain benefitMain cost / failure mode
Golden-path skeletonProduction-ready clone in minutesSnapshot rot: scaffolds age, generated apps diverge from the current road unless the skeleton is dogfooded
Versioned packagesIndependent, decoupled adoptionVersion sprawl: a platform-wide fix still needs teams to upgrade, so old versions linger
Task CLITribal knowledge becomes a shared toolSingle point of failure and a maintenance burden; the abstraction can hide what an engineer needs to see when debugging

The skeleton trade-off is the sharpest. A scaffold that emits a frozen snapshot ages the moment it is generated, and apps drift back toward bespoke. The mitigation is to dogfood the skeleton (the platform team builds real apps from it) and to keep config in extended-from-registry form rather than copied, so upgrades flow rather than fork.

Common Pitfalls

Four mistakes recur, each with a concrete fix.

  • Mandatory but not easier. The platform is required yet slower than rolling your own, so teams comply resentfully or route around it with shadow scaffolds. Fix: treat on-ramp time as the headline signal, remove friction before adding governance, and let adoption be voluntary so resistance surfaces as low adoption rather than hidden workarounds.
  • Big-bang platform. Shipping a “complete” platform nobody asked for, designed against assumed needs. Fix: ship the thinnest viable platform and grow it from real pull, the Thinnest Viable Platform idea from Team Topologies.
  • Lockstep upgrades. Forcing every app to the latest package version at once, which destroys the autonomy the semver contract was meant to protect. Fix: semver plus independent adoption, with visibility into how far behind apps run so the platform team can nudge, not force.
  • Platform as a project, not a product. Treating the road as a one-time delivery rather than an ongoing product with internal customers. Fix: platform-as-a-product, with voluntary adoption as the success measure and feature teams as customers whose pull-through is the feedback loop.

The thread through all four is the thesis: a paved road that is mandatory but not actually easier fails, because the only durable proof that the road works is that teams choose it.

Closing

For an organization running many frontend apps with per-team drift accumulating, the thin paved road, a golden-path skeleton, versioned shared packages, and a task CLI, is the right default, and voluntary adoption is the metric that proves it. Reach for a developer portal only once discovery across apps is a cost in itself, and reach for enforcement only where divergence creates regulatory or safety risk. The single next step is to measure on-ramp time for a new app today; if it is days rather than minutes, that gap is the road’s first job.

References

Related posts