---
title: 'Release Publishing Workflow'
source: 'https://academia.sh/en/courses/code-review/release-publishing-workflow'
course: 'Code Review and Team Process'
language: en
updated: '2026-08-17T18:10:46+00:00'
license: 'CC BY-SA 4.0'
---

# Release Publishing Workflow

Twenty-four work items and six reader questions decide 144 decisions: the raw change list answers 22 of them (0.153), the classified list 44, the meaning list 88, and with tag and release asset together 132 (0.917); all 12 of the 12 unanswered decisions are also from the unwritten requirement class.

The previous lesson saw the chain's final transition as a single number: merged work gets
bound to a point and announced outward. That announcement is itself a text and it has a
reader — someone who never appeared through this whole course. Review knows the author and
the reviewer; **the release note's reader has seen neither the change nor the
discussion.**

This lesson's question is not how the note gets produced. The question is: **what does the
note tell the reader?** How many questions does the gap between a list of changes and a
list of the **meaning** of changes answer, and how many of the reader's questions find no
answer in any form?

## What the Reader Asks

Someone reading a release note is not curious about the release; they are curious about
**their own situation.** They ask six questions, and every question's answer is found in
the note only if a specific field was written.

The `did it change` question's answer is in the **title** — this is the minimal field
that says something changed. The `which area` question wants a **class**: where the
change touched. The `does it affect me` question wants an **impact**; the `what should I
do` question wants an **action**. The `after which point` question's answer is a
**boundary**, and a **tag** gives it. The `how do I get it` question's answer is an
**asset**, and a **release asset** gives it.

How a tag is placed and what it marks was established in the Introduction to Version
Control course; which pipeline a release passes through to get published is the subject
of the Continuous Integration and Delivery course. Neither is repeated here. In this
lesson, the tag and the **release asset** are counted only as **fields**: two fields
answering two of the reader's questions.

## Two Lists

The same release can be written in two formats, and both are produced from the same
information.

```text
# example release note — two formats, not executed

## change list
- #377 timeout setting on the payment call
- #381 retry counter fix
- #388 log format updated

## meaning list
### Applications making outbound calls
- The timeout duration is now configurable; the default did not
  change. To do: if no setting is given, behavior is the same, no
  action needed.
- Retry count is two instead of three. To do: call paths expecting
  three attempts should be reviewed.
### Internal changes
- Log format and style fixes; no effect on the reader's side.
```

The difference is not in length. The first list says **what was done**, the second says
**what happened to the reader**. In the first list, all three lines carry the same
weight; in the second, the change that does not concern the reader is folded into a
single line, and the two items the reader actually needs to look at are separated out.
The measurement counts these two differences separately: **question answered** and
**line scanned**.

The measurement's assumptions:

- **TF29** — The release's work items are taken from the shared setup's set: **24**
  items, and the class distribution is the course's fixed distribution. `change` and the
  class list are not modified.
- **TF30** — The item in the **unwritten requirement** class was never merged; it cannot
  enter any note format. The number of items entering the note is therefore **22**.
- **TF31** — The reader has six questions and every question's answer stands in a single
  field. If the field is written, the question is answered; if not, it stays unanswered.
  There is no wrong answer in this measurement.
- **TF32** — Four note formats are tried, and each covers the previous one's fields:
  title; title and class; meaning list; the meaning list with tag and release asset
  added.
- **TF33** — The reader-facing classes are **interface**, **implementation**, and
  **documentation**; test and style items do not change the reader's situation. If the
  class field is not written, the reader cannot tell them apart and scans every line
  entering the note.
- **TF34** — The set's resolution is **1/24** at 24 items, **1/144** at the decision
  scale; one item's one question is the smallest unit measurable in this measurement.

## Measurement

```python
"""Release note: the gap between a change list and a meaning list.

Part 1 - four note formats and the reader's six questions.
Part 2 - the line the reader scans against the line that is useful.
"""
SEED = 20260815
AXES = ("interface", "implementation", "test", "documentation", "style")
UNWRITTEN = "unwritten requirement"
CLASSES = AXES + (UNWRITTEN,)
CHUNK = 50
# Reader's question -> the field carrying its answer.
QUESTIONS = {"did it change": "title", "which area": "class",
             "does it affect me": "impact", "what should I do": "action",
             "after which point": "boundary", "how do I get it": "asset"}
FORMATS = {
    "raw change list": ("title",),
    "classified list": ("title", "class"),
    "meaning list": ("title", "class", "impact", "action"),
    "meaning + tag + asset": ("title", "class", "impact", "action",
                              "boundary", "asset"),
}
# Reader-facing class: the release note's reader searches for these.
READER_FACING = {"interface", "implementation", "documentation"}


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}


d = change(600)
# The release's work items: the class distribution is the course's fixed distribution.
ITEMS = [{"no": k["no"], "class": k["class"]} for k in d["defects"]]
# The unwritten class was never merged; it cannot enter any note format.
IN_NOTE = [o for o in ITEMS if o["class"] != UNWRITTEN]
distribution = {s: sum(1 for o in ITEMS if o["class"] == s) for s in CLASSES}
print("release: " + ", ".join(f"{s} {n}" for s, n in distribution.items()))
print(f"work items {len(ITEMS)} | in note {len(IN_NOTE)} | reader questions "
      f"{len(QUESTIONS)} | decisions {len(ITEMS) * len(QUESTIONS)}")
print()
print(f"{'note format':<24s} {'fields':>6s} {'answered':>8s} {'unanswered':>10s} "
      f"{'ratio':>6s} {'from unwritten':>14s}")
total = len(ITEMS) * len(QUESTIONS)
for name, fields in FORMATS.items():
    answered = sum(1 for o in IN_NOTE for s in QUESTIONS if QUESTIONS[s] in fields)
    unwritten = (len(ITEMS) - len(IN_NOTE)) * len(QUESTIONS)
    print(f"{name:<24s} {len(fields):6d} {answered:8d} {total - answered:10d} "
          f"{answered / total:6.3f} {unwritten:14d}")

print()
print(f"{'note format':<24s} {'scanned lines':>13s} {'useful':>6s} {'ratio':>6s}")
for name, fields in FORMATS.items():
    # No class field: the reader scans every line; with it, only the reader-facing group.
    scanned = (len(IN_NOTE) if "class" not in fields
               else sum(1 for o in IN_NOTE if o["class"] in READER_FACING))
    useful = sum(1 for o in IN_NOTE if o["class"] in READER_FACING)
    print(f"{name:<24s} {scanned:13d} {useful:6d} {useful / scanned:6.3f}")
```

```
release: interface 3, implementation 6, test 3, documentation 3, style 7, unwritten requirement 2
work items 24 | in note 22 | reader questions 6 | decisions 144

note format              fields answered unanswered  ratio from unwritten
raw change list               1       22        122  0.153             12
classified list               2       44        100  0.306             12
meaning list                  4       88         56  0.611             12
meaning + tag + asset         6      132         12  0.917             12

note format              scanned lines useful  ratio
raw change list                     22     12  0.545
classified list                     12     12  1.000
meaning list                        12     12  1.000
meaning + tag + asset               12     12  1.000
```

## Reading the Gap

**22** of a hundred forty-four decisions are answered in the raw change list — a ratio of
**0.153**. The list is complete, no work item is skipped, and it does not answer five of
the reader's six questions. Completeness and answering are not the same thing.

Adding the class field raises answered to **44**, the meaning list to **88**. The real
jump is in the third row: the **impact** and **action** fields alone carry **44**
decisions, roughly a third of the set. What these two fields share is that neither can be
**derived** from the merged work. Title and class can be extracted from the merged work;
impact and action exist only if someone writes them.

In the fourth row, the ratio climbs to **0.917**. This number is familiar in the course:
in a 100-line change, the ratio of defects found was also **0.917**. This is not a
coincidence — in both cases what is left out is exactly **2/24**, and those two items
belong to the same class.

The table below gives the second gap. In the raw list, the reader scans **22** lines and
**12** of them are useful: ratio **0.545**. Close to half the reader's reading is wasted.
Once the class field is added, scanned drops to **12** and the ratio becomes **1.000**.
What stands out is that this gain is secured at the **second** row: grouping cuts the
reader's scanning in half before impact and action are even written. **The cheapest
improvement is not rewriting the note, it is classifying it.**

## Summary

- The release note's reader has seen neither the change nor the discussion; they have six
  questions, and every question's answer stands in a single field written in the note.
- The raw change list answers **22** of a hundred forty-four decisions (**0.153**), the
  classified list **44**, the meaning list **88**, and with tag and release asset
  together **132** (**0.917**).
- The **impact** and **action** fields alone carry **44** decisions, and neither can be
  derived from the merged work; they exist only if someone writes them.
- The reader scans **22** lines in the raw list and uses **12** (**0.545**); the class
  field drops scanned to **12** and makes the ratio **1.000** — this gain is secured
  without writing impact or action.
- All **12** of the **12** unanswered decisions also belong to the **unwritten
  requirement** class; unmerged work shows up in no note format.

## Course Wrap-Up

The course ran on a single measure: a review's number is **not how many people are
looking, it is how many axes are being looked at and how many chunks are read.** Fifteen
lessons applied this measure to separate places.

| lesson | measured | found or missed |
|---|---|---|
| Purpose of Code Review | what axis diversity finds | three reviewers on the same axis find 6, on separate axes 15, a single reviewer on five axes 22, five reviewers again 22; 2 unwritten-requirement defects are missed in all four |
| Meaning of Interface | the cost of closing one axis | the five axes' sets are disjoint, the sum of pairwise intersections is 0; closing interface drops found from 22 to 19, and ten reviewers without interface stay at 19 |
| Meaning of Implementation | an axis's marginal contribution | implementation adds 6 in both orders, style adds 7, the remaining three add 3 each; 1, 3, and 12 readings on the same axis stay at 6, ratio 0.250 |
| Reviewing Tests | the defect the test itself carries | three test defects sit in chunks 9, 1, and 7, and those chunks already had 1, 2, 1 findings from other axes; missed in an unread chunk is 0 on all five axes, and closing the test axis drops found from 22 to 19 |
| Documentation Review | what is missed when the documentation axis is closed | the five axes' individual closing cost is 3, 6, 3, 3, and 7; closing documentation drops found from 22 to 19, documentation missed is 0 while the axis is open, and unwritten missed stays at 2 |
| Code Style and Automation | attention shifting once the style axis is handed to a tool | human findings drop from 22 to 15, style's share from 0.318 to 0.000; the tool's contribution equals the axis left uncovered — 0 if five axes are covered, 15 to 22 with three reviewers, and missed stays at 2 in the best tooled case |
| Change Size | size against fixed attention | 22 (0.917) at 100 and 300 lines, 18 (0.750) at 900, 2 (0.083) at 2400; 36 of 48 chunks go entirely unread |
| Feedback Language | the round the same finding's wording produces | actionable 2 rounds / 12 wait / 44 note-rounds, observation 3 / 18 / 66, criterion-free 4 / 24 / 88; found stays at 22 and missed at 2 |
| Disagreement Resolution | the round an unresolved finding carries | rule absence: 6 rounds / 36 wait and 8 defects in the code; binding to a criterion: 3 rounds / 18 units and only 2 in the code |
| Review Metrics | wait, round, and queue health | three reviewers on the same axis: 6 rounds / 36 wait / 3 remaining, single reviewer on five axes: 2 / 12 / 0; longest open time in the queue is 2 rounds versus 15 |
| Issue Tracking and Boards | chain wait and review's share | wait outside review fixed at 19, the link between 12 and 36, share between 0.387 and 0.655; 3 remaining defects take the chain from 55 to 105 |
| Contribution Guidelines | the round a written expectation closes | in a narrow review, round is 6, 5, 4, 3, 2 and wait drops from 36 to 12; a requirement statement drops unwritten-requirement missed from 2 to 0 |
| Ownership and Approval Rules | the effect of required reviewers on axis diversity | as approvals climb from 1 to 3, found stays at 6, rounds at 6, wait at 36; a single approval with no axis restriction finds 22, and in narrow ownership 4 axes stay open with 16 defects standing there |
| Repository Platform Capabilities | the transition each capability shortens | the chain drops from 45 to 21, gain 24; found 15, missed 9, and unwritten missed 2 stay fixed in every row; discussion's gain is 4, 6, 12 |
| Release Publishing Workflow | the question the note answers the reader | 22 (0.153), 44, 88, and 132 (0.917) of 144 decisions; all 12 of the 12 unanswered are unwritten requirement |

Three readings come out of the table. **First:** what grows the number is not the person,
it is the axis, and this was reconfirmed in every topic — a third person looking at the
same axis adds **0**, a new axis adds between **3** and **7** defects. **Second:** what
determines round and wait is what could not be seen in the first round; the guide,
ownership rule, and platform capabilities touch wait, not what is found. **Third:** the
same two defects are missed across all fifteen rows. Adding an axis, shrinking size,
cutting the round, writing a rule, opening a surface — none of them reaches them. The one
mechanism that reaches them was the guide item requiring the author **to write** the
requirement, and it was not a review tool.

The third reading draws the course's boundary. **Review looks at what was submitted.**
Everything inside a submitted change — its interface, implementation, test,
documentation, style — can be seen with enough axes and a small enough size. The absence
of what was never submitted is not review's subject; only **writing it** closes that gap.

The next course — Technical Writing and Documentation — takes up exactly this work: the
separation of document types from each other, the separate structures of tutorial and
reference, the repository's own documents, the layout of an interface reference, and
writing release notes with the reader's meaning in view. This lesson's final measurement
leaves a number there: how many of the six questions a note answers depended not on who
wrote the note but on **which fields were written.** The distinction has to be set from
the start: **review looks at what was written; writing what was not written is a
separate job.** The measure this course leaves behind is the unit that will be used there
to count how many questions every document written counts as answering.
