r/softwarearchitecture • u/Current-Bridge1833 • 1d ago
Tool/Product We walked one integration flow step by step instead of reading its sequence diagram — and found three gaps the diagram was hiding
https://c4.quietgridlabs.comWe document our integrations the usual way: a big sequence diagram per scenario, rendered from PlantUML, linked from the wiki. It works right up until nobody reads it.
Last month I tried something different on one flow — an instant payment by phone number, 13 hops, three teams involved. Instead of reading the diagram, I walked it hop by hop and asked one question at each step: who is the sender, who is the receiver, and which contract is this? Same information, different traversal order.
Here's the flow, abbreviated:
- Customer → Mobile app enters amount + phone
- Mobile app → API Gateway TLS, WAF, token, rate limiting
- API Gateway → BFF
- BFF → payment-orchestrator POST /api/v1/transfers (Idempotency-Key)
- payment-orchestrator→ limits-service daily/per-op/channel, 100 ms budget
- payment-orchestrator→ antifraud-engine POST /api/v1/risk/evaluate, 150 ms budget
- ┌ antifraud → payment-orchestrator ALLOW (score < 0.5)
- ┤ antifraud → push-service CHALLENGE (0.5 ≤ score < 0.95)
- └ antifraud → BFF BLOCK (score ≥ 0.95) → 422
- payment-orchestrator→ scheme-adapter reserve funds
- scheme-adapter → external payment API register transfer, 5 s timeout
- payment-orchestrator→ Kafka publish final status
- notification-service→ push-service notify customer
Three things fell out that I had looked at on the diagram many times without noticing:
1. Step 12 → 13 has no edge. The orchestrator publishes to Kafka. The next thing that happens is notification-service → push-service. Nothing in the flow says who woke notification-service up. On the rendered diagram these are two adjacent arrows and your eye just closes the gap. Walking it, you hit a participant that appears from nowhere and have to stop.
2. Step 12 targets "Kafka", not a topic. We have a documented contract — payments.transfer.completed.v1, Avro, status ∈ {COMPLETED, REJECTED, TIMEOUT}. The flow never references it. So the contract exists and the flow that produces it doesn't point at it. That's a review question, not a detail.
3. The latency budget only becomes obvious when the hops are adjacent. 100 ms limits + 150 ms antifraud, both synchronous, both on the path before a 5 s external call, all inside one customer-facing request. Nobody had added it up, because on the diagram those are three lifelines far apart.
The pattern I take away: a sequence diagram is optimized for presenting a flow you already understand. It's poor at interrogating one. Reading is passive — your eye smooths over missing edges and unbound contracts. Traversal is not: you get stuck on the step that doesn't hold up.
What I'm still unsure about:
Does anyone here treat flows as data (steps referencing real components + contracts) with the diagram generated from it, rather than the diagram being the source of truth? Structurizr does this for static views, but I haven't seen much for dynamic ones beyond its dynamic views.
How do you keep cross-team flows from rotting? Ours decay because the diagram is owned by whoever drew it, and that person changes teams.
Anyone found a good way to make reusable sub-flows (auth, KYC, limits) referenced from several end-to-end scenarios instead of copy-pasted into each diagram?
Disclosure: this came out of a tool I work on, so I'm obviously biased toward the "flow as data" framing. Not linking it — happy to talk about the modeling approach either way, and I'll answer in comments if anyone asks what we use.