PR Lens

The problem

We drew ten landmark pull requests. Here is what they look like

React Hooks, Node's fetch, Kubernetes Ingress and seven more, run through the PR Lens renderer. 269 changed files and 31,386 added lines came out as 37 lanes and 74 components.

react/react#13968: 36 changed files and 5,868 added lines, drawn as seven components across four lanes.

We ran ten landmark open-source pull requests back through the PR Lens renderer: React Hooks, Node's fetch, Kubernetes Ingress, Vue's <script setup>, Rust's ? operator, Tokio's work-stealing rewrite, Neovim's LSP client, Django's ASGI handler, webpack's ContainerPlugin, and vLLM's PagedAttention V2. Between them they change 269 files, add 31,386 lines and remove 7,801. Drawn, they come to 37 lanes and 74 components. The largest picture in the set has twelve boxes in it. For scale, LinearB's 2026 benchmarks put AI-assisted pull requests at 408 changed lines at the 75th percentile.

How I counted

The file and line numbers are the ones GitHub reports for each pull request, stored in the gallery's manifest alongside the base and head commit for every entry. I did not retype them from memory.

The lane and component numbers I counted out of the rendered SVGs, because the renderer marks them. Every lane heading is emitted as a text element with class="lanelabel", and every component title as one with class="ntitle", so counting those two strings in the file gives you the exact shape of the drawing:

for f in docs/showcase/gallery/*.svg; do
  echo "$f $(grep -c 'class="lanelabel"' "$f") $(grep -c 'class="ntitle' "$f")"
done

This is the same method the PR Lens app uses in its own test suite to check that the size labels printed beside the landing page's renders match what those renders draw. Those labels were wrong the first time they were written, because someone counted them by eye off the picture. Counting the markup is the only way I trust.

Delta counts come from the same place: a new component's card carries card-added, a changed one carries card-modified, and a removed one is drawn as a ghost with its title struck through.

The ten, with their numbers

Pull requestFilesAddedRemovedLanesComponents
react/react#13968, Hooks365,86813047
nodejs/node#41749, lib: add fetch168,0763510
webpack/webpack#10440, ContainerPlugin13567547
neovim/neovim#11336, LSP client155,556134
kubernetes/kubernetes#14175, Ingress8766047
vuejs/core#2532, <script setup>111,08167037
rust-lang/rust#31954, the ? operator263691648
django/django#11209, ASGI handler389314246
vllm-project/vllm#1348, PagedAttention V26764139312
tokio-rs/tokio#1657, work-stealing pool1007,4086,79536

Lanes run from three to five, with a median of four. Components run from four to twelve, with a median of seven.

Diagram size does not follow diff size

Line up the columns and the pattern that is supposed to be there is missing. Tokio's rewrite touches 100 files, more than twice as many as anything else in the set, and draws as three lanes and six components. vLLM's PagedAttention V2 touches six files, the fewest of the ten, and draws twelve components, the most.

Across the ten, the correlation between changed files and component count is -0.33, and between added lines and component count it is -0.14. Ten points is far too few to make anything of a coefficient, so treat those as a description of this table rather than a law. The ordering is what matters, and the ordering is the wrong way round from what everyone expects.

Two hundred and sixty-nine files, and nothing over twelve boxes

Look at the two extremes and the reason becomes obvious.

tokio-rs/tokio#1657: 100 changed files, 7,408 added and 6,795 removed lines, drawn as six components across three lanes.

Tokio's pull request rebuilt the work-stealing thread pool. Structurally it is one substitution: the runtime's entry points stop delegating to the old thread pool and start delegating to a new one, and the old implementation is struck through. One substitution in the system, a hundred files in the source. Tokio and vLLM are also the only two entries in the set that draw a removed component: one here, two there.

vllm-project/vllm#1348: six changed files, drawn as twelve components across three lanes, the most in the set.

vLLM went the other way. Six files, 764 added lines, and inside them a V1 kernel path, a new V2 path, a reduction kernel, the pybind bindings that expose them, a shared get_alibi_slopes() hook, a bfloat16 helper, a test, a benchmark and the two kernels it retired. Twelve components in six files, which is a change that reads as small in the diff and draws as the largest picture in the set.

What the colours say

Across all ten renders there are 36 new components, 26 changed ones, and 3 removed. The remaining nine are unchanged parts of the system, drawn because the change needed something to sit against.

Two entries are interesting for what they lack. Vue's <script setup> pull request draws no new components at all: six of its seven boxes are amber, and the seventh is untouched context. It is 1,081 added and 670 removed lines of rework inside an existing compiler, and the picture says so at a glance. webpack's ContainerPlugin is the mirror image, four new components and nothing modified, with three untouched pieces of the compiler core sitting around them to show where the new plugin plugs in.

nodejs/node#41749: 8,076 added lines, the most in the set, drawn as ten components across five lanes.

Node's fetch is the best illustration of why line counts mislead. It has the largest addition count of the ten by a clear margin, and one of the ten boxes on its diagram is a vendored copy of the undici HTTP client. A dependency copied wholesale into a repository is thousands of lines and one component. There is no amount of reading the diff that would make that clearer than the box does.

What size still reads as one picture

Every one of the ten fits in five lanes or fewer. None of them needs a second screen or a zoom to be legible in a pull request comment, which was the constraint the renderer was designed around.

The ceiling is higher than any of these. The renderer's own size ladder tops out at a synthetic monorepo change with six lanes and thirty-seven components, and it is still readable, if not something anyone should open as a single pull request. One renderer, three sizes walks that ladder from a one-line fix upwards.

Which puts the ten landmark pull requests in perspective. Hooks, fetch and Ingress, changes that reshaped their ecosystems, all sit comfortably in the middle of the range. If those fit in one picture, the agent-written pull request in your queue almost certainly does too.

Why this matters for the pull requests you get today

The reason I ran this exercise was to check an assumption behind PR Lens: that a change worth reading has a structure small enough to see, no matter how many lines it moved. The table says yes for ten famous changes, and none of them were written to be easy to draw.

Agent-written pull requests fail readers for a different reason than size, which I have written about in agent pull requests lose the narrative. They arrive with everything touched at once and no order to read it in. The finding here is that the structural size of a change is nearly always manageable even when its line count is not, and structure is the thing you can hand a reviewer in one image. For where that fits in a full review method, the pillar for this cluster is reviewing AI-generated pull requests.

Every diagram in the table is live at the Hall of Fame, animated, with the pull request linked from each one. The SVGs themselves are in the open source repository under docs/showcase/gallery, so the counts above are reproducible with the two grep calls at the top of this post.

Questions people ask

What did the React Hooks pull request actually change?

react/react#13968 changed 36 files, adding 5,868 lines and removing 130. Drawn, it is seven components across four lanes: the public Hooks API in the React package, a dispatcher and a hook state machine in the reconciler, a partial renderer and a server-side dispatcher in the server renderer, and the feature flags in shared config that gate all of it.

How big does a pull request have to be before a diagram stops helping?

None of these ten hit the limit. The largest, Tokio's work-stealing rewrite at 100 changed files, drew as three lanes and six components. The PR Lens renderer handles six lanes and thirty-seven components on a synthetic monorepo change, which is larger than any real pull request in this set.

How were the lane and component counts measured?

By counting markup in the rendered SVGs rather than by eye. Each lane label is emitted with class lanelabel and each component title with class ntitle, so counting those two strings gives the shape of the drawing. This is the same method the PR Lens app's own test uses to check the size labels on its landing page.

Do bigger diffs produce bigger diagrams?

Not in this set. Across the ten, the correlation between changed files and component count is -0.33, and between added lines and component count it is -0.14. With ten data points neither number means much on its own, but the ordering does: the pull request with the most files drew one of the smallest pictures.

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.

Add to GitHub