---
title: 'Change Size'
source: 'https://academia.sh/en/courses/code-review/change-size'
course: 'Code Review and Team Process'
language: en
updated: '2026-08-17T18:10:46+00:00'
license: 'CC BY-SA 4.0'
---

# Change Size

Attention is fixed at 12 chunks; the same 24 defects are found 22 times at 100 lines, 18 times at 900 lines, 2 times at 2400 lines, and 36 of 2400 lines' chunks go unread.

The previous lesson handed the style axis off to a tool and measured attention shifting
to another axis. The pattern built across the topic was this: adding axes raised the
number of defects found. A single reviewer looking at five axes found what five reviewers
found; what grew the number was not who looked, but which axis was looked at.

This lesson's question comes from the opposite direction. Let the axis count stay fixed
and let **what is being looked at grow**. Same reviewer, same five axes, same defects —
only the change is 2400 lines instead of 100. With axis diversity left entirely intact,
what happens to the number found?

## Attention Is a Budget

The shared fixture's reviewer works with two limits. The first is **axis**: they see only
the defects on the axes they look at. The second is **attention**: the number of chunks
actually read in one review round is limited. A chunk is 50 lines, attention is 12
chunks. This means roughly 600 lines are actually read in one sitting.

Attention being a budget is the fixture's most important assumption, and it agrees with
observation. A reviewer does not read a change at the same intensity start to finish;
first chunks are read carefully, later ones are scanned faster and faster, and past a
point reading gives way to approval. The fixture hardens this decline into a binary: the
first 12 chunks are read, the rest are not. The hardening does not exaggerate the number,
since chunks that stop getting read in reality are not completely blind either — but it
makes the pattern visible.

The first consequence of this is arithmetic. If the change is under 600 lines, every
chunk is read and the attention limit never engages; the only thing limiting is the axis
list. Once the change passes 600 lines, **every added chunk goes into the unread set.**
Attention does not grow; the change does.

## Same Defects, Four Sizes

The constraint that makes the measurement meaningful is this: all four sizes carry the
**same 24 defects**. The fixture does not grow the defect count along with the change's
size. This is deliberate. If the defect count also grew, a falling ratio would not be
surprising; held constant, the ratio's drop has exactly one source — the unread chunk.

The class distribution is also the same at all four sizes: interface 3, implementation 6,
test 3, documentation 3, style 7, and **unwritten requirement 2**. The sixth class is the
class no axis searches for: the defect of what was never written. Review looks inside the
submitted change; it cannot see the absence of what was never submitted. This lesson
shows those two defects separately in the missed column, because they keep going missed
regardless of size and are not the cause of the drop.

The only thing that changes is the line count. Line count sets the chunk count, and chunk
count sets the area defects are spread over. Defects are scattered across this area;
since attention stays fixed, as the scattering area grows, an increasing share of defects
falls into unread chunks.

The measurement's assumptions:

- **RC1** — All four sizes are generated from the same shared fixture: 24 defects, the
  same class distribution, the same oracle. Since we placed the defects ourselves, which
  chunk each one sits in is known.
- **RC2** — The only thing that changes is the line count. The axis assignment is fixed:
  one reviewer, all five axes. Attention is fixed: 12 chunks.
- **RC3** — Reading starts at the first chunk and stops once attention runs out. A defect
  in an unread chunk goes unseen no matter which axis its class is on.
- **RC4** — In the second measurement, `review` takes a starting chunk; this changes
  **where** the attention limit is placed, not the size of attention itself. Every window
  still reads 12 chunks.
- **RC5** — Splitting does not redistribute defects. Same 2400 lines, same 48 chunks,
  same 24 defects; only the reading happens in four separate windows.
- **RC6** — The set's resolution is **1/24**, that is, **0.042**, at the defect level;
  **1/12** at the chunk level. A smaller difference cannot be defended with this set.

## Measurement

```python
"""Change size: same 24 defects, four sizes, fixed attention.

Part 1 - unread chunks stay fixed as size grows, found ratio falls.
Part 2 - what changes when the same 2400 lines are read in four windows.
"""
SEED = 20260815
AXES = ("interface", "implementation", "test", "documentation", "style")
UNWRITTEN = "unwritten requirement"
CLASSES = AXES + (UNWRITTEN,)
ATTENTION = 12       # chunks actually read in one round
CHUNK = 50            # lines
SCALES = (100, 300, 900, 2400)


def rng(seed):
    d = seed % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def change(lines, defect_count=24, seed=SEED):
    r, defects = rng(seed), []
    chunk_count = max(1, lines // CHUNK)
    for i in range(defect_count):
        defects.append({"no": i + 1, "class": CLASSES[r(6)],
                         "chunk": r(chunk_count)})
    return {"lines": lines, "chunks": chunk_count, "defects": defects}


def review(d, axes, start=0, attention=ATTENTION):
    """A reviewer starts at the given chunk and reads up to attention chunks."""
    read = set(range(start, min(start + attention, d["chunks"])))
    return {k["no"] for k in d["defects"]
            if k["class"] in axes and k["chunk"] in read}


FULL = set(AXES)
d0 = change(600)
distribution = {s: sum(1 for k in d0["defects"] if k["class"] == s) for s in CLASSES}
print("class distribution: " + ", ".join(f"{s} {n}" for s, n in distribution.items()))
print()
print(f"{'lines':>6s} {'chunks':>6s} {'read':>6s} {'unread':>7s} "
      f"{'found':>6s} {'missed':>6s} {'unwritten missed':>17s} {'ratio':>6s}")
for lines in SCALES:
    d = change(lines)
    b = review(d, FULL)
    missed = [k for k in d["defects"] if k["no"] not in b]
    unw = sum(1 for k in missed if k["class"] == UNWRITTEN)
    print(f"{lines:6d} {d['chunks']:6d} {min(ATTENTION, d['chunks']):6d} "
          f"{max(0, d['chunks'] - ATTENTION):7d} {len(b):6d} {len(missed):6d} "
          f"{unw:17d} {len(b) / len(d['defects']):6.3f}")

print()
big = change(2400)
total = set()
print(f"{'pass':>5s} {'chunk range':>12s} {'found':>6s}")
for i, start in enumerate(range(0, big["chunks"], ATTENTION)):
    b = review(big, FULL, start)
    total |= b
    print(f"{i + 1:5d} {f'{start}-{start + ATTENTION - 1}':>12s} {len(b):6d}")
missed = [k for k in big["defects"] if k["no"] not in total]
print(f"four-pass total: found {len(total)}, missed {len(missed)}, "
      f"ratio {len(total) / 24:.3f}")
print("missed classes: " + ", ".join(sorted({k["class"] for k in missed})))
```

```
class distribution: interface 3, implementation 6, test 3, documentation 3, style 7, unwritten requirement 2

 lines chunks   read  unread  found missed  unwritten missed  ratio
   100      2      2       0     22      2                 2  0.917
   300      6      6       0     22      2                 2  0.917
   900     18     12       6     18      6                 2  0.750
  2400     48     12      36      2     22                 2  0.083

 pass  chunk range  found
    1         0-11      2
    2        12-23     12
    3        24-35      2
    4        36-47      6
four-pass total: found 22, missed 2, ratio 0.917
missed classes: unwritten requirement
```

## Where the Ratio Collapses

The top table's first two rows are boring, and they should be. At 100 lines there are 2
chunks, at 300 there are 6; both are under attention, and unread chunks are **0** in
both. Found is **22**, missed is **2**, and both of the missed are **unwritten
requirement**. That is, as long as the change stays under attention, size has no effect
at all; the only thing missed is already the class no axis searches for.

In the third row, the limit engages. 900 lines is 18 chunks, 12 are read, **6 chunks go
unread**. Found drops to **18**, the ratio becomes **0.750**. Of the **6** missed, **2**
are unwritten requirement, and the remaining **4** are defects that were readable but
were not read. By class, these sit on axes the reviewer is looking at — implementation
and style. They are on the reviewer's list, they are in the oracle, only not in front of
their eyes.

The fourth row is the collapse. 2400 lines is **48 chunks** and attention is still 12;
**36 chunks go entirely unread.** Found drops to **2**, the ratio becomes **0.083**.
Defect count did not change, class distribution did not change, the reviewer's axis list
did not change. Only the change grew.

The class list in the missed column also widens in this row. At 900 lines, the missed
were spread across two axes; at 2400, the **22** missed spread across all six classes at
once. The loss from closing off an axis is confined to that axis and it is known where it
looks; the loss from an unread chunk is **independent of axis** and where it falls is
unknown. The two losses are not the same: the first is a **decision**, the second is an
**accident**.

The difference between the two rows fits one sentence: the ratio tracks read chunks over
total chunks. At 900 lines, 12/18 is read and the ratio falls to 0.750; at 2400, 12/48 is
read and it falls to 0.083. Falling below **12/48 = 0.250** itself is because defects are
not spread evenly across chunks: the first 12 chunks hold only **3** defects, and one is
unwritten requirement. In a large change, where the reviewer happens to stop becomes a
gamble.

Whether the differences sit inside the measurement's band also needs checking. The drop
from 22 to 18 is a difference of **4/24 = 0.167**; since the set's resolution is 0.042,
this is four steps above it and defensible. The drop from 18 to 2 is **16/24 = 0.667**
and needs no argument. In contrast, the difference between 100 and 300 lines is **0** —
the set has nothing to say, and saying it has nothing to say is itself a result.

## Where a Large Change Comes From

The numbers show a limit, but they do not show why it gets crossed. No one writes a
2400-line change in one sitting; that size **accumulates**. There are three common
sources of accumulation, and all three are about **flow**, not writing.

The first is **branch lifetime**. The distinction built in the Branching and
Collaboration course turns into a number here: a short-lived branch produces a small
change, a long-lived branch grows a little every day and by review time is not a single
purpose but a week's total. The second is **mixed classes**: renaming, moving files, and
format fixes arrive in the same change as a behavior change. The third is **generated
files**: a dependency lock file, a compiled output, or a regenerable table growing the
line count while carrying nothing inside it a reviewer can decide on.

All three share the same consequence, and it is the measurement's fourth row: attention
stays the same, chunk count grows. The remedy for all three is given not during review
but **before** it — shortening branch lifetime, separating classes, keeping generated
files out of review. Once 48 chunks land on the review table, only one thing can still be
done: send it back.

## Splitting Does Not Shrink Lines, It Grows Read Chunks

The bottom table reads the same 2400 lines in four windows. The change was not split,
defects were not rescattered, attention was not grown — reading was done four times, each
time through a different 12-chunk window. The result: **2 + 12 + 2 + 6 = 22** found,
**2** missed, ratio **0.917**.

This is the **same** ratio as the 100-line change. What determines the number found is
not the change's line count but the number of chunks read. When 2400 lines are read in
four windows, all 48 of the 48 chunks get read and none go unread. The **2** missed
defects are still the unwritten requirement class, and splitting does not touch them: no
window shows what was never written.

The imbalance between windows is also instructive. The first window yields only **2**,
the second **12** defects. The window read in a single pass is the poorest window in
defect terms. A reviewer reading the change in one round has no way of knowing this;
which chunks are dense is learned only after reading all of them.

Splitting's cost does not show in the number, but it is real: four windows mean four
separate readings. This cost gets paid later in this topic, as wait time and review round
count. What needs recording here is that the cost **exists** and is **predictable** — the
price of finding 22 defects is splitting the reading into four.

## The Limit of a Reviewable Change

The measurement gives a practical limit: a change is reviewable when it fits into as many
chunks as the reviewer's attention. With the fixture's numbers, this is roughly 600
lines. The number itself is the fixture's assumption and varies team to team; what does
not vary is that a **limit exists**, and that crossing it makes the ratio drop
**silently**.

**Silently** is the key word here. An unread chunk produces no feedback. A review of a
2400-line change also gets approved, it also closes, and someone looking from outside
cannot tell the two reviews apart. **Approval is not proof of having been read.** The
measurement's **22** missed defects also entered the code right after an approved review.

The way to keep within the limit is to split the change, but splitting cannot be
arbitrary. Each piece must be understandable on its own and correct on its own; otherwise
the reviewer reads the piece, does not understand what it does, and waits for the next
piece — another name for deferring the reading. The workable splitting criteria are:
**single purpose** (a piece does one thing), **standalone correctness** (the system does
not break if the piece merges alone), and **separating moved from changed** (renaming,
moving files, and format fixes stay out of a behavior change).

The last criterion pays off the most. If a format fix grows a change by 300 lines, those
300 lines are 6 chunks, and 6 chunks of attention have been stolen from the behavior
change. The previous lesson handed the style axis off to a tool; this lesson shows the
same handoff's second payoff — the axis handed off removes not only the discussion but
the **chunk** from review too.

There are also changes that cannot be split. If a single interface's name is changing and
appears across two thousand lines, breaking it into pieces leaves every piece broken on
its own — the first criterion collides with the second. What can be done here is not
splitting but **declaring a reading order**: the author writes which chunks of the change
need a decision and which repeat the same pattern. The bottom table shows why this
works — two of the four windows yield 2 defects, one yields 12, one yields 6. Marked
chunks would keep the reviewer's first 12 chunks from being a gamble.

This declaration is not an accountability tool and should not be used as one. What it
measures is not the person's attention but the **change's readability**: the same 48
chunks give a different result once their order is declared. The next two lessons carry
this distinction forward — what gets measured, every time, is a property of the change
and the process, not the person.

## Summary

- Attention is a budget: the number of chunks read in one review round is fixed (12
  chunks, 600 lines, in the fixture). If the change is larger than that, every added
  chunk goes into the unread set.
- Same 24 defects, same axis list: at 100 and 300 lines, **22** are found (**0.917**); at
  900, **18** (**0.750**); at 2400, **2** (**0.083**). Defect count did not change, only
  the change grew.
- 2400 lines is **48 chunks** and **36 chunks** go entirely unread; the 12 chunks read
  are the poorest window in defect terms, which is why the ratio falls even below 12/48.
- When the same 2400 lines are read in four windows, found rises to **22**, ratio to
  **0.917**. What determines this is not line count but the number of chunks read.
- The **2** missed defects stay the same at every size and in every window: **unwritten
  requirement**. Neither shrinking the size nor splitting shows what was never written.
- Approval is not proof of having been read; an unread chunk produces no feedback, and
  two reviews look the same from outside.

## Next Step

When a change is a reviewable size, the reviewer finds 22 defects. But a found defect is
not a fixed defect: the gap between them is closed by a **review comment**, and what the
comment says determines how many review rounds it takes to close the defect. The next
lesson measures the same 22 findings written two ways — a note stating what should change
and why against a note that only says what was seen — and which note form forces a change
to close with the defect still open.
