The F4 flow model
Where C4 shows how a system is arranged, F4 shows how work moves through it: steps, decisions, and hand-offs, in the order they happen. It's the dynamic counterpart to C4's static structure — the view that answers "how does this actually run?" and "who is responsible when it fails?".
The name F4 comes from Frame, Flow, Form, Function — it's a notation name, not a level system. An F4 flow is flat: it has no zoom hierarchy of its own. Instead it borrows its structure from the C4 model you've already built, and can be read back at any C4 zoom level.
The whole story
A process (say, "Handle a claim") is described by one or more flows. A flow is a set of horizontal lanes — one per actor or system part — holding activities (the steps), connected by arrows in the order things happen. When a connection crosses from one lane to another, that's a hand-off: work changing hands. Read top-left to bottom-right, a flow is the story of one process from trigger to outcome.
graph LR
subgraph Handler["Lane: Claims Handler"]
A1["Open claim"]
A2["Review"]
end
subgraph API["Lane: Claims API"]
B1["Validate"]
B2["Record decision"]
end
subgraph Pay["Lane: Payment Provider"]
C1["Issue payment"]
end
A1 --> A2 --> B1
B1 -->|"Approved"| B2 --> C1
B1 -->|"Rejected"| A2
- A lane is an actor or a part of the system — "Claims Handler", "Claims API", "Payment Provider".
- An activity sits in the lane of whoever performs it, named verb-first: "Validate", "Issue payment".
- A hand-off is a connection that crosses lanes — and that's the key idea below.
- Flows can be split into sections — named horizontal bands that group lanes by phase or theme, each readable on its own.
Every hand-off is a C4 relationship
A hand-off crossing from one lane to another is the dynamic counterpart of a C4 relationship. Lanes (and individual activities) can be bound to C4 elements — a lane that is the Claims API container, an activity that happens inside a specific component. Because flow and structure then come from one shared model, they can't tell two different stories about the same interaction. Bound elements also show up in the C4 side panel, so from any C4 element you can see every flow it participates in.
This is what makes F4 more than a flowchart: it's wired into the same model as your architecture, so behavior and structure stay honest with each other.
Flows hand off, calls answer
A connection comes in two kinds. A flow is the default: control moves on to the target, and the story continues from there. A call is a synchronous ask-and-answer: the target does its work, answers back, and the caller continues to its own next step — drawn as one connection with a solid request arrow out and a dashed return arrow back. Without calls you'd have to route the flow into the callee's lane and draw an artificial path back, which misstates what the code does. With them, "look this up, then proceed" reads exactly as it runs.
One flow, readable at any zoom
You author a flow once, at full detail. The viewer can then re-render it at a coarser C4 zoom level — Context, Container, Component, or full detail (Code) — per section. Lanes bound to fine-grained C4 elements fold up into their parents: several component-level activities merge into one container-level step, with a count showing how much detail is folded away.
This means one authored flow serves every audience: management reads it at Context level, engineers at Component level — and because it's a view of one model rather than separate drawings, the levels can never drift apart. Editing always happens at full detail.
Focus with tags
Activities and lanes can carry tags ("happy-path", "error-handling", "v2"). The viewer's visibility panel lets a reader spotlight chosen tags — either ghosting everything else (dimmed but visible) or hiding it outright. One diagram, many readings.
Tips & tricks
- Name activities verb-first. "Verify payment", not "Payment" — the verb is the step.
- Lanes are roles and systems, never people's names. "Billing", not "Alice" — the process must survive staff changes.
- Watch the lane count. Past six or seven lanes a flow stops being readable; split the process into linked flows instead.
- One abstraction level per flow. Don't mix "Process order" with "Validate ZIP code" in the same flow; push detail into a linked flow.
- Mark where the flow starts and ends so the reader knows the trigger and the outcome.
- Label decision branches with their outcomes ("Approved" / "Rejected") — an unlabeled branch is a guess.
Common pitfalls
- Modeling every micro-step. Show the steps that matter to the reader, not every internal call.
- Unbound lanes everywhere. A flow whose lanes have no C4 binding is just a flowchart — it can silently contradict your structure.
- Treating F4 as a level system. It isn't — flows are flat; zoom is a reading of the flow, not separate diagrams.
- Flows as one-time artifacts. Processes drift; revisit flows when the structure or the process changes.
Where next
- Create a process and lanes — start building one.
- Link flows to your C4 model — bindings and zoom levels in practice.
- The C4 model — the structure these flows run over.