Skip to content
academia.sh

Lesson 11 / 15

Issue Tracking and Boards

The wait a work item spends between statuses is 19 units outside review; the review round varies between 12 and 36 depending on the assignment and its share climbs from 0.387 to 0.655; putting three remaining defects back on the board takes the total from 31 to 105.

Contents

Up to this point, review was measured around a single change: how many axes were looked at, how many chunks were read, how many rounds it went through. The change stood alone in these measurements — it came from nowhere and went nowhere. In a team, though, a change is not just a change; it is one item inside a flow. It is born as a proposal, enters a queue, someone takes it on, it is written, reviewed, merged, and closed.

In this chain, review is only one link. This lesson’s question is: how long does the link itself take, and what is its share of the whole chain? To know the value of shortening a review round, it first has to be known what the round stands inside of. If the rest of the chain is longer than review, cutting the review round in half changes little in the total.

The Work Item and Its Statuses

A work item is the smallest unit of work a team tracks: a bug report, a request, a task. An item has a single identity, a single owner, and at any moment a single status. A board is the surface these items are laid out on according to their status; every column is a status, and an item moves left to right.

A board is not a record tool, it is a visibility tool. The same information could stand in a list too; what a board adds is that how many items have piled up in which status can be read at a glance. A column swelling says that exit from that status is slower than entry into it.

# example board dump — not executed

proposal      queued        in progress   in review     merged
──────────    ──────────    ──────────    ──────────    ──────────
#412          #398          #377          #361          #350
#411          #395          #374          #359
              #391                        #356
              #388                        #352
                                          #349

The status set itself is a design decision, and it has a single criterion: a status makes sense when its exit depends on someone else’s action. in review is a status because there the item is waiting not on the author’s action but the reviewer’s. A column whose exit is always in the same person’s hands is not a status, it is that person’s personal list; it takes up room on the board and reports no wait. The same criterion limits column count too: a new status is added only if there is a new handoff point.

In this dump, the in review column carries five items, in progress two. The number itself is not a defect; the defect is the column’s emptying speed falling below its filling speed. If five items have piled up there, the review queue is lengthening, and a lengthening queue writes wait onto every one of them.

Wait Stands Not in a Status but in a Transition

This is the point where a board readily misleads. Not all of the time an item spends standing in the in review column is review; part of it is waiting for the reviewer to look at the item. What has to be measured is not how long the item stands in one column, but how long it takes to move from one column to the next.

This distinction defines the measurement. There are five transitions in the chain, and four of them are outside review: a proposal enters the queue, someone from the queue takes it on, writing finishes and the item goes out to review, review finishes and the item closes. The fifth transition — from in review status to merged status — is review itself and it is not fixed: it depends on how many rounds it goes through.

Wait time is counted here without a unit. Not hours or days, but a comparable unit; the goal is not to read the absolute duration but the ratio between links to each other. These metrics are metrics of the process: how long an item waits is the result not of the person looking at that item, but of the shape of the chain.

Column Length and Wait Are Not the Same Thing

The five items standing in the board’s in review column are an accumulation; the wait measured at the transition is a rate. The two do not give the same number. A five-item column means one unit of wait if the exit rate is five items per unit; it means five units of wait if the exit rate is one item per unit. Someone looking at the column cannot tell the two states apart, because a board shows accumulation and does not show rate.

The direct consequence of this is: shortening the column does not shorten the wait. Moving two of the five items to another column lowers the accumulation and relieves the board; it does not touch how long the transition takes. The only thing that shortens the wait is speeding up the transition itself, and in the review link this means lowering the round count.

One more distinction is needed. A single work item can carry more than one change; then the review link runs more than once per item, and the chain’s total grows accordingly. This measurement counts one change per item and gives its numbers under that assumption. If an item is split into three changes, the transitions outside review are paid once, the review link three times — its share grows in that proportion. This says that splitting items is not free: splitting makes review easier but raises review’s share of the chain.

Review’s Place in the Chain

What determines the length of the review link? The course’s fifth reading already said this: what lengthens the round and the wait is what could not be seen in the first round. What is found in a round gets fixed; what is not found carries over to the next round, and every round writes one more wait onto the chain. If axis diversity is low, the first round finds little, round count rises, the link lengthens.

The chain’s total is therefore fed from two sources. One is the fixed part — the proposal, queue, writing, and closing transitions; these are independent of how review is done. The other is the variable part, and it is the direct result of axis assignment. The measurement counts these two separately and gives their ratio.

There is one more defect: the remaining defect once the rounds are over. A remaining defect does not mean review is finished and the item is closed; the closed item later gets reopened because of that defect and enters the chain a second time. This second pass does not start with the proposal — the item has already been proposed — but it repeats everything starting from the queue.

The measurement’s assumptions:

  • TF1 — The shared setup’s 600-line change and 24 defects are used as they are; the class distribution and the oracle are the course’s constant.
  • TF2 — The wait of the four non-review transitions is fixed and independent of the assignment: 5, 9, 2, and 3 units respectively. These values represent the chain’s shape, not a team’s measurement.
  • TF3 — The review link’s wait comes from the shared setup’s round cycle: every round writes 6 units and round count is the result of the defect count found in the first round.
  • TF4 — A found defect is considered fixed in that round; a remaining defect carries over to the next round. Six rounds is the upper bound, and an assignment resting on that bound leaves a defect remaining.
  • TF5 — A remaining defect reopens the item once after it closes. The second pass starts from the queued status; the proposal transition is not repeated.
  • TF6 — The metric is reported per item, not per person. No number in the chain can be read as a role’s performance; all of it is the result of the chain’s shape.
  • TF7 — The set’s resolution at 24 defects is 1/24; because the wait unit is 6 per round, the smallest measurable difference in the chain is 6 units.

Measurement

"""Work item chain: wait between statuses and review's share.

Part 1 - found, missed, and review wait in four assignments.
Part 2 - the chain's total, review's share, and the cost of reopening.
"""
SEED = 20260815
AXES = ("interface", "implementation", "test", "documentation", "style")
UNWRITTEN = "unwritten requirement"
CLASSES = AXES + (UNWRITTEN,)
ATTENTION, CHUNK = 12, 50
# Transitions outside review: wait read off the board.
OUTSIDE = (("proposal", "queued", 5), ("queued", "in progress", 9),
           ("in progress", "in review", 2), ("merged", "closed", 3))


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

    def draw(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % 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


def review_rounds(found, defect_count, wait_per_round=6):
    remaining, rounds, wait = defect_count - len(found), 1, wait_per_round
    while remaining > 0 and rounds < 6:
        rounds += 1
        wait += wait_per_round
        remaining -= max(1, len(found) // 2)
    return rounds, wait, max(0, remaining)


FULL = set(AXES)
ASSIGNMENTS = {
    "single reviewer, all axes": [FULL],
    "three reviewers, same axis": [{"implementation"}] * 3,
    "three reviewers, separate axes": [{"interface"}, {"implementation"},
                                        {"test", "documentation"}],
    "five reviewers, five axes": [{a} for a in AXES],
}
OUTSIDE_TOTAL = sum(b for _, _, b in OUTSIDE)
d = change(600)

print(f"work item 1 | change {d['lines']} lines, {d['chunks']} chunks, "
      f"defects {len(d['defects'])} | wait outside review {OUTSIDE_TOTAL}")
print("transitions outside review: "
      + ", ".join(f"{a}→{b} {n}" for a, b, n in OUTSIDE))
print()
print(f"{'assignment':<32s} {'found':>7s} {'missed':>5s} {'unwritten':>10s} "
      f"{'rounds':>3s} {'review':>8s} {'remaining':>5s}")
for name, assignment in ASSIGNMENTS.items():
    b = panel(d, assignment)
    missed = [k for k in d["defects"] if k["no"] not in b]
    unwritten = sum(1 for k in missed if k["class"] == UNWRITTEN)
    rounds, wait, remaining = review_rounds(b, len(d["defects"]))
    print(f"{name:<32s} {len(b):7d} {len(missed):5d} {unwritten:10d} "
          f"{rounds:3d} {wait:8d} {remaining:5d}")

print()
print(f"{'assignment':<32s} {'chain':>6s} {'review share':>13s} "
      f"{'reopening':>14s} {'total':>6s}")
for name, assignment in ASSIGNMENTS.items():
    b = panel(d, assignment)
    rounds, wait, remaining = review_rounds(b, len(d["defects"]))
    chain = OUTSIDE_TOTAL + wait
    # A remaining defect puts the item back on the board: a second round starting from queued.
    reopened = 0 if remaining == 0 else chain - OUTSIDE[0][2]
    print(f"{name:<32s} {chain:6d} {wait / chain:13.3f} "
          f"{reopened:14d} {chain + reopened:6d}")
work item 1 | change 600 lines, 12 chunks, defects 24 | wait outside review 19
transitions outside review: proposal→queued 5, queued→in progress 9, in progress→in review 2, merged→closed 3

assignment                         found missed  unwritten rounds   review remaining
single reviewer, all axes             22     2          2   2       12     0
three reviewers, same axis             6    18          2   6       36     3
three reviewers, separate axes        15     9          2   3       18     0
five reviewers, five axes             22     2          2   2       12     0

assignment                        chain  review share      reopening  total
single reviewer, all axes            31         0.387              0     31
three reviewers, same axis           55         0.655             50    105
three reviewers, separate axes       37         0.486              0     37
five reviewers, five axes            31         0.387              0     31

Reading the Chain

The wait outside review is 19 units, and it is the same in all four rows. The only thing that changes is the review link, and it swings between 12 and 36. The chain’s total accordingly swings between 31 and 55: the worst assignment produces a chain 77% longer than the best one.

Review’s share is in the right column. In the best assignment, 0.387 of the chain is review; that is, roughly two-thirds of the item’s wait happens outside review. In the worst assignment, the share climbs to 0.655 — review is now more than half the chain. The gap between these two numbers gives a result: review is not a fixed-cost link, it is the chain’s most volatile link.

A practical ordering follows from this. The queued→in progress transition is the longest non-review link at 9 units, and in the good assignment it alone is about three-quarters of the review link. In the chain’s best state, the place to shorten is not review, it is the queue. But in the bad assignment, review is 36 units — four times the queue — and there, shortening the queue contributes little to the total. Which link to shorten depends on which assignment it is, and this is told not by the column length on the board but by the transition’s wait.

The table on the left also gives found and missed for the same rows, and the two should be read together. The three reviewers, same axis row finds 6 and misses 18; the row with the longest chain is also the row that finds the least. Single reviewer, all axes finds 22, misses 2, and has the shortest chain. So a long chain does not buy a better review — the source of the length is exactly what could not be found.

Among the missed, every row has 2 unwritten requirement defects, and this number does not change across the four rows. Neither the chain’s shape, nor the assignment’s diversity, nor round count finds those two defects. Issue tracking is not the remedy for this class; however well an item is tracked on a board, the absence of what was never submitted is invisible to review.

Reopening

The second table’s last two columns give the chain’s real cost. In the three reviewers, same axis assignment, 3 defects still stand after six rounds. The item is closed; the defects are not. A remaining defect eventually surfaces somewhere and the item is reopened.

Reopening adds 50 units to the chain: every transition besides the proposal runs one more time. The total climbs from 55 to 105. The same change closes in 31 units in the best assignment; the gap here is 74 units, more than three times as much.

How this number is read matters. Reopening is not a delay, it is a deferred wait: the wait not paid on the chain’s first pass is paid with interest on the second pass. Someone looking at the board sees no problem on the first pass — the item is closed, the column is empty, the numbers are clean. The problem appears on the board when the item comes back, and at that moment it is no longer associated with the original review’s measurement.

This is why a closed-item count alone is not enough to make a board readable. The number of reopened items has to stand as its own column or its own count; otherwise the chain’s most expensive behavior is invisible anywhere. A closing count reports two different processes as the same number for as long as it does not separate what closed on the first pass from what closed on the second.

Summary

  • A work item is the smallest unit of work in a flow; a board lays items out by status, and wait accumulates not in the statuses but in the transitions between them.
  • The chain’s wait outside review is fixed at 19 units; the review link varies between 12 and 36 depending on the assignment, carrying the chain’s total from 31 to 55.
  • Review’s share of the chain is between 0.387 and 0.655; review is not a fixed-cost link, it is the chain’s most volatile link, and which link to shorten depends on the assignment.
  • A long chain does not buy a better review: the 36-unit link finds 6 defects and misses 18, the 12-unit link finds 22 and misses 2.
  • The 3 defects remaining after six rounds reopen the item and add 50 more units to the chain; the total climbs to 105. A closing count hides this second pass unless it is counted separately.
  • In all four of the four assignments, 2 unwritten requirement defects are missed; issue tracking does not find this class.

Next Step

This measurement assumed the item’s author knew what to submit: the change entering the chain was a change written while trying to meet the team’s expectations. So where does the author learn those expectations from? Someone inside the team has learned them across rounds; someone coming from outside has not. An unwritten expectation comes back as a finding in review’s first round, and every finding writes one more round. The next lesson measures the written form of these expectations: which classes does a contribution guide close before review, how many rounds does closing each class cut, and is it really the guide that is the one mechanism able to touch a class no axis searches for?

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close