Skip to main content

The other caller of the code you changed

Level 5: Judging Code You Did Not Writemedium26 mincode readingcode reviewminimal changeregression tests

Change a shared helper without breaking its second caller: map every call site, keep both contracts, and move the special case into the layer that wants it.

Shared code has more than one boss

Yesterday's ticket was small. Sessions shorter than a minute were billing zero minutes on the usage report. An assistant was pointed at the report, followed the number back to a shared helper called minutes, and changed the helper to round up. The usage suite went green, the diff was one line, and it shipped.

This morning a different team's pager fires a minute early on every job, because their alerting module calls minutes too, and it was built on the promise that a partial minute does not count. Nobody edited that module. Nobody opened it. It was not in the context window, and it had no test at the boundary where the two answers differ.

That is the most common regression shape in AI-assisted work, and this lesson starts where you actually arrive: after the change landed. Two callers want two different things from one function, and the function currently serves one of them.

Find every caller first

Before you judge any change to shared code, get the list of who depends on it. Search the repository for the function name and for imports of its module, then write one line per call site saying what that caller asks for. It takes a minute, and it is the entire difference between a repair and a second outage.

callerwhat it asks of minutes
the alerting checkwhole elapsed minutes, so a partial minute never trips a threshold
the billing rollupany started minute, because a customer who used 30 seconds used the service

Written out, the conflict is obvious and the one line change stops looking like a fix. Here is the same shape with storage sizes, so the pipeline trace stays yours to do:

# storage/size.py
def kilobytes(byte_count):
    """Whole kilobytes stored. A partial kilobyte does not count."""
    return byte_count // 1024


# quota/enforce.py
def over_quota(byte_count, limit_kb):
    return kilobytes(byte_count) > limit_kb    # wants whole stored kilobytes


# invoices/lines.py
def storage_charge_kb(byte_count):
    return kilobytes(byte_count)               # wants any started kilobyte charged
Check yourself
A ticket asks you to change the rounding in a shared helper. What comes first?

The change lives where the need lives

A helper's docstring is a contract, and every caller signed it. When one caller needs something different, the difference belongs in that caller, or behind a new parameter whose default preserves the old behavior for everyone who did not ask for a change.

# before: the invoice's rounding was pushed into the shared helper
def kilobytes(byte_count):
    return (byte_count + 1023) // 1024    # quota now trips one kilobyte early


# after: the invoice rounds up at the invoice
def kilobytes(byte_count):
    """Whole kilobytes stored. A partial kilobyte does not count."""
    return byte_count // 1024


def storage_charge_kb(byte_count):
    return (byte_count + 1023) // 1024

The move to avoid is a branch inside the helper keyed on who is calling. It keeps the code in one place, which feels tidy, but the shared function now knows its callers by name, and the next caller either lands in the wrong branch or adds a third one.

Check yourself
One caller needs rounding that contradicts the helper's documented contract. Where does that difference belong?
Check yourself

The alerting check wants whole elapsed minutes and the billing rollup wants any started minute. Sort each arrangement by whether both contracts survive it.

The helper rounds up and both callers use it as is
The helper returns whole elapsed minutes and billing rounds up locally
The helper returns whole elapsed minutes and billing is left unchanged
The helper gains a round_up flag defaulting to False and billing passes True

Prove you did not break the neighbor

The reason yesterday's change survived review is that the neighbor had no test at the boundary where the two rules disagree. Whole minutes and rounded up minutes agree on every exact minute and differ on every other input, and the alerting suite only asserted exact ones. So the repair is not finished when both suites pass. It is finished when the drifted boundary has the test the neighbor was missing, which is why the alerting suite in this lesson pins 179 seconds and 59 seconds rather than only 180 and 60.

Pitfalls

  • Reverting the helper and stopping there. The neighbor recovers, the caller that asked for the change breaks, and you have traded one outage for the other.
  • Branching inside the helper on which caller is asking. One shared function that knows its callers by name is harder to change than two honest ones.
  • Copying the helper into your caller and editing the copy. It ends the argument today and starts a drift bug next quarter.
  • Reading green CI as a list of callers. It is a list of tests, which is a different and usually shorter list.

Interview nuance: in a pairing round, a shared helper with a second call site is seeded on purpose, and the interviewer is watching for one sentence. Saying "before I touch this, let me see who else calls it" and then actually searching is one of the clearest experience signals you can give, because it shows you have been on the receiving end of somebody else's local fix.

Check yourself
Yesterday's change to the shared helper shipped with CI fully green, and it broke a caller. How?
Worked example (Python)
# One helper, two callers, two different contracts.
def minutes_floor(seconds):
    return seconds // 60


def minutes_ceil(seconds):
    return (seconds + 59) // 60


def alert_fires(minutes_fn, runtime_seconds, threshold):
    # Ops contract: only fully elapsed minutes count.
    return minutes_fn(runtime_seconds) >= threshold


def billed(minutes_fn, sessions):
    # Billing contract: any started minute bills as a full minute.
    return sum(minutes_fn(s) for s in sessions)


print("helper  alert at 179s against a 3 min threshold  billed for [30, 90]")
for name, fn in [("floor", minutes_floor), ("ceil", minutes_ceil)]:
    fires = alert_fires(fn, 179, 3)
    print(f"{name:<6}  {str(fires):<41}  {billed(fn, [30, 90])}")

print()
print("Ops wants no alert at 179 seconds. Billing wants 3 minutes for [30, 90].")
print("floor: ops is quiet, billing under-bills the short sessions.")
print("ceil:  billing is right, ops gets paged a minute early.")
print("No single body satisfies both, so the difference belongs in a caller.")

Apply

Your turn

The task this lesson builds to.

Fix the early alerts without losing yesterday's usage fix: restore the documented contract of pipeline/duration.py and put the round-up where it belongs. Start with README.md for the change log. The alerts module is owned by another team and is read-only. Some tests are hidden.

3 hints and 3 automated checks are waiting in the workspace.

Practice

Make it stick

A second problem on the same idea, so it survives past today.

Payroll grouping broke the day after a generated change made week starts land on Sunday for the analytics report. Repair backoffice/weeks.py and backoffice/analytics.py so payroll gets its documented Monday weeks back while the analytics report keeps its Sunday weeks. Start with README.md for the change log. Some tests are hidden.

3 hints and 2 automated checks are waiting in the workspace.