Diagrams
One renderer, three sizes: a one-file fix, a feature, a monorepo refactor
The same renderer draws a one-line change and a thirty-seven component monorepo refactor. Here is what changes between those two pictures, and what deliberately does not.
The same renderer has to answer a one-line fix and a refactor that lands in six packages, or it is a toy. LinearB's 2026 benchmarks, from 8.1 million pull requests across more than 4,800 organisations, put AI-assisted pull requests at 408 changed lines at the 75th percentile against 157 unassisted, and the shape of those changes is wide as well as long: Salesforce Engineering describes agent pull requests spanning backend, config, tests and UI in one go. So the question worth asking about a diagramming tool is what its picture does at the third size up, once the change stops fitting on a screen.
Here is the ladder we hold ourselves to, three renders from the same code path.
Rung one: one lane, one component
A single endpoint changed. One lane, one card, one amber badge.
The temptation with a change this small is to pad the diagram until it looks like it was worth generating: add the router, add the test file, add the deploy pipeline. Every one of those makes the picture worse, because now a reader has to work out which of the five boxes is the point.
A diagram that says one thing and stops is doing its job. Most of the discipline in a renderer is in the small cases, where there is nothing to hide behind.
Rung two: three lanes, fifteen components
A feature. Three lanes hold it: UI, core, infrastructure. Fifteen components sit in them, of which four are added, five are modified, two are removed and four were never touched by the diff.
This is the size where lanes start earning their keep. Without them, fifteen cards is a cloud. With them, you can read the change as a sentence: the endpoint moved, the work behind it is new, the old poller and its queue are going, and the dashboard is not involved.
It is also the size where the untouched cards become load-bearing. Dashboard, authorize, recordMetrics and Object storage carry no badge, and those four plain cards are the fastest thing in the review: they tell you which parts of the system you can leave closed. More on that in blast radius diagrams.
Rung three: six lanes, thirty-seven components
The render at the top of this post. Six lanes for web apps, API, services, packages, data and external systems. Thirty-seven components: thirteen added, ten modified, three removed, eleven untouched.
Nobody holds that change in their head from a file list. The diagram does not make it small, and it does not pretend to. What it does is answer the questions you can answer from shape alone, in about the time it takes to look. The change adds a document service, a search indexer and an outbox, and retires a v1 REST surface, a legacy sync and an XML exporter. It reaches OpenSearch and OpenAI, which are new. It does not reach Stripe, Sentry, the identity provider or the warehouse.
That last sentence is the one a reviewer most wants before deciding how carefully to read, and establishing it from a file list of that size is most of an afternoon.
Where those counts come from
Lane and component counts in this post are read off the shipped SVG files rather than estimated: the lane labels and the node titles in the rendered output are counted directly, and a test in the landing page's suite holds the numbers on the page to the assets it ships. Counting boxes in a picture by eye is how they went wrong the first time we wrote them down.
What changes between the three, and what does not
Between rung one and rung three, four things scale and one thing deliberately does not.
Lanes carry the growth. A component belongs to exactly one lane, so widening a change adds cards inside existing columns before it adds columns.
Depth is counted within a lane, not across the diagram. A lane that only enters the story late still starts at the top of its column, and a rank no card occupies leaves no empty row behind. Without that rule, a wide diagram becomes mostly whitespace.
Every lane is the same fixed width, and this is the one that does not scale. The width is a constant rather than something derived from the widest label a lane holds. A content-derived width couples every column to the ones before it, so a single long node name in the first lane would slide every card in every later lane sideways, and the diagram of push two would not look like the diagram of push one. A lane holding something narrow is wider than it needs to be, and that is the price.
Animation stays constant too. In a data-flow view the animated steps share one cycle and take it in turn: one dot crossing one arrow at a time, in the order the steps happen, with the next arrow lighting as the last dot lands. A seven-step flow and a two-step flow both play at that rhythm, so a longer sequence takes longer rather than moving faster.
Delta colour stays exactly the same vocabulary at every size: green new, amber changed, red gone with a strike-through, neutral for untouched. Thirty-seven components with four colours is legible. Thirty-seven components with a legend would not be.
Past thirty-seven, the answer is nesting
A renderer that only works on small changes is a renderer you cannot use on the pull request that actually scared you. There is still a ceiling, and the way past it is nesting.
A graph document carries a nested tree of drill-down views, each scoped to one part of the change. That tree is what the pull request comment is built from: the whole blast radius on top, then the new path, then what was retired, each in its own collapsible section with its own diagram. A reader picks their depth. The contract caps the tree at 128 views, because a render is one asset per view per theme and a manifest carries 256 assets, so a document with more views than a render could describe is refused at the contract rather than discovered later.
There is a practical constraint underneath all of this. A GitHub comment column is about 830 pixels wide, and a six-lane system is several times that, so a wide diagram arrives scaled to fit with one click giving you the size the labels were drawn at. For anything genuinely large, the comment links to the canvas, where the diagrams are full size with pan, zoom and a theme of your choosing.
Making the big one right for your repository
The model picks the lanes, and on an unfamiliar monorepo it sometimes picks them by folder when a runtime boundary would have read better. That is fixable without touching generated output. A .github/pr-lens.yml in the repository holds corrections: rename a node, pin one to a lane, group several under a sub-cluster, exclude a path.
schemaVersion: 0.1.0
map:
rename:
- match: functions/src/broadcast/sendBroadcastBulk.ts
to: Broadcast sender
exclude:
- "**/*.test.ts"
lane:
- match: packages/broadcast-lib/**
lane: functionsA match beginning with id: addresses one node exactly; anything else is a path glob over the files backing a node, so the correction survives the model renaming that node between runs. Corrections are an overlay applied over fresh inference every time, and inference never writes back into the file. On a monorepo, three or four lane pins is usually the whole configuration you ever need.
The reason the model can be corrected this cheaply is that it never draws anything: it writes a typed document and the renderer owns the geometry. Why the model should describe the graph and never draw it covers that split, and how to visualise a pull request as a diagram ranks the alternatives, including the ones where the picture is the model's own output.
Questions people ask
How do you diagram a monorepo-wide change?
- Group the changed components into lanes that match a boundary a reader already holds, such as a runtime or a tier, and keep the untouched neighbours the change reaches. Then split the diagram into a top-level view plus nested views scoped to one part of the change each, so the reader chooses their depth instead of getting one enormous picture.
How big a change can fit in one diagram?
- The largest render we ship is thirty-seven components across six lanes, and that is the size we design the layout rules against. Past it, the move is a nested diagram rather than a bigger one: an overview that says where the change lands, with drill-down views for the parts worth following.
Do lanes have to match the folder structure?
- No, and they usually should not. Lanes are the reader's mental model of the system: a runtime, a tier, a boundary. A monorepo's folder tree is one candidate for that, and the app, service and data split is often a better one.
What if the diagram groups things wrongly for my repository?
- Commit a .github/pr-lens.yml with corrections. You can rename a node, pin one to a lane, group several under a sub-cluster, or exclude a path entirely. Corrections are matched by node id or by a glob over the files backing a node, and they are applied as an overlay on every run rather than written back into inference.
Sources
Keep reading
Turn it on
Install the App and every pull request in the repositories you pick gets drawn. Or hand the prompt to the agent you already have open.