Skip to content

Systems & architecture

Frontend architecture that lasts

Treat the app as a system: state machines, an error-handling strategy, offline-first sync and feature flags — the decisions that keep a codebase healthy as it grows.

5 parts30 min readUpdated Jan 17, 2026

Parts

  1. Part 1

    Think in Systems, Not Screens

    Frontend apps break when we design them page by page. A short field guide to mapping state, data flow and ownership before drawing the UI.

    2 min

  2. Part 2

    State Machines for UI: Making Impossible States Impossible

    Why isLoading, isError and data living side by side breed bugs, how to model UI as states and events, a typed useReducer state machine for a submit flow, and when a statechart library is worth it.

    6 min

  3. Part 3

    A Frontend Error-Handling Strategy: From Error Boundaries to Retries and Reporting

    Classify errors before handling them, understand what error boundaries do and don't catch, retry properly with backoff and jitter, separate user-facing messages from developer logs, and report errors without leaking personal data.

    6 min

  4. Part 4

    Offline-First and Optimistic UI: Designing Sync That Never Loses Data

    Optimistic updates with rollback, an IndexedDB-backed outbox, idempotency keys, retrying when the network returns, conflict resolution strategies, showing sync status and testing offline scenarios.

    9 min

  5. Part 5

    Feature Flags in the Frontend: Types, Evaluation, Rollout and Cleanup

    Feature flags decouple deploy from release, but used carelessly they turn into tech debt and flickering UIs. This post covers the four kinds of flags, evaluating on the server vs the client, typed flag definitions in TypeScript, gradual rollout, A/B testing and a cleanup process.

    7 min

Series review

0/10 answered · 0 correct

10 questions drawn from the parts of this series. Answers here count toward each post's own quiz.

  1. Question 1 of 10IntermediateThe list page fetches users, the detail page fetches that same user again, and the header fetches the current user once more. What is the core problem with this design and the fix?
  2. Question 2 of 10IntermediateWhere should a list filter, the text currently being typed in a field, and the order list fetched from an API live?
  3. Question 3 of 10StarterA component uses three flags: isLoading, isError and data. What is the core problem with this approach?
  4. Question 4 of 10IntermediateIn the post's approach the reducer never calls an API. Where does the request run, and why is this design sound?
  5. Question 5 of 10IntermediateWhich of the following errors is NOT caught by a React error boundary?
  6. Question 6 of 10IntermediateWhy does retry need jitter (randomness) on top of exponential backoff?
  7. Question 7 of 10StarterWhat is the core difference between optimistic UI and offline-first?
  8. Question 8 of 10IntermediateWhy does each outbox mutation need an idempotency key when retrying?
  9. Question 9 of 10IntermediateThe rule "Pro customers see advanced reports" is permanent. How does the post say to handle it?
  10. Question 10 of 10IntermediateIn the Next.js App Router, why evaluate flags once on the server and bootstrap them to the client through a provider?

Other series

Series