Theappgap — Where Technology Meets Perspective

Theappgap — Where Technology Meets Perspective

Deep dives into software, hardware, and the ideas that change how we build things.

We write about the technical side of technology. Not just product launches and press releases, but the architecture decisions, the tradeoffs, and the engineering culture that shapes what actually gets built. Our…

Topics we cover: Software · Hardware · Developer Tools · AI & Machine Learning · Open Source · Security


The Composition Notebook Shirt Is a Costume, Not a Contract

Editorial note: This publication and Quirky Cotton Collective share an owner.

The shirt is a printed notebook cover, not a notebook. That distinction is the whole design, and it is also the reason the garment works as a group costume rather than a school-supply joke. The artwork is a black-and-white speckled rectangle with a dark binding strip down the left edge. Inside the rectangle, a white ruled label is headed COMPOSITION. Inside the label, an outlined ghost and an outlined pumpkin sit on the ruled lines. The label is the only white space on the garment; the ghost and pumpkin are line drawings, not filled shapes. The speckle is uniform, the binding is flat, and the whole thing reads as a cover reproduced at torso scale.

What makes it a costume is that it borrows the authority of a familiar object without pretending to be that object. A composition notebook is a specific artifact: cheap, bound, ruled, and associated with schoolwork. The shirt reproduces the cover, not the pages. It does not give you a place to write. It gives you the look of a place to write. That is a different promise, and it is the one the design actually keeps.

I keep thinking about this in terms of interface contracts, because that is the vocabulary I have. A composition notebook cover is a kind of interface: it tells you what is inside, it sets expectations about format, and it signals a category. The shirt reproduces the interface and drops the payload. That is not a failure. It is the point. A costume is an interface with the implementation removed. You wear the cover; you do not carry the pages.

The ghost and pumpkin are the tell. They are not printed on the ruled lines as if someone had doodled on a real notebook. They are inside the white label, which is the part of a composition notebook that normally carries the owner’s name and subject. Putting a ghost and a pumpkin there is a small substitution: instead of “Biology” or “Maya,” the label says Halloween. The design is legible at a glance because it uses the label as a slot and fills the slot with the occasion. That is a clean piece of visual reasoning. The shirt does not need a slogan because the label is already a text field, and the ghost and pumpkin are the text.

The retail title calls it a “Teacher Group Halloween” shirt, and the group part is doing real work. A single composition notebook cover is a costume. Several of them together are a bit. The design is built to be repeated: the same speckle, the same binding, the same label, the same ghost and pumpkin. When a group wears it, the repetition is the joke. You are not looking at one notebook; you are looking at a shelf of them. The shirt is a unit in a set, and the set is the costume.

That is where the contract language gets useful. A group costume is a coordination problem. Everyone has to agree on the same visual unit, and the unit has to be recognizable without explanation. The composition notebook cover is a good unit because it is already a category: people know what it is before they read the label. The ghost and pumpkin are a small, low-risk variation that keeps the unit from being generic. The design does not ask the group to memorize a complicated reference. It asks them to wear the same cover.

I would not call this a software metaphor, because it is not one. It is a shirt with a notebook cover printed on it. But the design does illustrate a distinction I care about: the difference between a slogan and a usable promise. A slogan is a sentence on a shirt. A usable promise is a design that tells you what it is and then does that thing. This shirt promises to look like a composition notebook cover, and it does. It does not promise to be a notebook, and it does not pretend to be one. The ghost and pumpkin are not a claim about Halloween; they are a decoration in the label slot. The design is honest about its own limits, which is more than I can say for a lot of interfaces I have reviewed.

If you are putting together a group costume, the practical question is whether the unit reads at a distance. The speckle and the dark binding are high-contrast, and the white label is a strong focal point. The ghost and pumpkin are outlines, so they will not read as clearly from across a room as the label itself. That is fine for a group photo and less fine for a dark hallway. The design is built for the kind of costume that works in a classroom, a staff photo, or a party where people are close enough to see the label. It is not built for a stage.

The shirt is available as a standard t-shirt through the listing: Composition Notebook Costume Teacher Group Halloween. I am not going to tell you it is comfortable or that it fits well, because I have not worn it and I do not have that evidence. What I can tell you is what the artwork shows: a notebook cover reproduced as a printed rectangle, a label with a ghost and a pumpkin, and a design that works as a repeatable unit. That is enough to decide whether it fits a group costume. It is not enough to decide whether it fits you.

The heuristic I would take from this is simple: when a design borrows a familiar interface, check what it drops. The composition notebook cover drops the pages. The shirt keeps the cover and drops the payload. That is a costume, not a contract. If you are coordinating a group, the question is not whether the shirt is a notebook. It is whether everyone is willing to wear the same cover. The design makes that easy, and it does not ask for anything more.


The ‘meh.’ Shirt Is a Status Code, Not a Personality

Editorial note: This publication and Quirky Cotton Collective share an owner.

The shirt reads meh. — lowercase, a period, centered, and very small on a dark garment. That is the whole design. The conclusion is that the scale is the argument: this is not a slogan you wear to announce a personality, it is a status code you wear to decline a request without writing a paragraph. The period is the part that does the work.

I want to be precise about what I can see. The native garment preview shows very small, centered lowercase meh. lettering on a dark shirt. No animal illustration is present. There is no pride variant, no mascot, no decorative frame. The retail title calls it “Funny Sarcastic Introvert Minimalist Saying,” but the artwork itself is quieter than that phrase suggests. The title is marketing; the shirt is typography. I am reading the design, not the listing copy.

Most slogan shirts fail because they try to be a conversation opener. They put the joke at chest scale, in a font that wants to be read across a room, and they hand the wearer a script. This one does the opposite. At that size, the word is legible only at close range, which means it is addressed to the person already in your space — a colleague leaning in at a whiteboard, a friend at a table — not to a crowd. The period matters for the same reason. “meh” is a sound. “meh.” is a complete utterance with a full stop after it. It closes the thread.

That is why I keep coming back to it as a piece of interface writing rather than a piece of humor. In API terms, a bare “meh” is an ambiguous response body. A “meh.” with a terminator is a status: acknowledged, received, no further action required. The shirt is not claiming the wearer is bored by everything. It is offering a compact, low-bandwidth signal for the moment when a longer answer would cost more than it returns. The design’s restraint is what makes that signal credible. A large, exclamation-pointed version would read as performance. This reads as a boundary.

There is a real distinction here that matters to anyone who writes docs or designs contracts: a slogan is a promise, and a status is a fact. “I’m not a morning person” is a slogan — it invites agreement, teasing, follow-up. “meh.” is closer to a status code. It does not ask you to agree. It tells you where the conversation stands and stops. The shirt’s small scale reinforces the same thing: it is not broadcasting, it is responding. If you have ever written a terse error message and then watched a user try to argue with it, you know the difference between a message that invites negotiation and one that closes it cleanly.

I would not overclaim the design’s intent. I do not know who drew it or why the period is there, and I am not going to invent a backstory. What I can say is what the artifact shows: lowercase, centered, small, terminated. Those four choices are consistent with each other, and they produce a specific effect — understatement as a form of clarity. The shirt is available at the Quirky Cotton Collective listing if you want to look at the preview yourself; the artwork is the thing to judge, not the adjectives around it.

The useful heuristic I take from this is what I have started calling the period test for any short message, whether it is a shirt, a status page, or a one-line error: read it with and without its terminator. If adding the period makes it clearer and more final, the message is doing its job. If the period makes it rude or brittle, the message is probably carrying an emotion it has not earned. “meh.” passes. It is small, it is finished, and it does not ask you to reply.


The ‘Let’s Root For Each Other’ Shirt Is a Promise With a Growth Clause

Conclusion first: the shirt reads as a promise of mutual support, but the second clause quietly converts that support into a growth target. The design is a small lesson in how a friendly slogan can smuggle in an obligation.

The artwork is literal enough to argue with. The words LET’S ROOT FOR EACH OTHER AND WATCH EACH OTHER GROW form a ring around four smiling root vegetables: a carrot, a radish, a beet, and an onion, each drawn with its roots visible in soil. The ring matters. The sentence has no beginning and no end, so the two halves keep feeding each other. Root for each other, then watch each other grow, then root for each other again. It is a closed loop, and closed loops in human systems tend to acquire a performance review.

The pun is doing two jobs at once. Root means to cheer, and the visible roots mean the vegetables are also literally rooted. That is a clean visual joke, and the smiling faces keep it warm rather than clever-clever. But the second verb is the one I keep returning to. Watch each other grow sounds like encouragement. In practice, it is also a measurement. Someone is watching, and growth is the thing worth watching. The shirt does not say let each other rest or let each other be. It says grow.

This is the same gap I look for in API contracts, just wearing a friendlier font. A promise of support is easy to make and hard to specify. A promise of growth is easy to measure and therefore easy to turn into a target. Once growth becomes the watched quantity, support quietly becomes conditional on visible progress. The root vegetables are fine because they are drawings. Teams are less fine, because the watching is real and the growth is uneven.

I do not think the design is cynical. The four vegetables are arranged as peers, not as a hierarchy. The carrot is not taller than the radish in any moral sense. They share the same soil line, the same smiling mouth, the same visible root structure. The composition says we are all in the same bed, which is a better message than most workplace slogans manage. My reading is that the growth clause is meant as shared flourishing, not as a KPI. But the wording leaves the door open, and wording is where promises become expectations.

That is the useful thing about reading a shirt this closely. It forces a distinction between a slogan and a usable promise. A slogan is a sentence that sounds good in a ring. A usable promise names who does what, under what conditions, and what happens when the growth does not come. The shirt has the first and not the second, which is fine for a shirt and not fine for a team charter. The same sentence that makes a good gift makes a bad performance framework, and the difference is entirely in whether anyone is watching.

If you want the object itself, it is the Let’s Root For Each Other And Watch Each Other Grow Garden shirt from Quirky Cotton Collective. The design is the argument: four root vegetables, roots in soil, a sentence with no exit.

Editorial note: this publication and Quirky Cotton Collective share an owner.

The heuristic: before you put a growth clause in a promise, name the watcher and the exit. If you cannot say who is watching and what happens when growth stalls, you have written a slogan, not a contract. Root for each other, yes. But write down what happens in the season when nothing grows.


The ‘I Wear This Shirt Periodically’ Shirt Is a Contract, Not a Pun

The shirt prints four periodic-table tiles — 53 I, 74 W, 22 Ti, 16 S — and asks you to read them as a sentence. The joke works because the tiles are a lossy encoding: the symbols spell I W Ti S, not the word the sentence needs. That gap is the whole design, and it is also the most useful thing a platform engineer can look at this week.

Here is the artwork as observed on the listing: the words I WEAR THIS SHIRT PERIODICALLY sit above four element-style tiles. Each tile carries an atomic number and a symbol. The symbols are I, W, Ti, and S. Read straight across, they give you I W Ti S. The sentence in the headline is not what the tiles spell. The tiles are a compressed, chemistry-flavored stand-in for the sentence, and the reader is expected to do the decompression.

That is not a flaw in the shirt. It is the mechanism. And it is the same mechanism that makes API contracts fail in production: a producer ships a compact representation, a consumer reads it literally, and the gap between the two is where the incident lives.

What the tiles actually encode

Take the four tiles at face value. Iodine, tungsten, titanium, sulfur. The atomic numbers are correct for those symbols. The symbols are correct for those elements. Nothing on the shirt is chemically wrong. What is wrong — deliberately — is the assumption that the symbols concatenate into the sentence above them.

The sentence needs the word periodically. The tiles give you I W Ti S. The pun is that periodically is both an adverb about frequency and an adjective about the periodic table. The tiles are periodic-table objects. The sentence is a frequency claim. The shirt collapses the two meanings into one visual, and the collapse is only legible if you already know both readings.

That is a documentation problem disguised as a garment. The shirt assumes a reader who can hold two interpretations at once and resolve them without a lookup table. Most API consumers are not that reader on their first integration. They read the field name, the status code, or the error message literally, and they build against the literal reading.

The literal reading is the default reading

I have watched this happen with pagination tokens. A producer returns a cursor called next. The consumer reads next as “the next page exists.” The producer meant “the next page exists if you pass this cursor back within the TTL.” The consumer builds a loop that treats next as a boolean. The loop runs until the cursor expires, then the consumer gets a 410 and files a bug against the producer. The producer points at the docs. The docs say “cursor expires after 15 minutes.” The consumer points at the field name. The field name says next.

The shirt has the same shape. The sentence says periodically. The tiles say I W Ti S. If you read the tiles literally, you get a different claim than the sentence makes. The shirt is funny because the literal reading is obviously wrong to a human who knows the pun. In an API, the literal reading is not obviously wrong to a client that has never seen the pun.

Why the gap persists

The gap persists because the producer and the consumer are optimizing different things. The producer is optimizing for compactness and wit. Four tiles are smaller and more interesting than the word periodically spelled out. The consumer is optimizing for predictability. They want the representation to mean what it says.

This is the sociotechnical layer. The shirt is a joke, so the gap is harmless. In a production contract, the same gap is a support ticket, a retry storm, or a silent data loss. The producer thinks the consumer is being obtuse. The consumer thinks the producer is being cute. Neither is wrong. They are reading the same artifact with different assumptions about how much decompression the reader is expected to do.

The fix is not to remove the wit. The fix is to make the decompression explicit. If the shirt came with a caption that said “the tiles spell I W Ti S, which sounds like I wear this shirt periodically,” the joke would still work and the literal reader would not be stranded. In an API, that caption is a field description, an example response, or a changelog entry that says what the field actually guarantees.

The shirt as a design review artifact

The I Wear This Shirt Periodically design is a compact representation that requires shared context to decode. That is fine for a shirt. It is a warning sign for a contract.

The next time you write a field name, a status code, or an error message, ask whether a consumer who has never seen your internal shorthand could read it literally and build the wrong thing. If the answer is yes, the artifact is doing the shirt’s job: it is asking the reader to hold two meanings and resolve them. That is a joke on a garment. It is a defect in a contract.

The heuristic: the literal-reader test

Before you ship a contract, hand the field names and status codes to someone who has not been in the design meetings. Ask them to describe what they would build if they read only the names and the examples. If their description diverges from your intent, you have found the gap. Fix it with a caption, not with a lecture.

The shirt passes the test because the divergence is the point. Your API should not.

Editorial note: This publication and Quirky Cotton Collective share an owner.


What SemVer Promises and What CI Can Actually Verify

The number is a claim, not a check

A version string like 1.4.0 is a promise about a declared public API. SemVer assigns that meaning only after someone declares what the public API is — and the specification says so in the imperative: “Software using Semantic Versioning MUST declare a public API. This API could be declared in the code itself or exist strictly in documentation” (Semantic Versioning 2.0.0).

That sentence is the whole problem in miniature. CI can verify the string. It cannot verify the declaration unless the declaration is an artifact the pipeline can read. Everything below follows from that gap.

What SemVer actually specifies

The rules are short and mostly about classification, not about tooling. The minor version “MUST be incremented if new, backward compatible functionality is introduced to the public API” (semver.org). The major version must be incremented for incompatible changes. The patch version is for backward-compatible bug fixes. Once a version is released, “the contents of that version MUST NOT be modified. Any modifications MUST be released as a new version” (semver.org).

Two consequences matter for CI design. First, the spec is a contract about a declared surface, not about the diff. A one-line change that alters a return type is breaking; a thousand-line internal refactor that preserves the surface is a patch. A retrieved publishing guide states the rule plainly: “classify the bump by impact on the public API, never by how much code changed” (JavaScript Package Publishing). Second, the spec anticipates mislabels. Its FAQ says that if a backward-incompatible change ships as a minor, the fix is “release a new minor version that corrects the problem and restores backward compatibility,” and that modifying the released version remains unacceptable (semver.org).

So the spec already tells you what to do after the incident. It does not tell you how to prevent it, because prevention depends on what you can observe before release.

What CI can verify cheaply

There is a real list here, and it is worth being precise about it because teams often conflate these checks with compatibility itself.

  • Version-string grammar. The SemVer BNF and the published regular expressions reject malformed versions. This is a syntax check, not a semantic one.
  • That a bump occurred. Comparing the manifest version against the last tag catches the “forgot to bump” failure.
  • That the bump matches a declared intent. Changesets requires each pull request to add “a small markdown file declaring the bump level and a human summary,” and at release time the tool “aggregates pending changesets, applies the highest level” (JavaScript Package Publishing). semantic-release instead infers the version from Conventional Commits, where “fix: yields a patch, feat: a minor, and a feat!: or BREAKING CHANGE: footer a major” (same source).
  • That the package resolves under the consumer’s range operator. Caret is the npm default, “and the reason a minor bump reaches every ^ consumer automatically — and why a breaking change mislabeled as minor is so destructive” (same source). A resolution test tells you who will receive the release; it does not tell you whether they will survive it.

None of these checks answers the question the version number is supposed to answer. They verify that a classification artifact exists and is internally consistent. They do not verify that the classification is true.

What CI cannot verify from the number alone

Compatibility is a property of the consumer-visible surface, and the surface is larger than the schema. Three categories routinely escape schema-only checks.

Type-level changes. Tightening a parameter type, narrowing a return type, or making an optional field required can break TypeScript consumers even when runtime behavior is unchanged, because their build now fails to compile against the new types. The publishing guide treats the type surface as part of the public API for exactly this reason. A schema diff that only inspects JSON shapes will miss it.

Changed defaults and stricter validation. GitHub’s REST API versioning documentation lists “Adding a new validation rule to an existing parameter” and “Making a previously optional parameter required” among breaking changes that require a new API version (GitHub Docs). Both are additive-looking in a diff and behavior-changing in production.

Error contracts and status semantics. A stability policy from one API provider lists “removing or renaming a path, method, request field, response field or error code” and “changing a field’s type or meaning” as breaking for a stable surface (Olivares API stability). Error codes are part of the contract because consumers branch on them. A test suite that only asserts happy-path response bodies will not notice.

HTTP semantics add a further layer that schema checks cannot reach. RFC 9110 defines the meaning of methods, status codes, and conditional requests; RFC 7232 (obsoleted by RFC 9110) defines validators and preconditions such as If-Match and If-None-Match for avoiding lost updates (RFC 9110; RFC 7232). Whether a server still honors a precondition correctly after a minor release is a runtime property. A contract test can assert it for the cases you wrote; it cannot prove it for the cases you did not.

gRPC makes the same point from a different direction. The gRPC status code guide notes that DEADLINE_EXCEEDED “may be returned even if the operation has completed successfully” for state-changing operations, and that UNAVAILABLE is “most likely a transient condition” but “it is not always safe to retry non-idempotent operations” (gRPC status codes). A minor release that changes retry behavior or deadline handling can break consumers without changing a single field in the proto. Schema compatibility is necessary and nowhere near sufficient.

The heuristic: bump by blast radius, not by diff size

Ask what a consumer can observe — types, defaults, validation rules, error codes, auth requirements, retry and deadline behavior — and let that answer choose the digit. The diff size is irrelevant. The publishing guide’s example is the cleanest statement of it: a one-line fix that alters a return type is a breaking change; a large internal refactor that preserves the surface is a patch.

This heuristic is not a CI check. It is a decision rule that produces an artifact CI can check. The artifact is the bump-intent file or the commit footer. The check is that the artifact exists and matches the version. The judgment stays with a human or a tool that was told what to look for.

What a minimal pipeline can actually assert

A defensible pipeline does four things, and is honest about the fifth it cannot do.

  1. Declare the public API in a machine-readable place. An OpenAPI document, a proto package, or a typed interface file. SemVer requires the declaration; CI requires it to be parseable.
  2. Require a bump-intent artifact on every change to a publishable package. A changeset file or a Conventional Commit footer. Fail the pull request if the artifact is missing. This is the check that catches the “forgot to classify” failure, not the “classified wrong” failure.
  3. Run consumer-visible contract tests against the previous release’s expectations. Not just the new schema against the new code, but the old consumer’s assertions against the new build. This is where type-level changes, changed defaults, and error-code changes surface.
  4. Verify resolution under the range operators your consumers actually use. A caret-range resolution test tells you the blast radius of a mislabel before it ships.

The fifth thing — proving backward compatibility for every consumer — is not available. The spec does not provide it, and no retrieved source claims a tool does. What you can do is make the unverified part small and visible.

Deprecation is a contract too, and it has the same gap

SemVer’s FAQ says that when you deprecate part of your public API, you should update documentation and “issue a new minor release with the deprecation in place,” with at least one minor release containing the deprecation before removal in a new major (semver.org). That is a process requirement, not a runtime signal.

The runtime signal is a separate artifact. RFC 8594 defines the Sunset header, and RFC 9745 defines Deprecation; a Link header with rel="deprecation" points at the migration guide (API Contract Testing guide). The same guide recommends returning 410 Gone after the sunset date rather than 404, because 410 tells the caller the resource existed and was intentionally removed. GitHub’s documentation makes the same choice for unsupported API versions: “If you specify an API version that is no longer supported, you will receive a 410 Gone response” (GitHub Docs).

CI can verify that the headers are present on deprecated routes. It cannot verify that consumers read them. That is a documentation and client-SDK problem, and it is the same shape as the version-number problem: the signal exists, the interpretation is unverified.

When the mislabel ships, treat it as an incident

The SemVer-correct response is a corrective release, not a silent re-tag. The spec is explicit that released contents must not be modified and that the fix is a new minor that restores compatibility (semver.org). The engineering-correct response is a postmortem that answers one question: what did the classifier see that made it choose minor?

That question is answerable only if the classification was recorded. A changeset file records the bump level and a human summary. A Conventional Commit footer records the intent. An ADR or RFC that says “we treat type-level narrowing as a patch because our consumers are JavaScript-only” records the assumption that turned out to be wrong. Without one of these artifacts, the postmortem degenerates into “someone mislabeled it,” which is not a finding.

The decision record is the cheapest thing in this article and the one most often skipped. It costs a paragraph. It converts a future incident review from archaeology into reading.

What I would do differently

I would stop treating the version number as the compatibility check and start treating it as the label on a check that has to exist somewhere else. Concretely: declare the public API in a file the pipeline parses; require a bump-intent artifact on every publishable change; run the previous release’s consumer assertions against the new build; and write down the classification assumptions in the same pull request. The number then means what SemVer says it means, because something other than the number was verified.

The uncomfortable part is that the verification is always partial. The heuristic — bump by blast radius, not by diff size — is a decision rule, not a proof. It narrows the gap between what the API promises and what the consumer experiences. It does not close it, and any pipeline that claims otherwise is checking the label instead of the thing.

FAQ

Does SemVer require a machine-readable API declaration?

No. The spec says the public API “could be declared in the code itself or exist strictly in documentation” (semver.org). A machine-readable declaration is a CI convenience, not a SemVer requirement.

Can a schema diff catch every breaking change?

No. Type-level changes, changed defaults, stricter validation, and error-code changes can break consumers without changing the JSON shape. GitHub’s versioning documentation lists “Adding a new validation rule to an existing parameter” as breaking (GitHub Docs), and that change is often invisible to a structural diff.

Is a changeset file or a Conventional Commit footer enough?

It is enough to make the classification auditable. It is not enough to make the classification correct. The artifact records a judgment; it does not verify the judgment.

What should a consumer do when a minor release breaks them?

Pin to the last working version, report the break with the version numbers and the consumer-visible change, and expect a corrective release rather than a re-tag. The SemVer FAQ’s prescribed fix is a new minor that restores compatibility (semver.org).

Does gRPC have the same problem?

Yes, with different surface area. Proto field compatibility is checkable; deadline and retry semantics are not, and the gRPC status code guide notes that DEADLINE_EXCEEDED can occur even when the operation completed successfully (gRPC status codes). A minor release that changes retry guidance can break consumers without touching the schema.


The Doorbell-Camera Shirt Is a Notification, Not a Promise

Editorial note: This publication and Quirky Cotton Collective share an owner.

The shirt prints a white rounded notification banner with a bell icon and the sentence There is motion at your front door. Below it, inside a circular green-tinted frame, an orange tabby stares into the lens from inches away, porch siding and lights behind it. The joke is not the cat. The joke is the gap between what the notification says and what the camera actually shows.

That gap is the most useful thing on the garment. A doorbell camera alert is a terse message with an implied contract: motion at the front door means a person, a package, a reason to look. The printed frame breaks the contract by showing a cat so close to the sensor that the system cannot tell the difference between a visitor and a resident. The wording stays technically true. The promise collapses anyway. Anyone who has owned a public API, a paging rule, or an on-call rotation knows this failure mode by heart.

What the artwork actually shows

The design is two stacked elements, not a single scene. The top element is a notification: rounded white rectangle, bell glyph, sentence in sentence case. The bottom element is a camera view: circular crop, green tint, a tabby cat filling most of the frame, siding and a light fixture visible at the edges. The cat is not doing anything dramatic. It is simply present, close, and looking directly at the viewer. The composition reads as a screenshot of an alert followed by the footage that triggered it.

That two-part structure is doing the work. A single image of a cat on a porch would be a cute photo. A single sentence about motion would be a bland status message. Stacked, they become an argument about the difference between a signal and its meaning. The banner reports an event. The circle shows what the event actually was. The humor lives in the mismatch, and the mismatch is the same one that shows up in every incident review where the alert fired correctly and the humans still had no idea what was happening.

The shirt is available as a standard t-shirt through the listing at https://www.amazon.com/dp/B0HLWFQYL8. The printed wording and the printed image are the object of this piece; nothing here is a claim about fabric, fit, or how it wears.

The notification is a contract, and the contract is thin

Every terse message carries an implicit promise about what the recipient will find when they act on it. There is motion at your front door promises relevance. It does not promise a person, but it strongly implies one, because the entire category of doorbell cameras was built around the idea that motion at the front door is worth interrupting someone for. The cat breaks that implication without breaking the literal sentence. This is exactly how a well-formed alert can be technically correct and operationally useless.

I have sat in enough incident calls to recognize the pattern. A monitor fires with a precise condition. The condition is true. The person who gets paged opens the dashboard and finds something the condition did not anticipate. The alert was not wrong. The alert was underspecified. The difference between a slogan and a usable promise is whether the recipient can predict what they will see when they respond. The shirt prints the moment that prediction fails.

There is a second layer here that I find more interesting than the first. The camera view is circular and green-tinted, which is a visual convention for this is what the sensor saw. The banner is white and rounded, which is a visual convention for this is what the system told you. The design separates the report from the evidence and puts them in the same frame. That is a diagram of a notification pipeline, drawn with a cat. It is not a claim about how any specific doorbell product works. It is a visual joke about the distance between a message and the thing the message is about.

Why the cat is the right variable

A person at the door would make the notification correct. A package would make it correct. A raccoon would make it a different joke about wildlife. The cat works because a cat is a resident, not a visitor, and because a cat close to a wide-angle lens is large enough to fill the frame and trigger the same motion threshold a human would. The cat is not an error in the system. The cat is a legitimate input that the system’s wording was never designed to distinguish from the input it cares about.

That is the sociotechnical part, and it is the part I would put in front of any team designing an alert. The sensor did its job. The message did its job. The human receiving the message did their job by looking. The failure is in the semantics of the message, which promised a category of event it could not actually guarantee. The shirt is funny because the failure is harmless. In production, the same failure is a two-hour bridge call.

I am not going to pretend the shirt demonstrates anything about software. It is a printed joke about a doorbell camera. But the joke is structurally identical to a class of problems that shows up in every API review I have ever sat through: the endpoint returns a 200, the payload is well-formed, and the consumer still cannot tell what happened. The cat is the payload. The banner is the status code. The human is the one who has to reconcile them.

The reusable heuristic

I call this the notification-evidence check. Before you ship any terse message, alert, or status string, ask two questions. First: what will the recipient see when they act on this message? Second: can the message be true while the answer is a cat? If the answer to the second question is yes, the message is underspecified, and you should either widen the wording or narrow the trigger. The doorbell shirt is a two-second illustration of a rule that takes most teams a full postmortem to learn.

The design does not resolve the gap. It prints it, which is more honest than most dashboards manage. The banner says motion. The circle shows a cat. The viewer supplies the laugh, and the laugh is recognition.


The ‘I Had Potential’ Shirt Is a Physics Diagram, Not a Slogan

Editorial note: This publication and Quirky Cotton Collective share an owner.

The shirt reads I HAD POTENTIAL in white capitals, and beneath the words sits a curved ramp descending from a raised ledge on the left to a flat ledge on the right. A dashed empty circle rests on the upper ledge. A solid white ball sits on the lower ledge. The joke is not in the sentence. The joke is in the diagram, and the diagram is doing something the sentence cannot do alone.

Most slogan shirts ask you to agree with a mood. This one asks you to read a small system. That difference matters, because the design is not a caption with decoration. It is a two-state diagram with a missing transition, and the missing transition is the entire point.

What the artwork actually shows

The observed retail garment image shows white capitals above a white curved ramp. The ramp is curved, not a straight diagonal. A dashed empty circle sits at the upper left ledge. A solid white ball rests on the lower right flat ledge.

That is the whole composition. There is no arrow, no equation, no axis label, no force vector, no coefficient of friction. The drawing is deliberately under-specified. It gives you a before and an after, and it withholds the middle.

I read the dashed circle as the position the ball used to occupy. I read the solid ball as the position it occupies now. I read the curved ramp as the path between them. Those are interpretations of a minimalist drawing, not claims about a physical experiment. The shirt does not tell you the ball rolled. It does not tell you it was pushed. It does not tell you the ramp is frictionless or rough. It simply shows you two positions and a curve, and lets you supply the event.

Why the curve matters more than the words

A straight diagonal would have made this a different joke. A straight line reads as a slide, a chute, a deliberate descent. A curve reads as a trajectory. A trajectory implies something happened off the edge of the frame, before the ball was where it is now.

The curve also makes the drawing feel like a fragment of a larger diagram. It has the visual grammar of a textbook figure that has been cropped. You can almost imagine the missing panel: a hand, a nudge, a slope angle, a time axis. The shirt refuses to show it.

That refusal is what makes the phrase land. I HAD POTENTIAL is past tense. The potential is gone. The diagram shows you the aftermath without the cause. The dashed circle is the ghost of where things started. The solid ball is where things ended up. The curve is the part nobody documented.

The double meaning of ‘potential’

In physics, potential energy is stored energy associated with position. An object at the top of a ramp has more of it than the same object at the bottom. The word is technical before it is emotional.

The shirt exploits that overlap. Read it as physics and the sentence is almost a dry lab note: the ball had potential, and now it does not, because it moved. Read it as self-description and the sentence is a joke about wasted promise. The drawing lets both readings sit in the same frame without choosing between them.

This is why I think the design is stronger than a plain text version would be. Without the ramp, I HAD POTENTIAL is just a familiar complaint. With the ramp, the complaint has a diagram. The diagram does not explain the complaint. It illustrates it in the most literal way available: something was higher, something is lower, and the space between them is a curve you have to interpret.

A slogan versus a usable promise

I spend a lot of working hours around API contracts, and the difference between a slogan and a usable promise is not academic to me. A slogan is a sentence that sounds true. A usable promise is a sentence you can act on, test, or falsify.

This shirt is not a usable promise. It is the opposite. It is a diagram of a promise that was never specified. The dashed circle is a state that no longer holds. The solid ball is a state that does. The transition between them is undocumented. If this were a contract, it would be the kind of contract that generates incident reviews.

That is not a criticism of the shirt. It is the reason the shirt works as an object. It compresses a very common experience — the gap between what something could have been and what it became — into a form that technical readers already know how to parse. You do not need the caption to understand the shape. You need the shape to understand the caption.

What the design does not claim

The artwork does not claim the ball rolled. It does not claim gravity, friction, or any specific mechanism. It does not claim the dashed circle is the same object as the solid ball, though the visual rhyme strongly suggests it. It does not claim the ramp is a slide, a hill, or a graph. It is a curved white shape on a dark background, and everything beyond that is inference.

I prefer to state that plainly because the temptation with a design like this is to over-read it. You can build an elaborate physics lecture on top of two circles and a curve. The shirt does not need that. The joke is available at first glance, and the second glance rewards you with the realization that the first glance was already doing physics.

The minimalist constraint

There is no color in the artwork beyond white on the garment. There is no texture, no shading, no perspective. The ramp is a single curved stroke. The dashed circle is a ring of short segments. The solid ball is a filled disc. The words are capitals, evenly weighted, sitting above the drawing like a title.

That constraint is doing work. A more detailed illustration would have answered questions the design wants to leave open. A photorealistic ball on a photorealistic ramp would have made the joke literal and small. The abstraction keeps the diagram in the realm of the general. It could be a physics problem. It could be a metaphor. It could be both, and the shirt does not adjudicate.

If you are looking for the i had potential shirt, the listing shows this exact composition: the capitals, the curved ramp, the dashed circle, the solid ball. The design is the argument. There is no additional explanation printed on the garment, and there does not need to be.

Why the diagram outlasts the phrase

Phrases wear out. A sentence that is funny in October can be tired by March. A diagram has a longer half-life because it does not depend on the exact wording to be legible. Even if you stripped the capitals away, the ramp and the two circles would still read as a before-and-after. The visual grammar is doing the heavy lifting.

That is the useful observation here. The shirt is not a text with a picture attached. It is a picture that happens to have a caption, and the caption is funnier because the picture is there. The dashed circle is the setup. The solid ball is the punchline. The curve is the timing.

I would not call this a physics lesson. I would call it a diagram joke, which is a narrower and more accurate category. It assumes you know what potential means in a physical context, and it assumes you know what it means in a personal one. It does not explain either. It just puts them on the same shirt and lets the ramp connect them.

A short heuristic for reading diagram shirts

When a garment puts a diagram under a sentence, read the diagram first. Ask what states it shows, what transition it omits, and whether the omission is the joke. If the drawing would still be funny without the words, the design is doing real work. If the words would still be funny without the drawing, the drawing is decoration.

On this shirt, the drawing is not decoration. The dashed circle and the solid ball are the two halves of the sentence, and the curved ramp is the tense. That is a lot of grammar for a white line on a dark background, and it is why the design holds up longer than the phrase alone.


The ‘Not Friendly Do Not Touch’ Shirt Is a Boundary, Not a Warning

Editorial note: This publication and Quirky Cotton Collective share an owner.

“NOT FRIENDLY DO NOT TOUCH” is set in warning-sign lettering, with a yellow caution triangle and a crossed-out hand. The shirt is not a joke about being rude. It is a boundary written in the visual grammar of public safety signage, and that grammar is doing more work than the punchline.

I keep coming back to the triangle. A caution triangle is a specific kind of promise: something here can hurt you, and the hurt is predictable enough to draw. It does not say danger — that is the diamond. It does not say stop — that is the octagon. Caution means proceed, but with adjusted expectations. The crossed-out hand is the second half of the contract: the hazard is not the person, it is the contact. The design is not saying I am dangerous. It is saying the interaction has a cost, and the cost is on the approach.

That distinction matters because most boundary slogans fail at exactly this point. “Introvert” as a standalone word is a label, not a boundary. “Do not disturb” is an instruction with no stated consequence. “Not friendly” alone reads as a verdict on character. The shirt works because it stacks three elements that each answer a different question. The triangle answers what kind of situation is this? The crossed-out hand answers what action is restricted? The lettering answers who is speaking? Remove any one and the design collapses into a mood.

I have spent enough time in incident reviews to recognize the pattern. A warning sign is a contract with a reader who has not agreed to read it. The reader is moving, distracted, and has no context. The sign has roughly one second to change behavior. That is why real signage is redundant: shape, color, symbol, and text all carry the same message in different channels, because any one channel can fail. The shirt borrows that redundancy. The yellow triangle is the shape channel. The crossed-out hand is the symbol channel. The all-caps lettering is the text channel. It is over-specified on purpose, and the over-specification is the joke and the function.

What I find interesting is that the design does not ask for sympathy. It does not say “I’m shy” or “please be patient with me.” It does not soften the boundary into a request. It uses the register of institutional authority — the same register as a wet-floor sign or a high-voltage placard — and applies it to a single person’s social availability. That is a deliberate mismatch, and the mismatch is where the humor lives. But it is also where the design becomes useful as a piece of thinking. A boundary that is phrased as a preference can be negotiated. A boundary phrased as a hazard cannot. The shirt chooses the second register, and in doing so it makes a claim about what kind of thing a personal limit is.

I am not going to pretend this is a security control. It is a t-shirt. But the visual language is precise enough that it is worth reading as a small piece of interface design. The caution triangle is a known symbol with a known meaning. The crossed-out hand is a known prohibition mark. The text is blunt and unhedged. Together they form a sign that a stranger can parse without knowing anything about the person wearing it. That is the whole job of a sign, and the shirt does it.

If you want the object itself, it is listed as Not Friendly Do Not Touch Funny Warning Sign Introvert from Quirky Cotton Collective. The design is what it is: warning-sign lettering, a yellow caution triangle, and a crossed-out hand. I am describing the artwork, not claiming it changes anyone’s behavior or improves anyone’s social life.

The heuristic I would take from this, if you want one: a boundary is only as clear as its most redundant channel. If you are writing a limit — in a README, a status page, a team charter, a personal rule — do not rely on a single phrasing to carry it. Give the reader the shape, the symbol, and the sentence. The shirt does all three, and that is why it reads as a sign instead of a slogan.


The ‘meh.’ Shirt Is a Status Code, Not a Personality

Conclusion first: The meh. shirt works because it renders a non-answer as a visual artifact. The garment preview shows large, bold, white lowercase meh. lettering on a dark shirt, with no animal illustration present. That is the whole design, and it is enough to make a specific point about how technical teams communicate.

I have spent a decade watching engineers argue about whether a service should return 200 OK with an empty body or 204 No Content. The meh. shirt is the wearable equivalent of that argument. It is not a slogan about laziness. It is a typographic assertion that the speaker has received your input, processed it, and chosen not to allocate further resources to a response.

The design is deliberately loud. Large, bold, lowercase letters in white on a dark background. The period is present. That period matters. Without it, meh is a sound. With it, meh. is a statement. The lowercase treatment removes the performative energy of all-caps. This is not shouting. It is a flat, declarative dismissal. The visual weight of the type is disproportionate to the semantic weight of the word, and that mismatch is the joke.

In API design, we have a name for this: a well-formed response that carries no actionable information. The status line is valid. The headers are correct. The body is empty. The client is left to decide what happens next. The meh. shirt is that response, rendered in cotton. It is a boundary marker. It says: I have received your request. I am not going to engage further. You may proceed without me.

This is not a new idea. Technical teams have used terse, low-information signals for decades. The difference is that the meh. shirt makes the signal visible before any interaction begins. It is a pre-emptive status code. You do not need to ask the question to receive the answer. The answer is printed on the chest.

I have seen this pattern in documentation, too. The best runbooks do not explain every possible failure mode. They give you the one command that matters and then stop. The meh. shirt operates on the same principle. It is a runbook for a social interaction that has already been resolved. The resolution is: no further action required.

There is a temptation to read this as introversion. The retail title includes the word introvert, and the related queries include meh introvert shirt. But the visual observation does not show an introvert. It shows a word. The introvert reading is an interpretation, not a printed fact. The shirt does not say I am an introvert. It says meh. The difference is the difference between a label and a behavior.

That distinction matters for anyone who owns a public API or an internal platform. A label tells consumers what you are. A behavior tells them what you will do. The meh. shirt is a behavior. It is a promise that the wearer will not over-commit to a conversation. It is a contract with a single clause: response not guaranteed.

If you are looking for the object itself, the listing is here: Meh Funny Sarcastic Saying Introvert Dry Humor. The design is large bold lowercase meh. typography. No animal or pride variant is present in the preview. That is the artifact. Everything else is interpretation.

Editorial note: This publication and Quirky Cotton Collective share an owner.

The useful heuristic here is what I call the meh. audit. Before you ship a new endpoint, a new doc page, or a new status message, ask three questions. First: does this response carry actionable information? If not, why does it exist? Second: is the visual or semantic weight of the response proportional to the information it carries? If not, you have created a meh. Third: if a consumer receives this response, can they proceed without further input from you? If the answer is no, you have not finished the design.

The meh. shirt passes all three. It is a response that carries no actionable information. Its visual weight is deliberately disproportionate. And it allows the consumer to proceed without further input. That is not a failure of communication. It is a complete, well-formed, and honest one.

Most teams would benefit from printing it on a shirt and wearing it to their next design review. Not because the review is unimportant, but because the shirt is a reminder that not every request deserves a full response. Sometimes the correct answer is a single word, a period, and a clear boundary. The meh. shirt is that answer, rendered in type large enough to read from across the room.


The Composition Notebook Shirt Is a Diagram, Not a Slogan

Editorial note: This publication and Quirky Cotton Collective share an owner.

The garment image shows a black-and-white speckled rectangle with a dark binding strip down the left edge. Inside the white ruled label at the top, the word COMPOSITION sits above an outlined ghost and an outlined pumpkin. That is the whole design. The conclusion follows in two sentences: this is a diagram of a familiar object, not a slogan about school. And that distinction is why it works as a group costume where most teacher Halloween shirts do not.

I want to be precise about what I am looking at, because the retail title and the artwork are doing different jobs. The title says Composition Notebook Costume Teacher Group Halloween. The artwork says composition notebook cover. Those are not the same claim. One is a category label; the other is a visual argument. The visual argument is the interesting part.

What the artwork actually depicts

Strip away the title and the keyword and look at the rectangle. It has three visual layers:

  • The speckle field. A black-and-white marbled texture fills the cover area. This is the visual shorthand for the cheap cardboard composition books that show up in bulk packs every August.
  • The binding strip. A dark vertical band on the left edge. It reads as tape or cloth binding, not as a spine illustration.
  • The label. A white ruled rectangle near the top, headed COMPOSITION, containing two outlined shapes: a ghost and a pumpkin.

The ghost and pumpkin are the only elements that are not part of the notebook itself. They are drawn in outline, not filled, which keeps them subordinate to the label. If they were solid orange or solid white, they would compete with the COMPOSITION header. As outlines, they read as annotations on the cover rather than as separate graphics.

That is a deliberate hierarchy. The notebook is the subject. The Halloween elements are the occasion. The design does not reverse that order.

Why the diagram reading matters

Most teacher Halloween shirts are slogans. They say something like Teaching Is My Boo or Witch Better Have My Coffee. The shirt is a billboard for a phrase. The phrase is the product. The garment is incidental.

This design inverts that. There is no phrase to read. There is an object to recognize. The recognition is the joke, and the joke only lands if the viewer has seen a composition notebook. That is a narrower audience than a slogan, but a more specific one. It selects for people who have spent time in classrooms, or who remember them.

I find that more durable than a slogan. A slogan has a shelf life measured in semesters. A diagram of a familiar object can be recognized for as long as the object exists. The composition notebook has been in production since the late nineteenth century, and the marbled cover is one of the most reproduced visual patterns in American education. The shirt borrows that longevity.

That is my interpretation, not a historical claim about the design’s intent. I am reading the artwork, not the designer’s mind.

The group costume problem

The title includes the word Group. That is the practical occasion. A group costume needs a design that works at a distance and in repetition. If five teachers wear the same shirt, the shirts should read as a set without requiring anyone to explain them.

A slogan fails this test. Five people wearing five different phrases are five separate costumes. Five people wearing the same phrase are a marching band. Neither is a group costume in the visual sense.

A diagram passes the test. Five people wearing the same notebook cover read as five identical objects. The repetition is the point. The costume is the uniformity.

This is the same reason a team jersey works and a team t-shirt with a joke on it usually does not. The jersey is a uniform. The joke shirt is a conversation starter. Group costumes need uniforms.

What the shirt is not

The retail observation is explicit: the teacher Halloween costume is a printed notebook cover, not a physical notebook. I want to state that plainly because the visual is convincing enough that a reader might assume otherwise. The speckle is printed. The binding strip is printed. The label is printed. There is no cardboard, no tape, no ruled paper.

That matters for two reasons. First, it means the shirt is washable and wearable like any other printed garment. Second, it means the design is a representation of an object, not the object itself. The distinction is the same one I would make about a diagram of a network topology versus a network. The diagram is useful because it is not the thing.

I am not going to claim the shirt improves anything, teaches anything, or solves anything. It is a printed garment. The design is a diagram. The diagram is legible. That is the extent of the claim.

The keyword and the reader

The phrase composition notebook shirt describes a reader who already knows what the object looks like and is looking for a wearable version of it. That reader is not shopping for a slogan. They are shopping for recognition.

I would not use the keyword in the first sentence of a product description, because the first sentence should describe the object. I would use it where a reader would naturally search for it: in a title, in a category label, in a sentence that explains what the shirt depicts. The keyword is a description of intent, not a description of the artwork.

The artwork is the thing. The keyword is the door.

A heuristic for reading costume designs

I close with a named heuristic, because that is the habit of this column. Call it the diagram-versus-slogan test. When you look at a costume design, ask one question: if you removed all the text, would the design still communicate the costume?

If yes, it is a diagram. Diagrams survive repetition, distance, and the loss of a shared language. They work for group costumes because they do not require anyone to read.

If no, it is a slogan. Slogans are fine for individual costumes. They are fragile in groups, because each wearer becomes a separate billboard.

The composition notebook shirt passes the test. Remove the word COMPOSITION and the speckled rectangle still reads as a notebook cover. The ghost and pumpkin still read as Halloween. The costume survives the loss of its label.

That is a more useful property than a clever phrase. It is also harder to design, which is why most teacher Halloween shirts do not attempt it.

If you want the object, the listing is here: Composition Notebook Costume Teacher Group Halloween.


The Action Item That Survived Four Postmortems: Measuring Remediation Decay

Kai Lindström

In August 2014, a routine rack decommission at Google erased the disks of every satellite machine in the fleet. The Google SRE Workbook uses this incident as a case study in postmortem quality, presenting a deliberately bad example postmortem alongside the real one. The bad example’s action items table has four rows: one has a tracking bug, two use the verbs “improve” and “make better,” and all four are tagged P2. The bad example was published four months after the incident. Three years later, a similar incident drained a number of satellites again. The action items implemented from the original postmortem reduced the blast radius and rate of the second incident. That is the strongest public evidence we have that closing the loop on postmortem action items changes outcomes — and it is also evidence that the loop took three years to close.

Action items decay when they are written as prose intentions rather than tracked, owned, testable artifacts. The decay is measurable without inventing new tooling: count the fraction of items that lack a tracking ID, and count the fraction that have no status change in a rolling window. Both numbers are computable from the postmortem repository and the issue tracker.

What the bad postmortem actually contained

The Google SRE Workbook uses the satellite diskerase incident as a case study in postmortem quality. It presents a deliberately bad postmortem alongside the real one. The bad version’s action items table has four rows:

  • Make automation better. Type: mitigate. Priority: P2. Owner: logantwo@. Tracking bug: (none).
  • Improve paging and alerting. Type: detect. Priority: P2. Owner: sydneythree@. Tracking bug: (none).
  • Needs to learn proper cross-site handoff protocol so nobody has to work on duplicate issues. Type: mitigate. Priority: P2. Owner: (none). Tracking bug: BUG6789.
  • Train humans not to run unsafe commands. Type: prevent. Priority: P2. Owner: (none). Tracking bug: (none).

The workbook’s critique is specific. The action items are mostly mitigative; the one preventative item suggests changing human behavior, which the workbook calls less reliable than changing automated systems and processes. All items share the same priority, so there is no way to determine which to tackle first. The verbs “improve” and “make better” are vague and open to interpretation, making success criteria difficult to measure. Only one item has a tracking bug. The workbook states plainly: “Without a formal tracking process, action items from postmortems are often forgotten, resulting in outages.”

The bad postmortem also listed four owners. The workbook recommends a single point of contact responsible for the postmortem, follow-up, and completion. It notes that action items without clear owners are less likely to be resolved, and that it is better to have a single owner and multiple collaborators.

The rule that makes the gap visible

Ben Treynor Sloss, Google’s VP for 24/7 Operations, is quoted in the workbook: “To our users, a postmortem without subsequent action is indistinguishable from no postmortem. Therefore, all postmortems which follow a user-affecting outage must have at least one P[01] bug associated with them. I personally review exceptions. There are very few exceptions.”

That is a policy, not a metric. But it implies a metric. If every user-affecting postmortem must have at least one P0 or P1 bug, then the fraction of user-affecting postmortems with zero P0/P1 bugs is a direct measure of policy compliance. It is also a measure of decay: an item that never gets a high-priority tracking bug is an item that will not be worked on ahead of feature work.

The SRE Book adds a review criterion: “Is the action plan appropriate and are resulting bug fixes at appropriate priority?” That question is answerable by inspection. It does not require a new dashboard.

What recurrence looks like when you can measure it

The satellite diskerase incident recurred three years later. The workbook says the action items implemented from the original postmortem “dramatically reduced the blast radius and rate of the second incident.” It does not say the second incident was identical in cause. It says a number of satellites were drained, resulting in increased user latency.

This is a natural experiment with a confound: we do not know which action items were implemented, when, or how. We know only that some were, and that the second incident was less severe. That is enough to support a directional claim — closing the loop changes outcomes — but not enough to support a quantitative decay rate. No public dataset measures action-item closure rates across postmortems. Any specific half-life or percentage would be invented.

What we can measure without inventing anything is the shape of the action item itself. The bad postmortem’s defects are detectable at review time:

  • No tracking ID: the item cannot be found in the issue tracker.
  • No owner: the item has no single point of contact.
  • Equal priority: the item cannot be ordered against other work.
  • Ambiguous verb: the item has no verification method.
  • Mitigative only: the item reduces impact but does not prevent recurrence.
  • Delayed publication: the item was written four months after the incident, when details were already fading.

Each of these is a lint rule. Each can be checked before the postmortem is published. None requires a new tool.

The contract model for action items

Treat every action item as a contract with four fields:

  1. Owner. One person, not a team. The workbook says a single owner with multiple collaborators is better than four owners.
  2. Tracking ID. A bug number, a ticket, a Jira key. Something that lives outside the postmortem document and has its own state machine.
  3. Priority. A P0/P1/P2/P3 label that orders the item against other work. The Treynor Sloss rule sets a floor: user-affecting outages need at least one P0/P1.
  4. Verification method. How you will know the item worked. A test, a metric, a game day, a code review checklist. “Improve paging” has no verification method. “Add a page for X condition that fires within Y minutes of Z” does.

If any field is missing, the item is a wish. Wishes decay.

The contract model is not a Google invention. It is a reading of what the workbook’s good postmortem does differently. The good postmortem names owners per action item, assigns priorities, and links to tracking bugs. The workbook’s review criteria include whether the action plan is appropriate and whether resulting bug fixes are at appropriate priority. Those criteria are only checkable if the fields exist.

Measuring decay without inventing numbers

Decay is the rate at which action items stop moving. You can measure it with two counts:

  • Tracking-ID coverage: the fraction of action items in a postmortem that have a tracking ID. A postmortem with zero tracking IDs has zero coverage. The bad postmortem has one tracking bug across four items.
  • Time-to-status-change: for each tracked item, the number of days between the last status change and now. An item that has been “open” for 180 days without a comment, a reassignment, or a priority change is decaying.

Neither count requires a new system. The first is a text search over the postmortem repository. The second is a query against the issue tracker. The SRE Book notes that Google has enhanced its postmortem template with metadata fields to facilitate automated analysis, and that tools to aggregate postmortems help identify common themes across product boundaries. The same metadata that enables trend analysis enables decay measurement.

What you do with the number is a management decision. A team with 40% tracking-ID coverage and a median time-to-status-change of 90 days has a different problem than a team with 90% coverage and a median of 10 days. The first team needs to fix its postmortem review. The second team needs to fix its prioritization.

What I would do differently

I would not add a new tool. I would add a review gate. Before a postmortem is published, the reviewer reads each action item aloud and asks four questions: Who owns this? Where is it tracked? What priority? How will we know it worked? If any answer is missing, the item goes back to the author. The workbook’s review criteria already include whether the action plan is appropriate and whether bug fixes are at appropriate priority. The four questions are a concrete way to apply those criteria.

I would also publish the postmortem within a week, not four months. The workbook notes that the bad postmortem was published four months after the incident, and that if the incident had recurred during that delay, team members likely would have forgotten key details. A timely postmortem captures details that a delayed one loses. The delay is itself a decay signal.

I would not claim that this fixes everything. The satellite diskerase incident recurred three years later despite action items. The action items reduced the blast radius, but they did not prevent recurrence. That is the honest outcome: closing the loop makes the next incident smaller, not impossible.

FAQ

Does every action item need a tracking bug?
The workbook says only one item in the bad postmortem had a tracking bug, and that without a formal tracking process, action items are often forgotten. The Treynor Sloss rule requires at least one P0/P1 bug for user-affecting outages. A reasonable policy is: every action item gets a tracking ID, and every user-affecting postmortem gets at least one P0/P1.

What if the action item is “write a design doc”?
That is still a contract. The owner is the author. The tracking ID is the doc’s ticket or the review request. The priority is the priority of the design work. The verification method is the review approval and the subsequent implementation.

How do I measure decay if my postmortems are in a wiki?
Count the fraction of action items with a tracking ID. That is a text search. If the number is low, the decay is already visible. You do not need a dashboard to start.

Is the Google case study representative?
It is one incident, one recurrence, and one set of action items. It is not a dataset. It supports the claim that closing the loop changes outcomes. It does not support a specific decay rate or half-life.

The heuristic

If you cannot point to a tracking ID and a verification method, you have written a wish, not an action item. Wishes decay. Contracts get closed.


The Code Snippet That Never Compiled: Putting Documentation Samples in CI

Artifact first: a documentation sample that has never been executed is not a sample. It is a hypothesis. Conclusion: if your docs build does not compile and run the code it publishes, your documentation is a claim your CI has never tested.

Consider a quickstart that fails in front of a customer because the sample calls a method that was renamed two releases earlier. The docs build is green. The API build is green. Nothing connects them. That is the gap in miniature: two pipelines, one contract, zero enforcement.

What “docs as code” actually commits you to

The Write the Docs guide on Docs as Code lists five practices: issue trackers, version control, plain-text markup, code review, and automated tests. Four of those are about where the text lives. The fifth is about whether it is true. Most teams adopt the first four and quietly skip the fifth, because the first four are cheap and the fifth requires deciding what “true” means for a snippet.

That decision is the whole job. A snippet is not a unit test. It has no assertion, no fixture, no teardown, and often no imports. To put it in CI you have to give it all four, or accept that you are only checking syntax.

Three levels of snippet verification, and what each one catches

Level 1: parse. Run the snippet through the language’s parser or a linter. This catches typos, unbalanced brackets, and syntax that changed between language versions. It does not catch a renamed method, a changed argument order, or a removed endpoint. It is cheap and it is better than nothing, which is the faintest praise available.

Level 2: compile or type-check against the real client library. This catches signature drift. If your snippet calls client.createUser(name, email) and the library now takes a request object, the compiler tells you. This is the level most teams should target first, because signature drift is the most common way a published sample goes stale.

Level 3: execute against a live or mocked service and assert on the response. This catches everything above plus behavioral drift: the endpoint that now returns 202 instead of 200, the pagination cursor that changed shape, the idempotency key that is now required. It is also the level that costs the most to maintain, because it needs a service to talk to and a way to keep that service’s state clean between runs.

My recommendation, stated as a recommendation and not a finding: start at level 2 for every snippet, and promote to level 3 only the snippets that appear in a quickstart or a migration guide. Those are the ones a reader will copy verbatim into a fresh project, and those are the ones whose failure is most expensive.

Extraction versus inline: the trade-off nobody documents

There are two ways to get a snippet into CI. You can extract it from the prose at build time, or you can keep it inline in a file that the docs build includes and the test suite imports.

Extraction keeps the prose as the source of truth. The reader sees exactly what the test ran, because the test ran the reader’s copy. The cost is that extraction is fragile: a fence marker, an indentation change, or a stray blank line can silently change what gets extracted, and a snippet that fails to extract may be skipped rather than failed.

Inline inclusion keeps the code as the source of truth. The snippet lives in a real file with real imports, and the docs build pulls it in. The cost is that the reader may see a fragment that does not stand alone, and the prose has to explain the surrounding context that the file provides.

Both work. The failure mode to avoid is the hybrid where the snippet is extracted, the extraction fails, and the pipeline treats a missing snippet as a passing test. If your extractor cannot find a snippet, that is a failure, not a skip.

Where the CI actually runs

GitHub Actions workflows are YAML files stored in .github/workflows, triggered by events such as push or pull_request, with optional path filters so a docs-only change does not rebuild the world. GitLab CI/CD uses .gitlab-ci.yml, with include for pulling in shared configuration and rules for conditional jobs. Azure Pipelines combines continuous integration, continuous testing, and continuous delivery, and runs test tasks across the major language ecosystems.

The mechanics differ; the shape does not. You need a job that runs on the same trigger as the code it documents, checks out the same revision, installs the same dependency versions, and fails the build when a snippet does not compile or run. If the docs job runs on a different schedule than the code job, you have built a staleness generator with extra steps.

One detail worth getting right: pin the dependency versions in the docs job to the versions the snippet claims to support. A snippet that compiles against latest and fails against the version in your support matrix is not verified. It is verified against a moving target.

What to measure, and what not to

Postman’s API testing material frames the distinction between testing, which happens during development to catch issues before production, and monitoring, which happens after deployment and includes telemetry for historical analysis. Snippet verification is testing. It belongs in the pipeline, not in a dashboard someone checks on Fridays.

Useful signals, in order of how much they tell you:

  • Snippet pass rate per release: the fraction of published snippets that compiled and ran in the release pipeline. A drop here is a leading indicator of support tickets.
  • Time since last successful execution per snippet: staleness. A snippet that has not run in six months is a liability regardless of whether it currently passes.
  • Extraction failures: snippets the build could not find. These should fail the build, and the count should be zero.

Signals that look useful and are not: total snippet count, docs build duration, and the number of snippets with a test file. None of those tell you whether a reader can copy the code and get a working result.

The decision record you should write before you build this

The reason snippet CI stalls is rarely technical. It is that nobody wrote down which snippets are covered, which level of verification applies to each, and who owns the fix when a snippet breaks because the API changed. That is an architecture decision record, and it should be short.

It needs to state: the scope (which docs directories are in), the verification level per category, the trigger (same as the code it documents), the failure policy (build fails, no exceptions), and the owner (the team that owns the API, not the docs team). The last one matters most. If the docs team owns snippet failures, the API team has no incentive to keep the contract stable, and the gap reopens.

A postmortem template helps here too. When a snippet ships broken, the postmortem should answer one question: why did the pipeline not catch this? The answer is usually that the snippet was not in scope, or that the extraction silently skipped it. Both are fixable, and both are invisible until you ask.

What I would do differently

I would not start with a general-purpose extractor. I would start with a list of the twenty snippets that appear in quickstarts and migration guides, put them in real files, and wire those files into the existing test suite. That is a week of work, not a quarter, and it covers the snippets whose failure costs the most.

I would also fail the build on extraction errors from day one. The temptation to warn instead of fail is strong, and it is how you end up with a green pipeline and a broken quickstart. A warning that nobody reads is a skip with better branding.

FAQ

Do I need to run every snippet against a live service?
No. Compile or type-check against the real client library catches the most common failure, which is signature drift. Reserve live execution for snippets a reader will copy into a fresh project.

What if my snippets are in a language without a compiler?
Use the language’s parser or linter for syntax, and add a smoke test that imports the module and calls the function with representative arguments. That gets you most of the way to level 2.

How do I keep the docs job from slowing down every pull request?
Use path filters so the job runs only when the docs or the client library change. GitHub Actions supports paths filters on push and pull_request; GitLab supports rules:changes.

Who should own snippet failures?
The team that owns the API. If the docs team owns them, the API team has no feedback loop, and the snippets will drift again.

Is this worth it for a small API?
If you publish a quickstart, yes. The cost of one broken quickstart in a support ticket is usually higher than the cost of wiring twenty snippets into CI.

Sources

Heuristic: a snippet that has never run in CI is a hypothesis, not a sample. Treat extraction failures as build failures, and put the twenty quickstart snippets in real files before you build a general-purpose extractor.