---
title: 'Meaning of Interface'
source: 'https://academia.sh/en/courses/code-review/meaning-of-interface'
course: 'Code Review and Team Process'
language: en
updated: '2026-08-17T18:10:45+00:00'
license: 'CC BY-SA 4.0'
---

# Meaning of Interface

The sets the five axes find are disjoint and the sum of pairwise intersections is 0; found drops from 22 to 19 when the interface axis is closed, and ten reviewers without an interface axis find 3 fewer defects than a single reviewer covering all five axes.

The previous lesson showed that a five-axis reading finds **22** of the 24 defects and a
single-axis reading finds **6**. The difference came from axis count; but the table
assumed something and did not verify it: that the axes' **shares are disjoint from one
another.** If the five axes sum to 22, that means no defect is being counted by two axes
at once.

This lesson measures that assumption, and it does the measuring on the first axis, the
**interface** axis. Its question is this: is an interface defect really found only by
the eye looking at the interface, or does it become visible from another axis too if
enough people read carefully enough?

## The Contract of a Public Interface

A piece of code's **public interface** is the set of externally callable names and their
behaviors: function signatures, type fields, return values, error forms, and the names
themselves. This set is called the **interface contract**, because the calling side
writes code trusting it.

Breaking the contract does not have to be a compile error. Versioning strategies and the
classification of breaking versus non-breaking changes were established in the Web API
Design course's Errors, Versions, and Documentation topic; they are not repeated here.
The only thing this lesson measures is **how it is noticed in review**: which axis's eye
sees the trace, and which does not.

## Five Ways to Break It, and Their Trace in Review

**Adding a required parameter.** The trace is in the signature itself and is read inside
the change. Calling code does not compile; the defect is also caught at compile time
after merge. Review's contribution here is not finding it, it is finding it **early**.

**Changing a parameter's meaning.** The signature stays the same — same name, same type
— but the expected value is different; a duration field's unit has moved from seconds to
milliseconds. The only thing visible in the change is a multiplier in the body; there is
no trace in the signature.

**Changing the return value's behavior.** Returning an empty value instead of an empty
list, a default instead of an error, unordered instead of ordered. The trace is in the
body; the calling side compiles and silently runs wrong.

**Changing the class of thrown error.** Catch blocks no longer match; the error escapes
uncaught and the calling side does not learn this at compile time.

**Removing or renaming a name.** This is the most visible form and is read directly
inside the change; who uses the removed name, however, is outside the change.

What the middle three forms share is this lesson's starting point: **the compiler is
silent, the tests pass, and review remains as the only defense** — because the tests too
were updated in the same change to match the new behavior.

## The Trace Is Inside the Change, Its Consequence Is Outside

The distinguishing trait of an interface defect in review is this: **its trace is
small, its consequence sits outside the change.** What is visible in the submitted
change is a single line — a signature. The code that line breaks is not inside the
change; it is in other files, other repositories, or calls not yet written.

This explains why the interface axis requires a separate reading. The eye looking at the
implementation reads the function's body, and the body is correct; the eye looking at
the test reads the tests, and the tests pass, because the tests were also updated to
match the new signature. Seeing the trace requires comparing the signature against its
**previous form**, and the only reading that does this is the one from the interface
axis.

```text
# taught review-comment example, not executed

subject   signature of the public function
before    parse(text)
after     parse(text, mode)

note      Second parameter added without a default; a call made with the
          previous signature is no longer valid. This is a change that
          cannot be published without touching calling code. Two paths
          exist: give the mode parameter a default that reproduces the
          previous behavior, or keep the previous signature and offer the
          new behavior under a separate name.
```

The note points at a spot in the change, names the broken condition, and proposes two
closing paths. This block is an example and is not run; the lesson's numbers come only
from the measurement block.

## Three Sources Available to Review

A reading working on the interface axis can look at three sources, and their scope
differs.

**The change itself** is the added and removed lines. The contract's new form is here,
and its previous form sits in the removed lines; comparing these two forms is the
interface axis's actual job.

**The change's title and description** carry the author's intent. The difference
between "added a mode parameter" and "added a mode parameter as required" determines
which question review asks. If this source is missing, the reading is forced to
interpret the signature on its own.

**The rest of the repository** contains calling code but is not part of the change. The
interface defect's consequence is here, and review is not required to look at this
source; doing so is a separate decision and generates a separate reading request.

The measurement does not model these three sources separately; if the interface axis is
open, the defect is found, if closed, it is not. This simplicity in the model exists to
keep what is measured in a single variable: **which axis is being looked at.**

## The Measurement's Assumptions

- **RA9** — The shared setup is unchanged: 600 lines, 12 chunks, 24 defects, attention
  12 chunks. The entire change is read; there is no unread chunk in this lesson either.
- **RA10** — A defect belongs to a single class. Two classes of defect can be found on
  the same line, but these are two separate defects and are counted separately.
- **RA11** — The **intersection** of two axes is the set of defect numbers both find.
  Because classes exclude one another, this intersection is expected to come out empty;
  the measurement does not assume this, it counts it.
- **RA12** — The "only this axis" column is the number of defects an axis finds that
  none of the other four finds.
- **RA13** — In panels without an interface reviewer, no reviewer carries the interface
  axis; the remaining four axes are distributed freely and the number of people can be
  increased.
- **RA14** — In the "ten reviewers, no interface" panel, each of the four axes gets two
  people, plus two more people who carry all four together; ten readings are done in
  total.
- **RA15** — The measurement is still single-round. Finding an interface defect later,
  once calling code breaks, is outside this table and is not counted as a review
  finding.

## Measurement

```python
"""Interface axis: does another axis find what one axis finds.

Part 1 - five single-axis readings; intersection and unique-to-this-axis.
Part 2 - configurations with and without the interface axis.
"""
SEED = 20260815
AXES = ("interface", "implementation", "test", "documentation", "style")
UNWRITTEN = "unwritten requirement"
CLASSES = AXES + (UNWRITTEN,)
ATTENTION = 12
CHUNK = 50


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

    def draw(n):
        nonlocal state
        state = (state * 48271) % 2147483647
        return state % n
    return draw


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


def review(d, axes, attention=ATTENTION):
    read = set(range(min(attention, d["chunks"])))
    return {k["no"] for k in d["defects"]
            if k["class"] in axes and k["chunk"] in read}


def panel(d, assignments, attention=ATTENTION):
    found = set()
    for axes in assignments:
        found |= review(d, axes, attention)
    return found


FULL = set(AXES)
d = change(600)
single = {a: review(d, {a}) for a in AXES}

print(f"{'axis':<15s} {'found':>7s} {'also in other four':>19s} "
      f"{'only this axis':>15s}")
for a in AXES:
    other = set().union(*(single[o] for o in AXES if o != a))
    print(f"{a:<15s} {len(single[a]):7d} {len(single[a] & other):19d} "
          f"{len(single[a] - other):15d}")
union = set().union(*single.values())
intersection = sum(len(single[a] & single[b])
                    for a in AXES for b in AXES if a < b)
print(f"union of five single readings {len(union)}, "
      f"sum of pairwise intersections {intersection}")

print()
FOUR = FULL - {"interface"}
PANELS = {
    "one reviewer, five axes": [FULL],
    "one reviewer, no interface": [FOUR],
    "three reviewers, interface included": [{"interface"}, {"implementation"},
                                             {"test", "documentation"}],
    "three reviewers, interface replaced": [{"implementation"}, {"implementation"},
                                             {"test", "documentation"}],
    "ten reviewers, no interface": [{a} for a in sorted(FOUR)] * 2 + [FOUR] * 2,
}
print(f"{'panel':<36s} {'reviewers':>9s} {'found':>7s} {'missed':>6s} "
      f"{'interface missed':>17s} {'unwritten missed':>16s}")
for name, assignment in PANELS.items():
    b = panel(d, assignment)
    missed = [k for k in d["defects"] if k["no"] not in b]
    iface = sum(1 for k in missed if k["class"] == "interface")
    unwritten = sum(1 for k in missed if k["class"] == UNWRITTEN)
    print(f"{name:<36s} {len(assignment):9d} {len(b):7d} {len(missed):6d} "
          f"{iface:17d} {unwritten:16d}")
```

```
axis              found  also in other four  only this axis
interface             3                   0               3
implementation        6                   0               6
test                  3                   0               3
documentation         3                   0               3
style                 7                   0               7
union of five single readings 22, sum of pairwise intersections 0

panel                                reviewers   found missed  interface missed unwritten missed
one reviewer, five axes                      1      22      2                 0                2
one reviewer, no interface                   1      19      5                 3                2
three reviewers, interface included          3      15      9                 0                2
three reviewers, interface replaced          3      12     12                 3                2
ten reviewers, no interface                 10      19      5                 3                2
```

## The Shares Are Disjoint

The top table's middle column gives **0** in all five rows, and the sum of pairwise
intersections is also **0**. None of the five axes finds even a single defect another
axis finds.

The column on the right is the mirror image of this: **all** of the defects each axis
finds appear only in that axis — **3** in interface, **6** in implementation, **3** in
test, **3** in documentation, **7** in style. Their total is **22**, equal to the union
of the five single readings. So an axis's contribution does not depend on which axes are
already open.

Being disjoint does not mean two defects cannot sit in the same file or the same line
range; it means the **classes** are separate. A signature defect and a style defect can
both stand on the same line of the same function, and the setup counts these as two
separate defects. Two eyes reading the same text find separate things because they are
searching for separate things.

The practical counterpart is exact: **the number of defects lost when an axis is closed
equals the number of defects in that axis's class.** There is no leftover that
compensates for a closed axis. If the interface axis is closed, three defects are gone;
no amount of reading added to other axes brings those three back. The bottom table tests
this sentence.

## Once the Interface Axis Is Closed

The bottom table's first two rows are the measurement's core. Same single reviewer, same
change, the only difference is whether the interface axis is open: found drops from
**22** to **19**, missed climbs from **2** to **5**, and the difference is exactly **3**
interface defects. The loss is exactly the size of the class.

The third and fourth rows repeat the same measurement on a three-person panel. With the
interface axis open, three reviewers find **15**; when a second implementation reading
replaces the person on the interface axis, that drops to **12**. Reviewer count did not
change, reading count did not change — the second implementation reading is even extra
effort. The result is **3** fewer defects. The return on looking at the same axis twice
is **0**, and this measurement makes that cost visible.

The last row is the lesson's sharpest result. **Ten reviewers** work without an interface
axis: two people on each of the four axes, plus two more people reading all four
together. Found is **19**. A **single** reviewer covering five axes finds **22**. A
ten-person panel finds **3** fewer defects than a one-person panel — and the difference
comes from the single axis none of the ten looked at.

The fourth row's configuration is not made up; when a defect is missed in a review, the
first decision made is often **adding one more reviewer**. If the added person is not
told which axis to look at, they look at the most familiar one — implementation. The
table gives the numeric result of that decision: the panel grew, reading count went up,
found dropped from **15** to **12**. The drop is not because the new person broke
something; it is because the reading they replaced was on **a different axis**.

The difference is **3/24**, that is, **0.125**; given the set's resolution is **1/24 =
0.042**, this is three times that resolution and sits comfortably inside the measurement
band. The number's smallness can be misleading: each of the three missed defects is a
contract break that breaks code outside the change, and it becomes visible only where
review could not find it — once the calling side breaks.

There is a cheap way to open the axis that does not show up in the table: the interface
axis can be opened without rereading the entire change. The lines carrying public
declarations are a small part of the change, and a reading that looks at them first sees
the interface class's three defects before the others. Opening an axis is not always
adding a person to the panel; sometimes it is only changing the reading order. The setup
does not measure this distinction — an axis is either open or closed — but what the
table says holds for both: **there is no number of people that compensates for a closed
axis.**

## The Unwritten Side of the Contract

The interface axis compares the **written** form of the contract. The signature, the
return type, and the error class are text, and text can be read. The contract also has
an unwritten side: behaviors the calling side relies on but that are stated nowhere —
whether repeating the call is safe, whether the returned list's order is stable, whether
the same input always gets the same response.

When these behaviors break, the interface axis finds no trace, because there is no
previous text to compare against. Its counterpart in the setup is direct: such a defect
falls not into the interface class but into the sixth class — **unwritten
requirement** — and keeps being missed even with the interface axis open. This is why
the bottom table's last column gives **2** in all five rows.

The distinction comes down to a single question: **is what broke written down
somewhere?** If it is written, it is the interface axis's job and is found once the axis
is opened. If it is not written, it is no axis's job, and it gets closed outside review,
where the contract gets written.

## Summary

- A public interface is the set of externally callable names and their behaviors; its
  contract can break in five separate ways, and three of them run wrong without breaking
  calling code.
- An interface defect's trace inside the change is a single line, and its consequence
  sits outside the change; this is why it requires comparing the signature against its
  previous form.
- The sets the five axes find are disjoint: the sum of pairwise intersections is **0**,
  everything each axis finds appears only in that axis, and the union of the five is
  **22**.
- The defect lost when an axis is closed equals the size of that class: closing the
  interface axis drops found from **22** to **19** and raises missed from **2** to
  **5**.
- When a second implementation reading replaces the person on the interface axis, a
  three-person panel finds **12** instead of **15**; a **ten**-reviewer panel without an
  interface axis stays under the **22** a single reviewer covering five axes finds, at
  **19**.
- When the unwritten side of the contract breaks, the interface axis has no text to
  compare; this defect falls into the sixth class and is missed even with the axis open.

## Next Step

The largest single share in the table belongs to the **implementation** axis: **6**
defects, **0.250** of the 24. The interface axis's finding came from comparing the
signature; the implementation axis's finding has no previous form to compare against —
what is looked at is the gap between what the code should do and what it does. The next
lesson measures this axis: as axes are opened one at a time, what curve does the found
count grow along, where does the implementation axis's own share sit on that curve, and
how much does being the single biggest finder make it, on its own, sufficient.
