Modeling Application State Before You Code

7 min read

Incidental vs. Accidental Complexity

Before writing a single line of state logic, it helps to understand what kind of complexity you're actually dealing with.

Incidental complexity is unavoidable. It's the irreducible complexity that comes from the problem domain itself the business rules, the data relationships, the flows. It doesn't matter which language, framework, or library you use. This complexity stays constant.

Accidental complexity is self-inflicted. It comes from the implementation choices you make the tools, the patterns, the architecture decisions.

Common sources of accidental complexity in React apps:

  • Multiple useState calls that could be consolidated
  • Chained useEffect hooks that create unpredictable data flows
  • Overly prescriptive library conventions (e.g., old Redux requiring separate files for actions, reducers, constants, and selectors)

The goal of modeling your application is to get a clear view of the incidental complexity the real problem so your implementation doesn't bury it under accidental complexity.


Why Model Your Application? 🎯

Modeling doesn't mean writing UML specs before touching code. It means quick documentation writing down, in the fastest way possible, how your data works and how your flows behave.

This is especially valuable when:

  • Jumping into a legacy codebase with no existing documentation
  • Onboarding teammates to a complex feature
  • Using AI coding assistants (a well-modeled app gives LLMs much better context)

Just like you wouldn't build a house without a blueprint, don't build a complex feature without at least a rough text-level model of its logic.

The recommended approach: maintain a flows.md file in your repo. Plain text is fine. It doesn't need to be a specific format it just needs to be readable by both teammates and AI tools.

Why keep it in the repo (not Notion)?

  1. Teammates and AI coding tools can read it directly as context
  2. It's version-controlled alongside your application code

The Three Essential Diagrams 💡

1. Entity Relationship Diagrams (ERD)

ERDs document your data model what entities exist and how they relate to each other.

This isn't strictly a database concept. ERDs are equally useful for modeling your frontend application's data.

Example: Travel Booking App

Plain text
User Flight Hotel Booking ---- ------ ----- ------- id id id id name origin name user_id email destination location flight_id price price hotel_id date dates status

Relationships:

  • A Booking belongs to one User
  • A Booking references one Flight and one Hotel
  • A User can have many Bookings

Recommended tool: dbdiagram.io — simple DSL, visual output, easy to share.


2. Sequence Diagrams

Sequence diagrams document the flow between actors in your system — UI, services, APIs, external processors.

Example: Airbnb-style booking flow

Plain text
UI → AirbnbSearchService : search(destination, checkIn, checkOut, guests) AirbnbSearchService → UI : results[] UI → UI : selectHome(homeId) UI → AirbnbAPI : createBooking(homeId, userId) AirbnbAPI → UI : paymentInfo UI → PaymentsProcessor : processPayment(paymentInfo) PaymentsProcessor → UI : confirmation

You don't need a diagramming tool for this. Plain text captures the intent. If you want a visual, paste it into Swimlane.io — it has a simple DSL and generates clean diagrams instantly.

Key insight: Sequence diagrams clarify what happens outside your React components — the async calls, the service interactions, the external dependencies.


3. State Diagrams

State diagrams document what your application can do in each state — all the possible paths, transitions, and screens.

Example: Flight booking flow

Plain text
flightSearch → [user submits search] → flightResults flightResults → [user selects flight] → hotelSearch → [user goes back] → flightSearch hotelSearch → [user submits search] → hotelResults → [user goes back] → flightResults hotelResults → [user selects hotel] → review → [user goes back] → hotelSearch review → [user confirms] → payment → [user changes hotel] → hotelSearch → [user changes flight] → flightSearch payment → [payment succeeds] → confirmation confirmation → (no back — terminal state)

What looks like a simple linear flow becomes a directed graph the moment you add real-world requirements like back navigation, optional steps, or branching logic.

A state diagram makes it immediately obvious when a state should be a dead end (like a confirmation screen) vs. when the user should be able to navigate back or branch to a different path.

Recommended tool: Stately.ai paste in an XState machine definition and get an interactive visual. Also supports Mermaid-compatible exports.


Generating Diagrams with AI Tools

You don't need to hand-draw these diagrams. Write your flows in plain language, then ask an LLM to convert them:

Workflow:

  1. Write a rough plain-text description of your flow
  2. Ask: "Convert this to a Mermaid state diagram" or "Make this a sequence diagram in Mermaid"
  3. Paste the output into Mermaid Live for visualization
Plain text
// Plain text input → "Start with destination input. When destination is entered, move to check-in date. Then checkout date. Then number of guests. When user searches, show loading state, then results. User can click a result to see details. User can go back from details to results." // LLM output → valid Mermaid stateDiagram-v2 block

This approach takes minimal effort and produces documentation that is:

  • Visually shareable with your team
  • Readable by AI coding assistants as context
  • Version-controllable in your repo

What to Document in a flows.md File

For each feature or flow, capture two things:

  1. What state the user is in — the current screen, mode, or step
  2. What can happen in that state — the events, transitions, and actions available
MARKDOWN
## Flight Booking Flow ### States - `flightSearch` — user fills in origin, destination, dates - `flightResults` — list of available flights shown - `hotelSearch` — user fills in hotel preferences - `hotelResults` — list of available hotels shown - `review` — summary of selected flight + hotel - `payment` — payment form - `confirmation` — terminal state, booking confirmed ### Transitions - flightSearch → flightResults : user submits search - flightResults → hotelSearch : user selects a flight - hotelResults → review : user selects a hotel - review → flightSearch : user changes flight - review → hotelSearch : user changes hotel - payment → confirmation : payment succeeds

Even this level of documentation no diagrams, no tools meaningfully improves your team's ability to contribute to and debug a feature.


Summary

Diagram TypeWhat It ModelsRecommended Tool
Entity RelationshipData structure and relationshipsdbdiagram.io
SequenceActor-to-actor communication flowSwimlane.io
StateApp behavior, screens, transitionsStately.ai / Mermaid

The three diagrams together answer: What is my data? How does it move? What can happen at each step?

You don't need all three for every feature. But having even one is better than none and the habit of modeling before (or alongside) building consistently produces cleaner state logic.