Skip to main content

Case Study: Scalable Frontend Architecture for a Multi-Team SaaS Platform

Evidence note:

This case study is a sanitized composite based on real multi-team SaaS architecture work. Team names, product details, and exact figures are generalized. Metrics are shown as bounded ranges so the reader can evaluate the outcome honestly without exposing private company data.

1. Context and Constraints

Product Context

  • B2B SaaS platform with marketing pages, authenticated product surfaces, and admin tooling
  • Growth pressure on both product breadth and release frequency
  • Strong need for UX consistency across multiple product areas

Organizational Context

  • 5 frontend teams
  • weekly production releases
  • backend owned by a separate organization
  • no dedicated frontend platform team at the start

Technical Context

  • React + Next.js App Router
  • mixed rendering strategies
  • ad-hoc state management patterns
  • shared utilities growing without clear ownership

2. Baseline Problems

Before proposing a target shape, the architecture review found four repeated failure patterns.

Structural

  • cross-feature imports were common
  • ownership boundaries were hard to explain
  • refactors touched more modules than expected

Data and State

  • backend-owned data was duplicated in client stores
  • cache behavior was inconsistent across teams
  • synchronization bugs were recurring in high-change flows

Performance

  • dashboards were hydrating more UI than necessary
  • bundle growth was not budgeted
  • rendering strategy was chosen by habit rather than route needs

Organizational

  • architectural decisions lived mostly in Slack and memory
  • teams re-litigated the same patterns
  • onboarding to the codebase took too long

3. Architecture Goals

The goals were written before the solution:

  1. reduce future change cost
  2. let teams work in parallel with lower blast radius
  3. make boundaries visible and enforceable
  4. treat performance as a system-level constraint
  5. preserve decisions through team churn

4. Key Decisions

ADR-001: Feature-Based Structure Over Layered Structure

Decision:

  • move to a feature-led organization model with explicit public APIs

Why:

  • locality of change mattered more than maximizing early reuse
  • layered folders were hiding ownership rather than clarifying it

Accepted cost:

  • some early duplication
  • more deliberate governance around boundaries

ADR-002: Modular Monolith Over Micro-Frontends

Decision:

  • keep a single deployable frontend with enforced internal boundaries

Why:

  • teams could still coordinate releases
  • shared UX consistency mattered
  • runtime simplicity was more valuable than theoretical autonomy

Accepted cost:

  • shared release cadence
  • stronger need for tooling and compatibility policy

ADR-003: Server-Owned Data Is Not Global Client State

Decision:

  • treat backend data as server state with explicit cache ownership

Why:

  • duplicated client copies were creating stale-state bugs
  • teams needed clearer ownership and invalidation rules

Accepted cost:

  • more discipline around fetch orchestration
  • more explicit route/data design work up front

ADR-004: Route-Level Rendering Strategy

Decision:

  • choose rendering by route and page segment, not by app-wide ideology

Example:

SurfaceStrategy
marketingSSG or cached SSR
auth and entry flowsSSR
dashboard shellserver-rendered shell plus interactive client regions
reportingSSR with explicit cache strategy

5. Implementation Shape

High-Level Structure

Enforcement

  • import constraints for cross-feature access
  • explicit module entry points
  • route-level rendering decisions documented
  • ADRs and RFCs for durable changes

Governance Additions

  • ownership map by feature and shared surface
  • migration playbooks for boundary changes
  • performance budgets for critical routes

6. Baseline -> Intervention -> Outcome

AreaBaselineInterventionOutcome after two quarters
onboardingnew engineers typically needed 2-3 weeks to navigate major product areas safelyfeature boundaries, ownership map, public APIsonboarding to productive contribution dropped to roughly 1-2 weeks
state bugsrepeated stale-data and sync defects in shared workflowsserver-state rules plus cache ownershipstate-related regressions dropped by an estimated 30-45%
bundle growthbundle size changed without reviewroute budgets plus review gatesgrowth stabilized and budget exceptions became explicit instead of accidental
architectural debaterepeated disagreement over "best" patternsADRs, RFCs, and paved-road defaultsrecurring debates reduced materially, especially in new feature kickoff

7. What Worked

  • teams changed features more safely because boundaries were easier to see
  • performance conversations moved from taste to budgets
  • ownership became easier to explain across product and platform surfaces
  • the architecture became easier to teach because its artifacts existed outside the code

8. What I Would Revisit Today

  • introduce route-level budget enforcement earlier rather than after bundle growth became visible
  • formalize ownership maps before the number of shared utilities accelerated
  • add contract-testing around the most volatile backend dependencies sooner

9. Lessons

  • modular monoliths are often the right default longer than teams expect
  • route-level rendering decisions prevent ideological overreach
  • ownership maps are as important as folder structures
  • architecture only becomes real when teams can use it without asking permission every day