Greenpeppersoftware

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

The Crossed-Out Hand Is the Whole Argument: Reading the ‘Not Friendly Do Not Touch’ Warning Sign

The first thing I noticed on this shirt was not the words. It was the hand.

A yellow caution triangle sits at the top, the shape every driver and every warehouse worker has been trained to read as pay attention, something here can hurt you. Inside or beneath it, depending on how you read the stack, is a hand with a slash through it. Then the lettering: NOT FRIENDLY on one line, DO NOT TOUCH on the next, set in the blocky, condensed sans that public signage uses when it cannot assume the reader speaks the local language.

That is the whole design. No mascot, no joke caption, no winking subline. And that restraint is what makes it worth looking at closely, because the shirt is doing something more specific than announcing that the wearer is an introvert.

What the sign actually says, and what it doesn’t

I want to be careful here, because it is easy to over-read a t-shirt. The artwork is a visual quotation of hazard signage. The triangle, the slash, the imperative verb — those are borrowed conventions. What the shirt does not contain is any claim about the wearer’s inner life, diagnosis, energy level, or social battery. It does not say I am tired or I need space or please understand me. It says do not touch, in the grammar of a fence around a substation.

That is a different kind of statement. A feeling is a request for accommodation. A hazard sign is a boundary with a stated consequence. The shirt picks the second register, and the crossed-out hand is the part that makes the choice legible: it is not the wearer’s face being crossed out, or a speech bubble, or a heart. It is the point of contact.

The crossed-out hand is a diagram of a boundary, not a mood

Think about what a crossed-out hand means in the signage vocabulary the shirt is borrowing. It appears on doors, on equipment, on packaging. It is not decorative. It marks the one action the reader is being asked not to take, and it does so without explaining why. The reason is irrelevant to the instruction. Hot, sharp, fragile, private — the sign does not care. It only cares that you keep your hand where it is.

That is an unusually clean piece of visual argument for a novelty shirt. Most introvert-themed apparel I see reaches for a caption that explains the joke, usually with an ellipsis and a sigh. This one trusts the reader to know what a caution triangle does. The humor, such as it is, comes from applying industrial-strength signage to a social situation. The design does not ask you to laugh at the wearer. It asks you to read the sign correctly.

Why the bluntness is the point

There is a version of this shirt that would be softer. Please ask before hugging. Low social battery. Quiet, but friendly. Those are all fine sentences, and they all share a problem: they negotiate. They invite the reader to weigh the request, decide whether it applies today, and perhaps make an exception. A warning sign does not negotiate. It states a condition and leaves the response to you.

The bluntness is not rudeness. It is a refusal to make the wearer responsible for managing a stranger’s interpretation in real time. Anyone who has stood in a crowded room doing the mental arithmetic of how do I exit this conversation without offending anyone will recognize the appeal of outsourcing that arithmetic to a triangle.

The gap between a slogan and a usable promise

Here is where the design gets interesting to me as someone who spends a lot of working hours on contracts and interfaces. A slogan is a sentence that sounds good. A usable promise is a sentence that tells the reader what to expect and what to do when the expectation is not met. Most slogans fail the second test. This shirt, oddly, passes it better than most.

Consider what the reader of the shirt can actually do with it. The sign names a boundary (do not touch), supplies a visual cue that is recognizable at a glance (the triangle and slash), and does not require the wearer to explain anything. That is a complete, if minimal, interface. It has a default state, a prohibited action, and no ambiguity about which is which.

What it does not have is a recovery path. A real hazard sign usually pairs the prohibition with an alternative — use the other door, call this number. This one stops at no. That is fine for a shirt, because the shirt is not trying to run a process. But it is worth noticing, because the difference between a boundary and a wall is exactly that missing second clause. The design is honest about which one it is offering.

What I would change, and what I would not

I would not add a caption. The restraint is the design. A line of small text underneath explaining the joke would collapse the whole thing into a meme, and the sign would stop doing its job.

I might, if I were art-directing it, tighten the vertical rhythm between the triangle and the two lines of type so the whole mark reads as a single unit at a distance. Warning signs are read at speed, often from an angle, and the current stack asks the eye to do a little more work than a real sign would. That is a small quibble. The hierarchy is already correct: symbol first, then the prohibition, then the qualifier.

The shirt is available as a standard t-shirt through the listing here: Not Friendly Do Not Touch Funny Warning Sign Introvert. The design is what it is — a caution triangle, a crossed-out hand, and two lines of block lettering. Whether that reads as funny, as a boundary, or as both is going to depend on the room you walk into.

The takeaway I keep coming back to

Most apparel that tries to say something about social energy ends up describing a feeling. This one describes an instruction. That is a harder thing to design and a more useful thing to wear, because it puts the burden of interpretation where it belongs: on the person approaching, not on the person being approached.

If you take one thing from the artwork, take the crossed-out hand. It is the smallest element on the shirt and the only one that is actually doing the work. The words tell you what the rule is. The hand tells you where the rule applies. Everything else is typography.

Editorial note: This publication and Quirky Cotton Collective share an owner. This is not an independent review.

The Element Tiles Don’t Spell the Sentence: Reading ‘I Wear This Shirt Periodically’

The shirt says I WEAR THIS SHIRT PERIODICALLY in plain caps, and then, underneath, four periodic-table-style tiles: 53 I, 74 W, 22 Ti, 16 S. Read the symbols in order and you get I W Ti S. That is not the sentence. That is the joke.

I keep coming back to this because it is a small, clean example of something I see go wrong in API contracts all the time: a label that looks like it encodes one thing, a payload that actually encodes another, and a reader who has to reconcile the two. The shirt is not a chemistry lesson. It is a diagram of a near-miss between human-readable intent and machine-readable content.

What the tiles actually say

Here is the literal reading, with nothing inferred:

  • Tile one: atomic number 53, symbol I — iodine.
  • Tile two: atomic number 74, symbol W — tungsten.
  • Tile three: atomic number 22, symbol Ti — titanium.
  • Tile four: atomic number 16, symbol S — sulfur.

Concatenated, the symbols are I W Ti S. The sentence above them is I WEAR THIS SHIRT PERIODICALLY. The pun is that the tile symbols approximate the sound of the sentence without spelling it. The tiles are not a literal encoding. They are a visual rhyme.

That distinction matters more than it might seem. If you treat the tiles as a serialization format, the shirt is broken. If you treat them as a mnemonic, it works. The design asks you to hold both readings at once and enjoy the gap between them.

The gap is the point, and it is also the risk

I have spent enough time on both sides of API contracts to recognize this pattern. A field named status that contains "active", "pending", and "archived" is readable. A field named status that contains 1, 2, and 3 is also readable — until someone adds 4 and nobody updates the enum in the OpenAPI spec. The label and the payload drift apart. The shirt is a controlled version of that drift, performed on purpose, for a laugh.

What makes the shirt work is that the drift is bounded. There are exactly four tiles. The sentence is short. The mapping is stable. Nobody is going to add a fifth tile next quarter and forget to tell the client. In a real API, that is exactly what happens.

Three readings of the same object

I find it useful to separate three layers here, because conflating them is where contract reviews go sideways:

  • Intent layer: the sentence. I wear this shirt periodically. This is what a human reads first and what the designer wants you to understand.
  • Encoding layer: the tiles. I W Ti S. This is what a parser would extract if you told it to read symbols only.
  • Reconciliation layer: the pun. The reader notices the mismatch, recognizes it as deliberate, and resolves it by treating the tiles as phonetic rather than literal.

Most API documentation failures happen because the reconciliation layer is missing. The intent is documented in prose. The encoding is documented in a schema. Nobody writes down the rule that connects them, so every new consumer reinvents it, and half of them get it wrong.

What I would steal from this shirt for a spec review

I am not going to pretend a novelty shirt is a design methodology. But the structure is a useful checklist prompt. When I review a contract now, I ask three questions that map cleanly onto the shirt:

  1. Does the human-readable name match what the field actually contains? If the field is called periodically but the values are I, W, Ti, S, say so in the description. Do not let the name carry the whole meaning.
  2. Is the encoding stable, or is it a rhyme? A rhyme is fine for a shirt. In a contract, rhymes are liabilities. If the mapping is phonetic, approximate, or convention-based, write it down and version it.
  3. Where does the reconciliation happen? If the client has to guess, the contract is incomplete. Put the rule in the spec, not in a Slack thread from eighteen months ago.

None of that is novel. But the shirt makes the failure mode visible in a way that a schema diff does not. You can look at it and feel the gap between what you read and what you parse.

The periodically pun is doing real work

The word periodically is the load-bearing element here, and not just because it is a chemistry pun. It implies recurrence. The shirt is worn from time to time. The tiles are arranged in a periodic table format. The sentence and the format share a root, and the joke lives in that overlap.

In contract terms, periodically is a cadence claim. It says: this happens on some schedule, not continuously. That is a weaker promise than always and a stronger promise than maybe. Most API rate-limit and retry contracts live in exactly that ambiguous middle, and most of them are worse at saying so than a four-tile novelty shirt.

If you are writing a retry policy, periodically is not good enough. You need a backoff schedule, a jitter strategy, and a maximum attempt count. The shirt gets a pass because it is a joke. Your runbook does not.

Where to find it

The shirt is sold as I Wear This Shirt Periodically Funny Chemistry Science Pun by Quirky Cotton Collective. The design is the sentence plus the four tiles described above. I am not going to tell you it will improve your code reviews. It will not. It is a shirt with a good structural joke, and that is enough.

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

The artifact: a three-layer contract check

Here is the adaptable piece. Before you ship a contract change, write down all three layers for every field you touched:

Field: retry_after_seconds
Intent:   "How long the client should wait before retrying."
Encoding: integer, seconds, >= 0, present only on 429 responses
Reconciliation: client MUST honor the value; if absent, use exponential backoff starting at 1s

If you cannot fill in the reconciliation line in one sentence, the contract is not ready. That is the whole checklist. The shirt passes because the reconciliation is the joke. Your API should pass because the reconciliation is written down.

The Smiling Root Vegetables and the Two Meanings of ‘Root’: Reading a Mutual-Growth Slogan Like a Contract

Four root vegetables smile up from the shirt: a carrot, a radish, a beet, and an onion, each with its roots drawn down into visible soil. Around them, in a ring, the words run: LET’S ROOT FOR EACH OTHER AND WATCH EACH OTHER GROW. The design is doing something more precise than it first appears. It is using one verb in two senses at once, and the two senses pull in opposite directions.

To root for someone is to support them from the sidelines. To root is also what the vegetables are doing in the soil: putting down roots, taking hold, growing. The pun works because the same word covers both the cheer and the biology. But the two meanings imply different things about what growth is and who is responsible for it. That difference is worth reading closely, because it is the same difference that separates a team slogan from a usable promise.

The artwork is a diagram, not a decoration

Look at the composition. The four vegetables are arranged inside the ring of text, not scattered around it. Each one has a face, and each one has roots that extend below the soil line. The roots are drawn, not implied. That is a deliberate choice: the design wants you to see the part that is usually hidden.

In a diagram, the visible elements are the ones the designer chose to show. Here, the visible elements are:

  • Four distinct vegetables, each identifiable by shape and color.
  • Faces, which make the vegetables agents rather than objects.
  • Roots below the soil line, which make the growth literal and grounded.
  • A closed ring of text, which frames the group as a unit.

The ring matters. The text does not trail off; it surrounds the vegetables completely. That enclosure suggests a bounded group, not an open-ended one. The slogan is addressed to a we that is already inside the circle. It is not a recruitment poster. It is a statement about how the people already present are supposed to treat each other.

Two meanings of ‘root,’ two models of growth

The first meaning, rooting for someone, is about encouragement. It is external. You stand outside the person’s effort and you cheer. You do not do the growing for them. You do not control the soil. You offer support and you watch.

The second meaning, putting down roots, is about internal development. It is what the vegetable does in the dark, below the surface, without an audience. The roots are the part nobody sees until the plant is pulled up or the soil is dug away.

The shirt puts both meanings in the same sentence, and that is where the argument lives. The slogan is not just saying we support each other. It is saying we support each other while each of us does the unseen work of growing. The support is external; the growth is internal. The two are related but not identical.

That distinction is easy to lose. A lot of team language collapses the two. We grow together can mean either we encourage each other’s individual development or the group’s growth is the same as each person’s growth. Those are different claims. The first is a promise about support. The second is a promise about alignment, and alignment is much harder to deliver.

What the slogan promises, and what it doesn’t

Read the sentence as a contract, the way I would read an API description. What is the offer? What is the obligation? What is left unspecified?

The offer is mutual rooting: each person supports the others. The obligation is reciprocal but not quantified. There is no schedule, no metric, no definition of what counts as support. The second clause, and watch each other grow, adds an observational commitment. You are supposed to notice the growth. That is a lower bar than causing it, but it is not nothing. Noticing requires attention, and attention is the thing most teams run out of first.

What the slogan does not promise:

  • That growth will be fast.
  • That growth will be visible on a predictable schedule.
  • That everyone will grow in the same direction.
  • That the group will grow as a unit.

Those absences are features, not bugs. A slogan that promised uniform, synchronized growth would be lying. Root vegetables do not grow at the same rate, and neither do people. The design acknowledges this by drawing four different vegetables with four different root shapes. The carrot’s root is long and tapered. The beet’s is round and bulbous. The onion’s is layered. They are not interchangeable, and the slogan does not ask them to be.

The difference between a slogan and a usable promise

This is where the shirt becomes useful as a thinking tool rather than just a pleasant image. A slogan is a compressed statement of values. A usable promise is a statement you can act on, check, and hold someone to. The gap between them is where most team culture language fails.

Let’s root for each other is a slogan. It is warm and true-sounding, but it does not tell you what to do on a Tuesday when a colleague’s pull request has been open for three days and you are busy. Watch each other grow is closer to a usable promise, because watching is an action. It implies a habit: noticing someone’s progress and saying so. But even that is underspecified. Watch how? Say what? To whom?

The shirt does not answer those questions, and it should not be expected to. It is a shirt, not a runbook. But the act of reading it closely surfaces the questions, and that is the useful part. If a team wants to adopt the sentiment, the work is in turning the slogan into something operational: a specific habit, a specific check-in, a specific way of noticing growth that does not depend on memory or mood.

Why the vegetables are smiling

The faces are the least realistic part of the design and the most important. Root vegetables do not smile. The smiles make the vegetables into characters, and characters imply agency. A smiling vegetable is a vegetable that is participating in its own growth, not just undergoing it.

That is a meaningful choice for a mutual-growth slogan. It shifts the emphasis from we will grow you to we will grow, and we will root for each other while we do it. The agency stays with the individual. The group provides the conditions and the encouragement. That is a healthier model than the top-down version, where growth is something done to people by a system.

I would not push the analogy further than that. The shirt is not a management framework. It is a picture of four vegetables with roots and faces, arranged inside a sentence. But the arrangement is deliberate, and the deliberation is worth noticing. The design chooses mutual support over directed development, visible roots over hidden ones, and distinct vegetables over identical ones. Those choices add up to an argument, even if the argument is small.

Reading the object without overreading it

There is a temptation, when writing about a shirt like this, to turn it into a lesson about teamwork or growth mindset or whatever the current term is. I want to resist that. The shirt is a shirt. It does not demonstrate a software practice, a security principle, or an engineering result. It is a piece of clothing with a pun on it.

What it does offer is a compact visual argument about the relationship between support and growth. That argument is worth taking seriously because it is well made: the two meanings of root are genuinely doing different work, and the design holds them together without collapsing them. The smiling vegetables and the visible roots make the abstraction concrete. The ring of text makes the group bounded. The four different vegetables make the group plural.

If you are the kind of person who reads API contracts for a living, you will recognize the move. It is the same move as writing a good error message: say what is happening, say what it means, and do not promise more than you can deliver. The shirt says we root for each other and we watch each other grow. It does not say we guarantee growth or we grow at the same rate. The restraint is what makes it usable.

The shirt is available as a standard t-shirt from Quirky Cotton Collective: Let’s Root For Each Other And Watch Each Other Grow Garden.

The artifact: a one-page reading checklist

If you want to use the shirt’s argument as a thinking tool, here is a short checklist for reading any mutual-growth slogan, including this one. It is adapted from the way I read API descriptions, which is to say it is about separating what is promised from what is assumed.

  • Identify the verb’s double duty. Does the slogan use a word in two senses? If so, are both senses doing work, or is one decorative?
  • Separate support from growth. Who is doing the growing, and who is doing the supporting? Are they the same actor?
  • Check the enclosure. Is the we bounded or open? Does the slogan address a specific group or an abstract audience?
  • Look for the hidden part. Does the design show the roots, or only the leaves? What is below the soil line?
  • Name the missing specifics. What would it take to turn this slogan into a habit? Write down one concrete action it implies.
  • Resist the overread. Does the slogan claim an outcome it cannot deliver? If so, note the gap and do not fill it with your own assumptions.

The checklist is not a test the shirt has to pass. It is a way of paying attention to what the shirt is actually doing, which is more interesting than what it might be made to mean.

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

Feature Flags Expire or They Multiply: A Cleanup Cadence with Expiry Metadata and a Monthly Audit

Every team that ships behind flags eventually has the same conversation. Someone opens the flag dashboard, sorts by age, and finds a release flag that was fully rolled out fourteen months ago. It is still evaluated in three services. Nobody wants to touch it, because the last person who deleted a flag config before removing the code watched a shipped feature silently revert for a cohort of users.

That failure is not a mystery. It is an ordering bug. The flag’s compiled-in default is usually the old behavior, so deleting the config entry while evaluations remain is a quiet rollback. The fix is not more caution. It is a cadence: record expiry intent when the flag is created, then run a monthly audit that ends every row in either a merged code-removal PR or a re-dated expiry with a written reason.

Why flags accumulate

Pete Hodgson’s widely cited article on feature toggles is blunt about the underlying dynamic: toggles introduce complexity, and teams should aim to constrain the number of toggles in their system. He also notes that release toggles are transitionary by nature, generally not sticking around much longer than a week or two, though product-centric toggles may need to remain longer. Ops toggles are mostly short-lived, with a small number of long-lived kill switches being common.

So the categories already have different natural lifespans. The problem is that most teams never write the lifespan down. A flag is created in a hurry, rolled out, and then lives in a kind of administrative fog. It is not on anyone’s roadmap, it is not in anyone’s on-call rotation, and it is not obviously broken. It just sits there, adding a branch to every code path that touches it.

Vendor documentation has started to name this. FeatureSignals’ lifecycle guide calls cleanup “often the forgotten step” and recommends flag expiration plus category-aware staleness thresholds to prevent stale flags. That is a useful confirmation of the problem, not a solution you can copy wholesale, because the enforcement still has to live in your process.

Expiry metadata: write it at creation, not at cleanup

The single highest-leverage change is to stop treating expiry as a cleanup activity. A practitioner guide on flag lifecycle management puts it directly: encode ownership and expiry intent in the flag’s metadata at creation time, not as an afterthought during cleanup sprints. The same guide argues that expiry intent does not have to be a hard date; a category such as experiment, release, ops, or permanent is enough for tooling to know which flags to nag about.

That distinction matters. A hard date that passes while the flag is still load-bearing trains the team to ignore expiry warnings. A category plus a target removal window records intent, and intent can be renegotiated in the audit with a written reason. A date that silently lapses teaches people that the system cries wolf.

The minimum metadata set

Every flag should carry these fields from the moment it is created:

  • Owning team, not an individual. People change teams and leave companies; a flag owned by a departed engineer is an orphan the day the account is deprovisioned.
  • Category: release, experiment, ops, or permanent. This drives the cleanup clock.
  • Expiry intent: a target removal window (for example, “within two sprints of full rollout”) or an explicit permanent designation.
  • Evaluation locations: the files or modules that read the flag. This is what makes the removal PR mechanical instead of archaeological.
  • Removal PR link, once one exists. The audit’s job is to make sure every row has one.

The evaluation-locations field only works if flag names are static. Unleash’s guide to managing flags in code recommends centralizing flag name definitions in constants or enums as a single source of truth, and explicitly warns against dynamically constructed names: building a name at runtime prevents static analysis and makes automated cleanup much harder. If your codebase builds flag keys from strings, the audit cannot find all the call sites, and the whole cadence collapses into manual grep archaeology.

Category drives the clock

A single blanket TTL for every flag is the wrong abstraction. It will nag about kill switches that are supposed to live forever, and it will under-nag about release flags that should have been removed weeks ago. The published category-to-timeline guidance is a reasonable starting point:

  • Release: days to weeks after full rollout.
  • Experiment: after the experiment concludes and a winner is declared.
  • Ops: rarely; ops flags are often long-lived.
  • Permission: rarely; permission flags may be permanent.

Those timelines come from one vendor’s documentation, so treat them as a starting point rather than a universal rule. The important structural point is that the category, not a single date, determines which flags the audit chases. The same guide notes that a category is enough for tooling to know which flags to nag about, which is exactly the property you want: the audit can exclude permanent flags by design, so its signal is not diluted by flags that are supposed to live forever.

The ordering rule that prevents silent rollbacks

This is the part teams get wrong under deadline pressure. The lifecycle guide is explicit about the failure: delete the config first and any straggler evaluation immediately starts returning its compiled-in default, which is usually the old behavior, silently reverting a shipped feature for whichever cohort the rule used to target.

The correct order is not negotiable:

  1. Remove the flag evaluations from the code. Keep the new behavior unconditional.
  2. Deploy that change and confirm it is live everywhere.
  3. Confirm no service is still calling the flag key. Check logs, metrics, or the provider’s evaluation data.
  4. Only then delete or archive the configuration entry.

The same guide recommends marking a flag deprecated, notifying the owning team, giving a deadline (typically two sprints), then hard-deleting. That two-sprint figure is one guide’s recommendation, not a law of nature, but the sequence is the part that matters. Deprecation is a state that separates “this flag is going away” from “this flag is gone,” and it exists precisely so the code-removal step can happen first.

There is a second reason to respect the ordering. OpenFeature’s specification requires that flag evaluation calls return the default value rather than throwing on abnormal execution. That is good design for availability, but it means a missing flag does not announce itself. The evaluation returns the default, the request succeeds, and the only symptom is a feature that quietly stopped working for some users. The spec also defines a DISABLED resolution reason for values resolved because the flag was disabled in the management system, which is useful for observability, but you should not assume every provider emits it or that it maps to expiry specifically. The safe assumption is that a deleted flag looks like a normal evaluation returning a default.

The monthly audit

Per-flag reminders do not work at scale. Each notification looks ignorable in isolation, and there is no moment when someone sees the whole list. A monthly audit produces one reviewable artifact with a named owner per row. The cadence is a design choice, not an empirical finding, but the structure is what makes it stick.

The audit is a removal pipeline, not a status report. Every row must end in one of two states: a merged code-removal PR, or a re-dated expiry with a written reason. Relabeling a flag as “still needed” without a reason is not a valid outcome, because that is how the list gets closed without anything changing.

The checklist

Run these four queries against the registry each month:

  • Past expiry intent. Flags whose target removal window has passed and whose category is not permanent.
  • Fully rolled out beyond a window. Flags at 100% rollout for longer than the category’s expected window. These are candidates for code removal even if nobody marked them stale.
  • No recent evaluations. Flags with no evaluation traffic in the last period. A flag nobody evaluates is either dead code or a missing call site; both need a decision.
  • Orphaned owners. Flags whose owning team no longer exists or has been renamed. Reassign before the next audit, not during an incident.

For each row, assign an owner and open a removal PR or record a re-dated expiry with a reason. The audit meeting is short because the output is a list of PRs, not a debate about whether a flag is still needed.

A small registry schema

The registry can be a YAML file in the repo, a table in the flag provider, or a spreadsheet that gets exported. What matters is that it is the source of truth for flag names and that it is machine-readable enough to run the audit queries. Here is a minimal schema, shown as a hypothetical example rather than a real deployment:

flags:
  - key: checkout.new-summary-panel
    owner_team: payments
    category: release
    expiry_intent: "within 2 sprints of full rollout"
    created: 2026-03-14
    evaluation_locations:
      - services/checkout/summary.ts
      - services/checkout/controller.ts
    removal_pr: null

  - key: search.ranking-v2
    owner_team: search
    category: experiment
    expiry_intent: "after experiment concludes"
    created: 2026-05-02
    evaluation_locations:
      - services/search/ranking.ts
    removal_pr: null

  - key: platform.disable-recommendations
    owner_team: platform
    category: permanent
    expiry_intent: null
    created: 2025-11-20
    evaluation_locations:
      - services/web/home.ts
    removal_pr: null

The permanent category is the escape hatch that keeps the audit honest. Kill switches and permission gates are supposed to live forever, and the audit should exclude them by design. If they show up as noise every month, people stop reading the list.

An audit script sketch

The script does not need to be clever. It reads the registry, compares each flag against its category’s window, and emits the three lists. This is a sketch, not a tested tool:

import yaml, datetime

WINDOWS = {
    "release": datetime.timedelta(days=30),
    "experiment": datetime.timedelta(days=60),
    "ops": None,
    "permanent": None,
}

with open("flags.yaml") as f:
    flags = yaml.safe_load(f)["flags"]

today = datetime.date.today()
past_expiry = []
for flag in flags:
    window = WINDOWS.get(flag["category"])
    if window is None:
        continue
    created = datetime.date.fromisoformat(str(flag["created"]))
    if today - created > window:
        past_expiry.append(flag)

print("Past expiry intent:")
for flag in past_expiry:
    print(f"  {flag['key']} (owner: {flag['owner_team']})")

The real version would also query the flag provider for rollout percentage and evaluation counts, and would check that every evaluation_locations entry still contains a reference to the key. That last check is what turns the audit from a status report into a removal pipeline: if the code no longer references the flag, the config entry is safe to delete, and the script can say so.

What the audit does not solve

The audit will not remove a flag for you, and it will not decide whether a long-lived kill switch is still worth its complexity. Permanent flags need an explicit category so they stop appearing as noise, but that category is a declaration, not a proof. A kill switch that has not been exercised in two years may be dead weight, and the audit should surface it for a human decision rather than silently excluding it forever.

It also will not fix dynamically constructed flag names. If your code builds keys at runtime, the static search cannot find all the call sites, and the removal PR becomes guesswork. The fix there is at review time: reject dynamically constructed names, centralize the constants, and make the registry the source of truth. The audit is only as good as the search it depends on.

The artifact

Copy this into your team’s docs and adapt the windows to your release cadence. The registry schema and the audit checklist are the two pieces that make the cadence real.

# Flag Registry Entry
key: <namespace.service.feature>
owner_team: <team name>
category: release | experiment | ops | permanent
expiry_intent: <target removal window or null for permanent>
created: <YYYY-MM-DD>
evaluation_locations:
  - <path/to/file>
removal_pr: <link or null>

# Monthly Audit Checklist
[ ] List flags past expiry intent (exclude permanent)
[ ] List flags at 100% rollout beyond category window
[ ] List flags with no recent evaluations
[ ] List flags with orphaned owners
[ ] For each row: assign owner and open removal PR, or re-date expiry with written reason
[ ] Confirm removal order: code removed, deployed, no callers, then config deleted

The point is not the YAML. The point is that every flag has an owner, a category, and an expiry intent before it ships, and that once a month someone looks at the whole list and turns stale entries into merged PRs. Flags do not expire on their own. They multiply until someone runs the audit.

The Loudest Quiet Word: What the Big White ‘meh.’ on a Dark Shirt Argues About Terse Signals

The first thing you notice is the scale. On the dark shirt, the word meh. is set in large, bold, white lowercase letters. It is not a whisper. It is not a small, self-deprecating aside tucked near the collar. It is a billboard-sized shrug, and the period at the end is doing more work than any exclamation mark could.

That combination—visual loudness paired with semantic dismissal—is the whole argument of the garment. And it is a useful argument for anyone who writes messages that other people have to act on.

The design is a contradiction you can read from across the room

Look at what the artwork actually contains: white lowercase lettering, bold weight, large size, dark background. No animal illustration. No secondary slogan. No decorative frame. The word is the entire composition.

Lowercase meh is casual by convention. The period is final. The bold white type is emphatic. So the shirt says something emphatic about not being emphatic. That tension is the joke, and it is also a small lesson in signal design.

I have spent a lot of my working life writing messages that need to be read once and acted on correctly: error responses, runbook steps, deprecation notices, changelog entries. The ones that fail are rarely the ones that are too short. They are the ones where the visual weight and the semantic weight disagree. A red banner that says “no action required.” A bold warning that turns out to be informational. A quiet footnote that turns out to be the thing that breaks the deploy.

This shirt is the opposite failure, and it is a harmless one: a loud presentation of a low-stakes sentiment. It works because the mismatch is intentional and legible. In a status page or an API error, the same mismatch is a bug.

What ‘meh.’ actually promises—and what it doesn’t

The word meh is a refusal to escalate. It declines to be enthusiastic. It declines to be outraged. It sits in the middle and puts a period on the sentence. As a piece of social signaling, that is a boundary: I am here, I am not performing, and I am not going to argue about it.

That is a different thing from a slogan. A slogan makes a promise. “The best.” “Always.” “Never.” A slogan is a claim you can hold someone to. meh. makes no claim at all. It is a terse signal that resists being turned into a commitment.

For technical teams, the distinction matters more than it sounds. The messages that cause the most friction are the ones that blur the line between a mood and a contract. A Slack message that says “probably fine” is a mood. A runbook step that says “verify the queue depth is below 100 before proceeding” is a contract. When a mood gets copied into a runbook, the next person on call inherits ambiguity they cannot debug.

The shirt is a mood, clearly labeled as a mood. That is why it reads as dry humor rather than negligence. The period is the label.

The period is the artifact

If I were to adapt anything from this design for a team practice, it would be the period. Not the word—the punctuation habit.

A period at the end of a short message is a small act of closure. It says: this is the whole thought, there is no follow-up coming, you may proceed. In a chat tool that rewards trailing ellipses and half-finished thoughts, a period is a tiny contract.

Here is a checklist I have used when writing terse operational messages, and it maps almost too neatly onto the shirt:

  • Match weight to stakes. If the message requires action, make it visually and structurally prominent. If it does not, do not dress it up in bold red.
  • End the sentence. A message that trails off invites a reply that asks what you meant. A period closes the loop.
  • Separate mood from instruction. “I am skeptical of this approach” is a mood. “Do not merge until the migration test passes” is an instruction. Do not let the first masquerade as the second.
  • Do not make a slogan out of a shrug. If the answer is genuinely meh, say so plainly and move on. Inflating it into a promise creates a contract you did not intend to sign.

None of this is profound. But the shirt is a reminder that presentation and meaning are separate layers, and that the gap between them is where readers get confused. A big white word on a dark background is not automatically important. A small quiet word is not automatically ignorable. The design has to agree with the message.

Why this works as an off-duty object

I would not wear this to a customer meeting. I would wear it on a day when the calendar is clear and the only thing I owe anyone is a non-committal nod. The shirt is calibrated for that: it is legible from across a room, it does not invite conversation, and it does not pretend to be more than it is.

That is a narrow use case, and the design respects it. The large lowercase lettering and the period do not try to be clever in three different directions at once. There is no animal, no secondary joke, no visual clutter competing for attention. The restraint is the point.

If you are looking for the object itself, the meh shirt is listed under that title, and the artwork is exactly what the preview shows: large bold white lowercase meh. on a dark shirt. I am describing the design, not a wearing experience. I have not tested the fabric, the fit, or the print durability, and I am not going to pretend otherwise.

The takeaway for people who write messages for a living

The next time you write a short message that other people have to interpret—a PR comment, a runbook line, a status update—check whether the visual weight and the semantic weight agree. If you are saying meh, do not put it in a banner. If you are saying stop, do not bury it in a paragraph.

The shirt gets away with the mismatch because it is a joke, and the period tells you so. Your error responses do not have that luxury.

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

The Dashed Circle and the Solid Ball: What the ‘I Had Potential’ Diagram Actually Argues

The first thing I noticed on the shirt was not the words. It was the dashed circle at the top of the ramp — an outline where a ball should be — and the solid ball sitting at the bottom, past the curve, on the flat ledge. The phrase I HAD POTENTIAL runs above it in white capitals, and the whole drawing is white on the garment. The ramp is curved, not a straight diagonal. That curve matters more than the slogan does.

This is a diagram joke, and like most diagram jokes it works because the drawing and the sentence are not quite saying the same thing. The sentence is past tense and self-deprecating. The drawing is a physics setup: a ball at rest on a ledge, a dashed outline where it used to be, a curved ramp between them. Read the drawing literally and you get a story about stored energy converting to motion. Read the sentence literally and you get a person announcing that the energy is gone. The gap between those two readings is the whole design.

What the drawing shows, and what it doesn’t

I want to be careful here, because it is easy to over-read a t-shirt. The observed artwork is a white curved descending ramp, a dashed empty circle at the upper left ledge, a solid white ball on the lower right flat ledge, and the words I HAD POTENTIAL in capitals above. That is the object. It does not come with a caption explaining the physics, and it does not need one.

What I can say is that the composition uses the visual grammar of a potential-energy diagram without committing to the math. A ball at the top of a ramp has gravitational potential energy relative to the bottom. A ball at the bottom has less. The dashed circle is the before; the solid ball is the after. The curve is the path between them. None of that is a scientific claim about the shirt or the wearer. It is just what the shapes are doing.

The interesting choice is the dashed circle. A dashed outline usually means something absent, projected, or not yet real. Here it sits at the start of the sequence, not the end. The solid ball is the one that actually exists, and it is at the bottom. So the drawing is not saying “the potential is still up there, waiting.” It is saying the potential was up there, and what remains is the ball that rolled down. The past tense in the slogan and the solidity of the lower ball agree with each other. The dashed circle is the ghost.

The difference between a slogan and a usable promise

I spend most of my working life around API contracts, and the thing that makes a contract usable is not the promise in the README. It is whether the promise survives contact with the actual system. A slogan says what you meant. A contract says what will happen when someone else depends on it.

This shirt is a slogan, and it knows it. The joke is that “potential” is being used in two registers at once: the everyday sense of “you could have been something,” and the physics sense of energy stored by position. The drawing picks the physics register and then delivers the everyday verdict. The ball did roll down. The potential is spent. That is the punchline, and it is a good one because it does not require you to know any equations. You only need to recognize a ramp.

Where I think the design is sharper than a generic sarcastic tee is that it does not blame anyone. There is no boss, no deadline, no “this meeting could have been an email.” It is a closed system: a ball, a ramp, a ledge. The self-deprecation is structural rather than social. That makes it easier to wear without it reading as a complaint about a specific job or a specific person.

Why the curve is better than a straight diagonal

A straight diagonal would have made the joke flatter. A straight line reads as a slide, and a slide is something you choose. A curve reads as a track — something the ball follows once it starts. The curve also gives the drawing a sense of acceleration without any arrows or labels. The ball at the bottom is not just lower; it is farther along a path that bends. That is a more specific visual idea than “thing went down.”

I would not claim the designer was thinking about conservative forces or friction. I have no evidence for that, and it does not matter. What matters is that the curve makes the drawing legible as a sequence rather than a symbol. You can read it left to right, top to bottom, dashed to solid. That is enough.

Who this is for, and who it isn’t

If you are the kind of engineer who has ever written a runbook that says “if this alert fires, the system is probably already degraded,” you will recognize the tone. It is the tone of someone who has watched a clean design meet a messy reality and decided to make a joke about it rather than a slide deck. The shirt does not promise that you are smart, or that you work hard, or that you are a “10x” anything. It promises that you had potential, and it shows you where the ball ended up.

That is a narrow joke, and it will not land for everyone. If you want a shirt that says something encouraging, this is not it. If you want a shirt that says something aggressive, this is not that either. It is a quiet, self-aware diagram. The I had potential shirt is available from Quirky Cotton Collective, and the design is the argument: the dashed circle is the before, the solid ball is the after, and the curve is the part you don’t get to skip.

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

The artifact: a one-page diagram review

I said every post should ship something adaptable, so here is the checklist I actually use when a diagram is supposed to carry an argument — in a design review, a runbook, or a t-shirt. It is not a physics checklist. It is a legibility checklist.

  • Name the before and the after. In this design: dashed circle at the top, solid ball at the bottom. If a reader cannot point to both states, the diagram is decoration.
  • Check that the visual grammar matches the words. The slogan is past tense; the solid ball is at the bottom. If the words said “I have potential” and the ball were still at the top, the joke would collapse.
  • Look for the ghost element. Dashed lines, greyed-out boxes, and empty outlines are doing narrative work. Ask what they are saying is absent, and whether that absence is the point.
  • Prefer a path to an arrow. A curve shows sequence and acceleration; an arrow only shows direction. If the shape can carry the motion, let it.
  • Remove the labels you don’t need. This drawing has no axes, no units, no equations. It works because the ramp is recognizable. Add a label only when the shape is ambiguous.
  • Decide what the diagram is not claiming. This one is not claiming the wearer is lazy, or that potential is measurable, or that physics explains a career. Write down the claims you are not making so the diagram does not accidentally make them.

That last item is the one I get wrong most often. A diagram is a compression, and compression always leaves something out. The useful question is not “is this accurate?” but “what does this leave out, and does the omission change the argument?” Here, the omission is any cause for the ball’s position. There is no hand, no push, no friction coefficient. The ball is just there. That omission is what makes the joke feel like a verdict rather than an excuse.

I would wear this to a team offsite before I would wear it to an interview. Not because it is unprofessional, but because it is a specific kind of joke about expectations, and expectations are easier to talk about when everyone has already agreed they are funny. The drawing does the work. The words just set the tense.

The Speckled Cover and the Ruled Label: What the Composition Notebook Shirt Argues About Familiar Surfaces

The first thing I notice on this shirt is not the ghost. It is the speckle. A black-and-white field of flecks, irregular but evenly distributed, wrapped around a rectangle that reads as a notebook cover. Down the left side runs a dark binding strip. Inside the rectangle, a white ruled label is headed COMPOSITION, and inside that label sit an outlined ghost and an outlined pumpkin. The whole thing is printed. It is a costume of a notebook, not a notebook.

That distinction is the argument. A composition notebook is one of the few objects in American life that most people can recognize from a three-foot distance without reading a word. The speckle does the work. The binding strip does the work. The ruled label does the work. The shirt borrows all three and adds two seasonal marks. What it produces is not a slogan about teaching or Halloween. It is a surface that already means something, re-used as a garment.

Why the speckle carries the recognition

Look at the order of information in the design. The speckled field is the largest element. The binding strip is second. The label is third. The ghost and pumpkin are smallest. That hierarchy is not decorative accident; it is how the original object is built. A composition notebook is identifiable before you read its label, because the cover pattern and the tape binding are the visual signature. The label confirms what the cover already told you.

This is the same reason a well-designed API is recognizable before you read its documentation. The shape of the resource, the naming of the fields, the structure of the error body — those are the speckle. The README is the label. If the speckle is wrong, no amount of label copy rescues the object. If the speckle is right, the label only has to confirm.

I do not know whether the designer thought about this. I am reading the artwork, not the intent. But the reading holds: the shirt puts the recognizable pattern first and the seasonal content second. That is a defensible order.

The label is ruled, not blank

The white label is headed COMPOSITION and contains ruled lines. On a real notebook, those lines are where a student writes name, subject, period. On the shirt, the ruled lines are occupied by an outlined ghost and an outlined pumpkin. The lines are still visible behind and around them.

That is a small, specific choice worth naming. The design does not replace the label with a Halloween graphic. It keeps the label’s structure — the heading, the rules — and places the seasonal marks inside it. The result reads as a notebook that has been annotated, not a notebook that has been replaced. The ghost and pumpkin are entries on a form, not a new form.

I find that more interesting than a shirt that simply prints a pumpkin on a black background. The composition notebook is a surface built for writing. The design respects that surface enough to write on it rather than cover it.

What the costume framing actually claims

The retail title calls this a costume and groups it with teacher group Halloween. The artwork supports the group reading: a matching set of notebook covers is a legible group costume in a way that a single slogan shirt is not. The speckle and binding are consistent across the group; the label is where individual variation could live. On this shirt, the label holds a ghost and a pumpkin. Another shirt in the same family could hold different marks and still read as the same object.

That is a useful design property, and it is the same property that makes a good internal template work. A shared structure with a small variable slot is easier to coordinate across a team than a fully bespoke artifact per person. The notebook cover is the structure. The ghost and pumpkin are the slot. The group costume is the coordination.

I would not push the analogy further than that. The shirt does not demonstrate anything about software, security, or engineering practice. It is a printed garment. But the visual logic — recognizable surface, confirming label, small variable content — is the same logic I look for when I review a contract or a runbook. The reader can take that observation or leave it.

What the shirt is not

It is not a physical notebook. The retail image shows a printed notebook cover on a garment; the object is a shirt with a graphic, not a notebook you can write in. It is not a claim that the wearer teaches, or that the wearer is a student, or that the wearer has any particular relationship to Halloween beyond choosing the shirt. The design borrows a familiar surface; it does not certify anything about the person inside it.

That is worth stating plainly because costume shirts often get read as identity claims. A composition notebook cover is a school supply. Wearing one as a costume is a visual joke about being a school supply, not a statement about your profession. The ghost and pumpkin are seasonal marks, not credentials.

The design as a small lesson in surface design

If I were adapting this observation into a checklist for API or documentation work, it would be three lines:

  • Lead with the pattern that identifies the object before the label is read. The speckle and binding do this. In an API, that is resource shape and field naming.
  • Keep the label’s structure when you add seasonal content. The ruled lines stay; the ghost and pumpkin sit inside them. In a changelog, that is keeping the entry format while changing the entry.
  • Make the variable slot small and consistent. The group costume works because the structure is shared and the variation is contained. In a template, that is a fixed skeleton with one editable section.

None of that is proven by the shirt. It is an observation about how the artwork is built, offered because it is the kind of thing this column notices. The shirt itself is a composition notebook shirt with a speckled cover, a dark binding strip, and a ruled label headed COMPOSITION holding an outlined ghost and pumpkin. That is what the image shows. The rest is reading.

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

The Smallest Possible Refusal: Reading the Lowercase ‘meh.’ on a Dark Shirt

The first thing I notice is the period. On the native garment preview, the word meh sits centered on a dark shirt in very small lowercase letters, and it ends with a full stop. No illustration, no animal, no extra slogan. Just three letters and a punctuation mark, scaled down until you have to be fairly close to read them at all. That period is doing more work than the word.

I have spent a lot of my working life around terse messages that are supposed to carry a lot of weight: error codes, status pages, changelog entries, the one-line comment above a function that explains why the obvious approach is wrong. The shirt is not a software artifact, and I am not going to pretend it demonstrates anything about API design. But it is a useful specimen of a particular kind of writing — the smallest possible refusal — and that is worth reading closely.

What is actually printed

According to the visual observation of the listing, the design is very small, centered lowercase meh. lettering on a dark shirt. There is no animal illustration. There is no pride variant. The retail title mentions “funny sarcastic introvert minimalist saying,” but the artwork itself is more restrained than that phrase suggests. The word is not shouting. It is not set in a novelty font at chest-spanning scale. It is small enough that it reads as a private note rather than a broadcast.

That distinction matters. A shirt that says something loud is asking the room to react. A shirt that says something small is asking the room to lean in, or not. The scale is the argument.

The period changes the grammar of the joke

Without the period, meh is a noise. It is the sound you make when someone asks how the migration went and the honest answer is a shrug. With the period, it becomes a sentence. A complete thought. A verdict that has been considered and then filed.

I have written enough incident summaries to recognize the difference. “Meh” is a reaction. “Meh.” is a position. The period says the speaker has stopped talking. It is the typographic equivalent of closing a ticket with no further action. In a work culture that treats every message as an invitation to a thread, that is a small act of boundary-setting.

I do not know whether the designer intended that reading. I am telling you what the punctuation does to me as a reader. That is interpretation, not history.

Why the small scale is the point

Most slogan shirts fail because they over-promise. They put a large, confident phrase on the chest and then have to live up to it in every interaction. The small meh. does the opposite. It under-promises so thoroughly that it cannot disappoint. It is the visual equivalent of a service-level objective set at 99.0 percent: technically a commitment, practically a shrug.

There is a real design lesson buried in that, though I would not push the analogy too far. When you write a status message, a deprecation notice, or a runbook header, the scale of the language should match the scale of the promise. A giant banner that says “everything is fine” is less trustworthy than a small, specific line that says what is actually known. The shirt is not a runbook. But it understands the principle.

The introvert reading, without the stereotype

The retail title includes the word “introvert,” and the related queries include “meh introvert shirt.” I am not going to claim the shirt is a personality test. What I will say is that the design reads as a low-energy boundary: a way of signaling availability without inviting conversation. It is not unfriendly. It is not rude. It is a small, legible “I am here, but I am not performing.”

That is a useful thing to have in a workplace, and a useful thing to have on a shirt. The difference between a slogan and a usable promise is that a usable promise tells you what to expect. Meh. tells you exactly what to expect: not much, and that is fine.

What I would not claim

I have not worn this shirt. I have not tested it, washed it, or fielded comments on it. I do not know how it fits, how the print holds up, or whether the dark fabric shows lint. Those are real questions for a garment, and I do not have answers. What I have is the artwork and the listing, and I am reading the artwork.

I also would not claim the shirt cures anything, improves anything, or signals membership in a particular tribe. It is a small word on a dark background. The rest is what you bring to it.

The artifact: a one-line boundary template

If you take one thing from this, take the period. The next time you need to close a thread, decline a meeting, or mark a feature as intentionally unsupported, write the smallest complete sentence you can and end it. Not a paragraph of hedging. Not a smiley face. A sentence with a period.

Here is the template I use:

[Decision]. [Reason, if it is not obvious]. [What happens next, or “No further action.”]

For example: “This endpoint is deprecated. Use /v2/orders. No further action on /v1.” That is a meh. in runbook form. It is not warm, but it is clear, and clarity is a kindness.

The shirt is available at the Quirky Cotton Collective listing if you want the object itself. I am not going to tell you it will change your life. It is a small lowercase word with a period. That is the whole argument, and it is enough.

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

Strangling a Legacy Service: Make Each Traffic Slice a Contract, Not a Percentage

Most strangler-fig migrations don’t fail at the new service. They fail at the routing layer, weeks in, when someone asks a simple question: which requests went to the new implementation, and can we undo it? Nobody can answer, because the façade was built as a one-off proxy and the routing rules were never written down.

The fix is to stop treating a traffic slice as a number. A slice is a contract with four clauses: routing key, stickiness, rollback trigger, and per-slice telemetry. Write those down before the first request moves, and the façade becomes the enforcement point for a decision instead of a place where decisions go to hide.

Why “10% to the new service” is not a routing decision

The mechanics of the pattern are well documented. Microsoft’s architecture guidance describes a façade that intercepts requests to the legacy back end and routes them either to the legacy application or to new services, shifting traffic incrementally as functionality migrates. AWS’s prescriptive guidance describes the same proxy layer, routing by API URL, with an anti-corruption layer translating calls between old and new interfaces.

What neither the percentage nor the diagram tells you is which 10%. Path-based routing and percentage-based routing are different instruments with different failure modes, and teams routinely configure one while describing the other. Istio’s traffic-shifting documentation is explicit that weight-based routing redirects a percentage of traffic between destinations, and that the two versions can scale independently without affecting the distribution. That is a statement about stateless request routing. It says nothing about whether a caller’s second request lands on the same implementation as the first.

That gap is where migrations get hurt. A percentage is an implementation detail of the façade. The contract is what the slice promises.

The four clauses of a slice routing contract

1. Routing key: what the façade actually keys on

Pick one and write it down per slice: path, method, header, tenant ID, account ID, or a combination. The key determines what rollback even means. If you route by path, rollback is a config change. If you route by tenant, rollback is a config change plus a decision about in-flight state for the tenants you moved back.

This is also the clause that makes your migration’s real API surface visible. During the transition, the routing rules are a public contract whether or not you documented them. If a consumer can influence routing through a header, that header is now part of your interface.

2. Stickiness: percentage splits assume statelessness

Legacy endpoints often aren’t stateless. Sessions, carts, multi-step checkout flows, and anything with a server-side cursor can break when consecutive requests land on different implementations.

Argo Rollouts’ concepts documentation is unusually blunt about this: not all applications can have different versions running in parallel at the same time, which is what canaries do, and this can be a showstopper for legacy applications. It also notes that Argo Rollouts doesn’t control traffic flow for connections it doesn’t understand, such as binary or queue channels.

So the stickiness clause has to answer: for this slice, how does a caller stay on one side for the duration of a request flow? Options include keying on a stable identifier (tenant, account, session), pinning by header set at the edge, or declining to slice at all and migrating the endpoint atomically. “We’ll figure it out” is not one of the options.

3. Rollback trigger: the signal and the threshold, agreed in advance

Google’s SRE Workbook defines canarying as a partial and time-limited deployment of a change and its evaluation, and lists two required capabilities: a method to deploy the change to a subset of the population, and an evaluation process to judge whether the change is good or bad. The evaluation is not optional infrastructure. It is half the definition.

Before a slice ships, name the signal (error rate delta, latency percentile, a business metric), the threshold that flips it back, and who has authority to flip it. Then verify the legacy path is still warm and reachable at that moment. A rollback trigger that points at a legacy deployment you scaled to zero last month is a decoration.

This is also where the façade’s own reliability matters. Both Microsoft and AWS flag that the proxy layer can become a single point of failure or a performance bottleneck. If the thing that routes your rollback is the thing that’s degraded, your rollback plan is theoretical.

4. Per-slice telemetry: aggregate metrics will lie to you

The SRE Workbook makes a point that every team running a canary discovers the hard way: because the canary population is a small subset of production, its errors are diluted in the overall error rate, and the canary evaluation is difficult to see and track compared to the overall traffic trend. The fix it describes is breaking metrics out by version so canary and control can be compared directly.

For a strangler migration, version isn’t the right dimension. Slice is. Emit the routing decision as a first-class event, tagged with slice ID, destination (legacy or new), routing key value, and outcome. Then you can answer “how is slice 3 doing” without reconstructing it from three dashboards and a guess.

{
  "event": "route.decision",
  "slice": "orders.read.v2",
  "destination": "new",
  "routing_key": {"type": "tenant", "value": "t_8842"},
  "sticky": true,
  "latency_ms": 41,
  "status": 200,
  "contract_version": "2026-10-01"
}

The contract_version field is the cheap part and the one you’ll thank yourself for. When the routing rules change, the telemetry tells you which requests were governed by which version of the contract.

Before and after: the same slice, two ways

A typical first attempt at a slice config, using weight-based routing:

# before: a percentage with no contract
route:
  - destination: legacy
    weight: 90
  - destination: new
    weight: 10

Nothing here says which requests, whether they stick, what flips it back, or how you’ll tell whether it’s working. The same slice with the contract made explicit:

# after: a slice with a written contract
slice: orders.read.v2
routing_key:
  type: tenant
  source: header X-Tenant-Id
  allowlist: [t_8842, t_9107, t_9311]
stickiness:
  mode: tenant-pinned
  ttl: request-flow
rollback:
  signal: error_rate_delta_vs_control
  owner: orders-platform
  legacy_path_verified: true
telemetry:
  emit: route.decision
  dimensions: [slice, destination, routing_key, contract_version]
owner: orders-platform
exit_criteria: legacy route receives zero requests for 14 days

The second version is longer. It is also the only one that can be reviewed, audited, or rolled back by someone who wasn’t in the room when it was written.

Feature flags and the façade are not the same instrument

Feature flags are a legitimate part of this toolkit. OpenFeature’s documentation describes flags as runtime-evaluated switches that alter application behavior without deploying new code, and lists canary releases — rolling out to an initially small subset of users — as a standard use case.

The distinction that matters: a flag decides behavior inside a service. The façade decides which service receives the request. Teams sometimes use flags to simulate routing, which works until the request never reaches the code that evaluates the flag. Be explicit about which layer owns the routing decision for each slice, and don’t let both layers claim it.

Governance: the temporary façade that becomes permanent

Martin Fowler’s original write-up names the cost directly: people often balk at building transitional architecture to let the new and legacy systems coexist, code that will go away once modernization is complete. The bargain is real — reduced risk and earlier value in exchange for infrastructure you intend to delete.

The bargain only holds if someone is responsible for the deletion. Two rules keep it honest:

  • One owner per slice. Named in the contract, not implied by team structure.
  • An exit criterion written before the slice ships. “Legacy route receives zero requests for N days, no consumers or reporting jobs still read from it, rollback window closed” is a criterion. “When we’re confident” is not.

Without those, the façade accumulates routing rules nobody can safely remove, and the transitional architecture quietly becomes the permanent one — with none of the design review a permanent system would have received.

The artifact: a one-page slice routing contract

Fill this in before each slice ships. It fits on a page, it goes in the repo next to the routing config, and it is the thing you point at when someone asks why a request went where it went.

SLICE ROUTING CONTRACT

Slice ID:            
Owner:               
Status:              proposed | active | draining | retired

1. ROUTING KEY
   Type:             path | method | header | tenant | account | composite
   Source:           
   Allowlist/rule:   

2. STICKINESS
   Mode:             stateless | tenant-pinned | session-pinned | atomic (no split)
   TTL:              
   Rationale:        

3. ROLLBACK
   Signal:           
   Threshold:        
   Authority:        
   Legacy path verified reachable:   yes | no
   Last verified:    

4. TELEMETRY
   Event emitted:    
   Dimensions:       slice, destination, routing_key, contract_version
   Dashboard:        

5. EXIT CRITERIA
   Legacy traffic zero for:          
   Downstream consumers checked:     
   Rollback window closed on:        

6. NOTES
   Known stateful edge cases:        
   Cross-system dependencies:        

The contract doesn’t make the migration easy. It makes each step reviewable, reversible, and finishable — which is the part the percentage never gave you.

FAQ

Can we skip the contract for small, obviously stateless endpoints?

You can shorten it, but write down the routing key and the exit criterion at minimum. “Obviously stateless” is a claim someone should be able to check later, and the exit criterion is what stops the route from lingering.

What if the legacy service can’t be modified to support an anti-corruption layer?

That constrains the migration shape, not the contract. You can still route at the edge and translate at the new service’s boundary. What you lose is the ability to redirect internal calls from inside the monolith, which changes which slices are feasible — worth knowing before you pick the first one.

How do we handle a slice that spans multiple endpoints?

Treat the slice as the unit, not the endpoint. The routing key and stickiness clause apply to the whole flow, and the exit criterion covers every route in it. Slicing per endpoint inside a multi-step flow is how you get a caller whose first request hit the new service and whose second hit the legacy one.

Where does the contract live?

Next to the routing configuration, versioned with it. If the config and the contract can drift independently, they will.

The Caution Triangle Is Doing the Work: Reading ‘Not Friendly Do Not Touch’ as a Boundary, Not a Slogan

Three words and a pictogram. That is the whole design: NOT FRIENDLY in warning-sign lettering, DO NOT TOUCH beneath it, a yellow caution triangle, and a crossed-out hand. No punchline, no mascot, no lowercase irony. It reads less like a joke you wear and more like a label someone printed and stuck to themselves.

That is what makes it worth looking at closely. The shirt is a small exercise in public-sign grammar, and the grammar is stricter than most slogans people put on clothing. I want to argue that the caution triangle, not the words, is what turns this from a mood into a boundary — and that the difference matters for anyone who has ever tried to write a rule other people will actually follow.

Editorial note: this publication and Quirky Cotton Collective share an owner, so treat this as an owner-authorized reading of the design rather than an independent review.

What is actually printed

Sticking to what the artwork shows: the phrase NOT FRIENDLY sits above DO NOT TOUCH in the visual register of a hazard placard. A yellow caution triangle appears with the lettering, and a hand is shown crossed out — the standard visual shorthand for do not applied to a body part rather than a machine or a chemical.

I am not going to claim this is a faithful reproduction of any regulatory standard, because I have not verified that and the shirt does not need it to work. What I can say from the design itself is that it borrows the form of industrial warning signage: terse, imperative, pictogram-assisted, and addressed to a stranger who has not agreed to read carefully.

Why the triangle carries more weight than the sentence

Words on a shirt are usually read as self-description. I am this kind of person. A warning sign is read differently. It is addressed outward, to whoever is approaching, and it assumes they might not stop to parse a full sentence. The triangle is the part that says this is a rule, not a vibe.

That distinction is the useful argument here. A slogan makes a claim about the wearer. A sign makes a demand on the reader. Not friendly alone is a slogan — it could be self-deprecating, it could be a boast, it could be a line from a song. Add do not touch and a crossed-out hand, and the sentence changes direction. It stops describing and starts instructing.

Whether that instruction is funny, rude, or simply honest depends entirely on who is standing in front of it. The design does not resolve that. It just makes the demand legible.

The crossed-out hand is the most specific element

Of the three components, the crossed-out hand is the one I keep returning to. A caution triangle is generic — it could precede almost any warning. The words are blunt but abstract. The hand is concrete. It names the exact behavior being prohibited, and it does so without a sentence.

That is a real design decision, and it is the kind of decision that separates a usable sign from a decorative one. Compare two ways of saying the same thing:

  • Abstract: Please respect my personal space. Polite, clear to a careful reader, easy to ignore.
  • Specific: a crossed-out hand under a caution triangle. Names the action, not the feeling.

The second version is harder to argue with because it does not ask the reader to infer what respect means in this particular moment. It points at a hand and draws a line through it. Whatever else you think of the shirt, that is efficient communication.

Where the design gets ambiguous, on purpose

I do not think the shirt is a serious accessibility aid, and I would not describe it as one. It is a piece of clothing that borrows the authority of signage for a personal, slightly theatrical purpose. The humor — if you find it funny — comes from the mismatch between the bureaucratic register of a hazard placard and the very human irritation underneath it.

That mismatch is also the design’s weak point, in the sense that it can be read two ways. One reader sees a boundary. Another sees a bit. The shirt does not choose, and I do not think it can. A real warning sign has an institution behind it that will enforce the rule. This one has a person wearing it, and the enforcement is whatever that person is willing to do when someone ignores the triangle.

That is not a flaw exactly. It is the joke. But it is worth naming, because it is the same gap that shows up whenever a team writes a rule and assumes the rule will hold itself up.

The part that connects to actual work

I spend most of my working life writing things other people are supposed to follow: API contracts, error-response conventions, runbooks, review checklists. The recurring failure I see is not that the rules are wrong. It is that they are written as descriptions when they need to be written as instructions, or the reverse.

A deprecation notice that says this endpoint is deprecated is a slogan. It describes a state. A deprecation notice that says this endpoint returns 410 after 2026-03-01; migrate to /v2/orders before then is a sign. It names the behavior, the date, and the replacement. The difference is not length. It is whether the reader knows what to do next without asking.

The shirt is a reminder of that distinction in its crudest form. Not friendly is a state. Do not touch, with a crossed-out hand, is an instruction. The design works because it includes both, and puts the instruction where the reader will actually see it.

What I would change, and what I would not

If I were designing this from scratch, I would keep the triangle and the crossed-out hand and I would probably shorten the text. NOT FRIENDLY and DO NOT TOUCH are doing slightly redundant work — the first is a characterization, the second is a rule. A stricter sign would drop the characterization and trust the pictogram. But the redundancy is also what makes it readable as a joke rather than a genuine hazard placard, so removing it would change the object entirely. I would leave it.

What I would not do is treat the shirt as evidence of anything about introversion, social behavior, or the people who buy it. The design is a visual argument, not a study. It says: here is a boundary, stated in the most legible grammar available. Whether that boundary is real, and how it is enforced, is up to the person wearing it — which is exactly the gap between a sign and a promise.

The shirt is available as a standard t-shirt through the listing: Not Friendly Do Not Touch Funny Warning Sign Introvert.

The artifact: a boundary-statement checklist

If you write rules for other people — in a runbook, a contract, an onboarding doc, or on a shirt — the same three questions apply. I keep this as a short checklist before I publish anything that is supposed to change someone’s behavior:

  • Is it a state or an instruction? If it describes a condition, add the action. If it gives an action, name the condition that triggers it.
  • Does it name the specific behavior? Do not touch beats respect my space because it points at the thing being prohibited. Replace abstractions with the concrete action.
  • Is there a pictogram, example, or code block? A visual or concrete instance does more work than a paragraph of explanation. The crossed-out hand is the model here.
  • Who enforces it, and how? A rule without an owner and a consequence is a slogan. Name the owner and the mechanism, or admit it is advisory.
  • Can it be read two ways? If yes, decide whether the ambiguity is the point. On a shirt, it can be. In a runbook, it usually cannot.

Five lines. The shirt passes four of them and fails the fifth on purpose. That is a better ratio than most documentation I have reviewed.

Page 1 of 14

Powered by WordPress & Theme by Anders Norén