Case Study: Scalable Frontend Architecture for a Multi-Team SaaS Platform
1. Context & Constraints
Product Context
-
B2B SaaS platform with:
-
SEO-critical marketing pages
-
Authenticated dashboard
-
Admin & reporting tools
-
-
Users expect:
-
fast initial load
-
responsive interactions
-
consistent UI across features
-
Team & Org Constraints
-
5 frontend teams
-
Weekly production releases
-
Shared design system
-
Backend owned by a separate team
-
No dedicated frontend platform team initially
Technical Constraints
-
React + Next.js (App Router)
-
Existing codebase with:
-
mixed rendering strategies
-
ad-hoc state management
-
shared utilities growing uncontrollably
-
2. Problems Identified
Before proposing solutions, I analyzed systemic pain points:
Structural Problems
-
Feature code tightly coupled through shared utilities
-
No clear ownership boundaries
-
High refactor risk
Data & State Issues
-
Server data duplicated in global client state
-
Inconsistent caching rules
-
Frequent data synchronization bugs
Performance Issues
-
Hydration cost across entire app
-
Bundle size growing without budget
-
No objective performance metrics
Organizational Issues
-
Architectural decisions undocumented
-
Repeated debates about “best” patterns
-
Onboarding new engineers was slow and error-prone
3. Architectural Goals
I defined explicit goals before designing anything:
-
Reduce future change cost
-
Enable multiple teams to work safely in parallel
-
Make architecture visible and enforceable
-
Treat performance as a system constraint
-
Encode decisions so they survive team changes
4. Key Architectural Decisions (ADRs)
ADR-001: Feature-Based Architecture over Layered Architecture
Decision
Adopt a feature-based architecture as the primary organizing principle.
Alternatives Considered
-
Layered (
components,hooks,services) -
Domain-only structure
Why Rejected
-
Layered structure optimized reuse, not change
-
Encouraged cross-feature coupling
Trade-offs
-
Some duplication accepted early
-
Requires discipline around boundaries
ADR-002: Modular Monolith over Micro-Frontends
Decision
Use a modular monolith with enforced boundaries instead of micro-frontends.
Why
-
Teams were small enough to coordinate releases
-
Micro-frontends would add runtime and performance complexity
-
Shared UX consistency was critical
Trade-offs
-
Shared release cadence
-
Requires strong governance
ADR-003: Server State Is Not Client State
Decision
All backend-owned data is treated as server state, not global client state.
Rules Introduced
-
Server data fetched & cached via a dedicated data layer
-
Client global state limited to:
-
auth session
-
feature flags
-
theme
-
Benefits
-
Eliminated duplicated sources of truth
-
Reduced state-related bugs
ADR-004: Route-Level Rendering Strategy
Decision
Rendering strategy chosen per route, not globally.
Route Type
Strategy
Marketing pages
SSG
Auth pages
SSR
Dashboard
CSR + streaming
Reports
SSR + caching
Trade-offs
-
Slightly higher architectural complexity
-
Significantly better performance predictability
5. System Design Overview
High-Level Structure
App Shell
├── Features
│ ├── Auth
│ ├── Billing
│ ├── Dashboard
│ └──Admin
├── Entities
│ ├──User
│ ├── Organization
│ └──Subscription
├── Shared
│ ├── UI Primitives
│ ├── Design Tokens
│ ├── Platform Utilities
Dependency Rules
-
Features may depend on Entities
-
Entities never depend on Features
-
Shared depends on nothing
-
No cross-feature imports allowed
These rules were enforced via linting and TypeScript path constraints.
6. Design System & UI Architecture
Design System Principles
-
Token-first (semantic tokens, not raw values)
-
Accessible by default
-
Composition over configuration
-
Versioned with deprecation policies
Component API Example
Instead of:
<Button color="blue" size="14px" />
We standardized:
<Button intent="primary" size="md" />
This:
-
prevented invalid combinations
-
simplified theming
-
enforced accessibility defaults
7. Performance Architecture
Performance Budgets Defined
- LCP ≤ 2.5s
- INP ≤ 200ms
- Initial JS ≤ 170KB (gzipped)
- CLS ≤ 0.1
Architectural Measures
-
Partial hydration for dashboard widgets
-
Server components for data orchestration
-
Third-party scripts isolated and deferred
-
Bundle size monitored in CI
Key Shift
Performance discussions moved from opinions to budget trade-offs.
8. Reliability & Error Architecture
Error Taxonomy Introduced
-
User errors (validation, permissions)
-
Network errors
-
Server errors
-
System errors
Error Boundary Strategy
-
Feature-level isolation
-
Local recovery where possible
-
Global fallback only for fatal errors
This reduced:
-
blank screens
-
global crashes
-
debugging ambiguity
9. Governance & Team Enablement
Architecture Artifacts Created
-
Architecture Decision Records (ADRs)
-
Frontend architecture standards
-
Design system governance docs
-
Migration playbooks
Enablement Tactics
-
Code generators for new features
-
Architecture office hours
-
RFC-based change proposals
Outcome
Teams followed architecture because it was easy and safe, not because it was mandated.
10. Outcomes & Impact
Quantitative
-
Reduced onboarding time for new engineers
-
Fewer regressions related to state and rendering
-
Stabilized bundle size growth
-
Improved Core Web Vitals consistency
Qualitative
-
Fewer architectural debates
-
Clear ownership boundaries
-
Higher confidence in refactoring
-
Improved collaboration with backend and design
11. What I Would Revisit Today
With hindsight:
-
Introduce streaming earlier for data-heavy dashboards
-
Formalize ownership maps sooner
-
Automate more architectural checks earlier
This reflects learning, not regret.
12. Key Takeaways
-
Frontend architecture is about durable decisions
-
Boundaries matter more than abstractions
-
Performance must be budgeted, not optimized later
-
Governance enables speed when done right
-
Architecture succeeds only if teams trust it
Why This Case Study Works in a Portfolio
It demonstrates:
-
System-level thinking
-
Trade-off awareness
-
Real-world constraints
-
Leadership without authority
-
Responsibility beyond code