Lesson 08 / 15
Structure and Headings
Heading layout measures the distance between the reader's question and its answer; across thirty-six visits, headingless text takes an average of 26.38 steps, type-headed 7.29, two-tier 7.00, and the two-tier layout pays for that gain by carrying 24 silently stale headings at the sixth version.
Contents
The previous lesson wrote the audience definition and measured the new reader resolving all 56 claims with the full definition. Being able to resolve is not being able to reach. The reader does not read a document start to finish; they arrive with a question and search for the sentence that answers it.
This lesson’s question is the cost of that search: how many steps sit between the reader’s question and the claim that answers it, and how much does heading layout shorten that distance? A second question comes with it, tied to the course’s own axis: a heading is itself a sentence — does it go stale too?
The Reader Does Not Read, They Scan
The reader of a technical text is not the reader of a literary text. They have a task in hand, they are standing in the middle of that task, and they arrive at the text with a question. Deciding to read the text from the start is rare; the norm is scanning. The eye jumps from heading to heading, stops if one resembles the question, reads what is under it, and moves on if it does not.
Scannable text is text built around this behavior. The measure of scannability is not aesthetic; it is the number of units the reader has to read before reaching the sentence they are looking for. This number can be measured, because both headings and claims are countable units.
The measure’s definition is this: reading a heading and saying “not my question” is one step; reading a claim and saying “not my answer” is also one step. Both jobs spend the reader’s attention to the same degree. This equating may look crude, but converting both into the same unit is the only way to see what headings actually buy.
The Axis of Sectioning
Sectioning a document has more than one axis, and the axis chosen sets the cost of the search. The course’s first topic already gave one axis: type. A second axis comes from the product itself: topic. A third option is nesting the two.
# example heading layout, not executed
type-headed topic-headed type + topic
Tutorial Setup Tutorial
How-To Starting a Setup
Explanation Measurement Starting a
Reference Output Format Measurement
Authorization Output Format
Error ...
Settings How-To
Setup
...
The reader uses these two axes with different confidence. Type they infer from the shape of the question: a reader asking “where do I start” looks for the tutorial, one asking “what type is this field” looks for the reference. Topic they already know; the reader is in the middle of their task and is aware which topic they are stuck on. Since both axes are accessible to the reader, both are workable for sectioning.
Not sectioning is a choice too. Headingless text is a single flow, and the reader’s only option is scanning from the start. The measurement carries this option too, as a fourth row, because it is the point of comparison.
The measurement’s assumptions:
- WD11 — The 56 claims come from the shared fixture: four types, four surfaces, 17 claims embedded in a runnable example. The oracle is the fixture itself.
- WD12 — The only thing this lesson adds is every claim’s topic; the topic is chosen from six headings by the fixture’s generator and does not look at the claim’s type.
- WD13 — A reader visit is a question–topic pair. All combinations of the six reader questions and the six topics are tried: 36 visits. The questions come from the shared fixture, and two of them fall into no type.
- WD14 — Reading a heading is one step, reading a claim is one step. The reader reads headings in order until they reach a matching one; they read the matching section’s claims in order too.
- WD15 — The reader correctly infers type from their question and topic from their own situation. The measurement is not a comprehension measurement, it is a distance measurement.
- WD16 — If the answer is not found, the reader is counted as having scanned every heading and every claim; the cost of a not-found visit is the layout’s total unit count.
- WD17 — A heading is itself a claim and is bound to a surface: type names are bound to concept, topic names to name. A heading cannot be embedded in a runnable example; this is why every heading that goes stale goes stale silently.
Measurement
"""Structure and headings: in how many steps is the reader's question reached. Part 1 - the spread of claims across topics and 36 reader visits. Part 2 - step count in four heading layouts, and where not-found comes from. Part 3 - a heading is a claim too: the true/stale/silent table of the heading set. """ SEED = 20260816 TYPES = { "tutorial": {"flow": 9, "signature": 3, "name": 2, "concept": 1}, "how-to": {"flow": 6, "signature": 5, "name": 2, "concept": 1}, "explanation": {"flow": 1, "signature": 1, "name": 1, "concept": 9}, "reference": {"flow": 0, "signature": 13, "name": 2, "concept": 0}, } FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12} EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10, "reference": 0.00} QUESTIONS = {"where do I start": "tutorial", "how do I do this": "how-to", "why is this like this": "explanation", "what type is this field": "reference", "what changed in which version": None, "why isn't this working": None} TOPICS = ("setup", "starting a measurement", "output format", "authorization", "error", "settings") VERSIONS = (0, 1, 2, 3, 6, 12) # A heading is itself a claim and goes stale according to the surface it is bound to. HEADING = {"headingless": {}, "type-headed": {"concept": 4}, "topic-headed": {"name": 6}, "type + topic": {"concept": 4, "name": 24}} def rng(seed): d = seed % 2147483646 + 1 def r(n): nonlocal d d = (d * 48271) % 2147483647 return d % n return r def claims(): entries, no = [], 0 for kind, mix in TYPES.items(): embedded_target = 0 for surface, count in mix.items(): for _ in range(count): no += 1 embedded_target += EMBEDDED[kind] embedded = embedded_target >= 1 and surface != "concept" if embedded: embedded_target -= 1 entries.append({"no": no, "type": kind, "surface": surface, "embedded": embedded}) return entries def assign_topic(entries, seed=SEED): """This lesson's one addition: every claim's topic.""" r = rng(seed) for i in entries: i["topic"] = TOPICS[r(len(TOPICS))] return entries def stale(surface, version): return version // FREQUENCY[surface] >= 1 A = assign_topic(claims()) VISIT = [(s, k) for s in QUESTIONS for k in TOPICS] SECTION = {"headingless": ([None], [None]), "type-headed": (list(TYPES), [None]), "topic-headed": ([None], list(TOPICS)), "type + topic": (list(TYPES), list(TOPICS))} def search(visit, layout): """Reading a heading and reading a claim both cost one step; a miss scans everything.""" question, topic = visit kind, step = QUESTIONS[question], 0 upper, lower = SECTION[layout] for u in upper: if u: step += 1 if u != kind: continue for l in lower: if l: step += 1 if l != topic: continue for i in A: if (u and i["type"] != u) or (l and i["topic"] != l): continue step += 1 if i["type"] == kind and i["topic"] == topic: return step, True return sum(HEADING[layout].values()) + len(A), False print(f"claims {len(A)} | topics {len(TOPICS)} | reader visits {len(VISIT)}") print("topic distribution: " + ", ".join( f"{k} {sum(i['topic'] == k for i in A)}" for k in TOPICS)) gap = [v for v in VISIT if QUESTIONS[v[0]] and not any( i["type"] == QUESTIONS[v[0]] and i["topic"] == v[1] for i in A)] print(f"visit falling into a type {sum(1 for s, _ in VISIT if QUESTIONS[s])} | " f"falling into no type {sum(1 for s, _ in VISIT if not QUESTIONS[s])} | " f"type exists but no matching claim {len(gap)}") print("coverage gaps:", [(QUESTIONS[s], k) for s, k in gap]) print() print(f"{'heading layout':>14s} {'headings':>8s} {'average step':>13s} {'worst':>7s}" f" {'found':>7s} {'not found':>11s}") for layout in HEADING: result = [search(v, layout) for v in VISIT] found = [a for a, b in result if b] print(f"{layout:>14s} {sum(HEADING[layout].values()):8d}" f" {sum(found) / len(found):13.2f} {max(found):7d}" f" {len(found):7d} {len(result) - len(found):11d}") print() print("heading claims - true/stale/silent") print(f"{'heading layout':>14s}" + "".join(f" {'v' + str(s):>8s}" for s in VERSIONS)) for layout, mix in HEADING.items(): total, line = sum(mix.values()), f"{layout:>14s}" for s in VERSIONS: stale_ = sum(n for y, n in mix.items() if stale(y, s)) line += f" {f'{total - stale_}/{stale_}/{stale_}':>8s}" print(line)
claims 56 | topics 6 | reader visits 36
topic distribution: setup 13, starting a measurement 8, output format 11, authorization 10, error 7, settings 7
visit falling into a type 24 | falling into no type 12 | type exists but no matching claim 3
coverage gaps: [('tutorial', 'starting a measurement'), ('how-to', 'settings'), ('reference', 'output format')]
heading layout headings average step worst found not found
headingless 0 26.38 56 21 15
type-headed 4 7.29 19 21 15
topic-headed 6 7.90 14 21 15
type + topic 28 7.00 11 21 15
heading claims - true/stale/silent
heading layout v0 v1 v2 v3 v6 v12
headingless 0/0/0 0/0/0 0/0/0 0/0/0 0/0/0 0/0/0
type-headed 4/0/0 4/0/0 4/0/0 4/0/0 4/0/0 0/4/4
topic-headed 6/0/0 6/0/0 6/0/0 6/0/0 0/6/6 0/6/6
type + topic 28/0/0 28/0/0 28/0/0 28/0/0 4/24/24 0/28/28
What the Heading Shortens
The top table’s biggest gap sits between the first and second rows. In headingless text, the reader reaches the claim they are looking for in an average of 26.38 steps; in the worst case, 56 units — the whole document. Adding four headings brings the average down to 7.29. Writing four lines cuts search cost to a quarter.
The second gap is much smaller, and it is the lesson’s actual finding. Six topic headings give 7.90 — worse than four type headings. The layout nesting both axes gives 7.00, only 0.29 steps better than the type-headed layout. Twenty-eight headings add a quarter of a step on top of what four headings already give.
Why so little? Because the scan inside a section is already short. Four types split 56 claims into groups of 15, 14, 12, and 15; a second axis brings one of those groups down to an average of two or three claims, but by the time the reader reaches that group, they have already read six more sub-headings. The scanning gained is almost exactly traded away by the heading-reading paid.
The worst-case column says something different: 56 / 19 / 14 / 11. A deeper layout barely moves the average but fixes the worst case. Sectioning depth is a decision made not for the average reader, but for the unluckiest one.
These rows point to a second lever too. In the type-headed layout, heading-reading is at most four steps; the rest of the average is scanning inside a section. So a section’s internal order matters as much as the heading count. Moving a frequently asked claim to the top of a section does the same job without opening a new heading tier. This is where the two types’ traditional ordering comes from: a tutorial is ordered by sequence of steps, because the reader carries out steps in order; a reference is ordered alphabetically, because the reader knows the name they are looking for and uses the order like an index. Both orderings answer the same question — how does the reader decide the next unit?
The Fifteen Visits That Are Never Found
15 of the thirty-six visits find an answer in none of the four layouts, and this number does not change from layout to layout. Headings cannot make findable what a text does not contain. The fifteen failures come from two separate defects, and confusing them is costly.
The first is 12 visits: the questions “what changed in which version” and “why isn’t this working” fall into no type. This is not a writing defect; it is the limit of the taxonomy the shared fixture declares — the course’s release-notes and troubleshooting lessons build these two questions as separate types. For these visits, what is needed is not adding a heading but writing the missing type.
The second is 3 visits: the question falls into a type, but not a single claim on that topic was written in that type. The measurement names them one by one — starting a measurement in tutorial, settings in how-to, output format in reference. This is a coverage gap, and it is directly a writing list. Heading layout makes these gaps visible; in headingless text, the same gap is a silent conclusion the reader reaches only after scanning all 56 units.
The cost of a not-found visit rises as the layout gets deeper: 56 units are scanned in headingless text, 84 units in the two-tier layout. A deep layout speeds up what is found while making what is not found more expensive. If a document has many coverage gaps, going deeper is the wrong investment.
A Heading Is Also a Claim
The bottom table brings headings into the course’s own axis. A heading is a sentence and is bound to a surface. Type names are bound to concept: tutorial, how-to, explanation, and reference are independent of the product’s naming and stay true up to the twelfth version — in the table, the four type headings run at 4/0/0 and only turn 0/4/4 at version 12.
Topic names are bound to name, and a name changes once every five versions. Six topic headings give 0/6/6 at the sixth version. The two-tier layout carries both: 4/24/24 at version 6, 0/28/28 at version 12. When a product feature’s name changes, the twenty-four sub-headings carrying that name go wrong at the same instant.
The third column is this table’s real news: every stale heading is silent. A heading cannot be embedded in a runnable example; no test reports that a heading names something that no longer exists. The document builds, the page opens, the heading shows up, and it is wrong.
The decision follows from reading these two tables together. The two-tier layout gains 0.29 steps on average, and in return carries 24 silently stale headings at the sixth version. Bringing the worst case from 19 down to 11 is a real gain, but its price is a maintenance debt. The justification for going deeper cannot be the average; it can only be the worst case, or the visibility of coverage gaps.
Summary
- The measure of scannable text is not aesthetic; it is the number of units the reader reads before reaching their question. Reading a heading and reading a claim convert to the same unit.
- Headingless text takes an average of 26.38 steps, four type headings 7.29, six topic headings 7.90, the two-tier layout 7.00. The first four headings cut cost to a quarter; twenty-four sub-headings add only 0.29 steps.
- The worst case falls as 56 / 19 / 14 / 11: going deeper is a decision made not for the average reader, but for the unluckiest one.
- 15 of thirty-six visits find no answer in any layout; 12 are the taxonomy’s limit, 3 are coverage gaps. Heading layout makes a gap visible, it does not close it; the cost of a not-found visit rises from 56 to 84 units in the deep layout.
- A heading is a claim: type names are bound to concept, topic names to name, and the two-tier layout carries 24 silently stale headings at the sixth version — since no heading can be embedded in an example, heading staleness is always silent.
Next Step
Every measurement here rests on a single silent assumption: the reader can match the word in their own question to the word in the heading. If a topic heading says “authorization” and the reader calls the same thing “permission,” the search breaks at the very first step — the reader misses the match, enters the wrong section, or assumes the text holds two separate things. The next lesson measures this matching: how much does calling the same concept by two names lower the number of claims a reader can find, and what exactly does each rule of a style guide measure.
To keep your progress and take notes, Log in
My notes
Log in to take notes.