{"slug":"lifecycle-status","meta":{"title":"Lifecycle Status","slug":"lifecycle-status","kind":"pattern","summary":"One named state per record, drawn from a small closed set that everyone in the business already says out loud — the single field that answers \"what happens to this next\".","problem":"A record's real state is spread across five booleans and two timestamps, so nobody can answer \"where is this?\" without reading all of them and knowing the precedence. Two people read the same row and reach different conclusions.","family":["orient","workflow"],"data_shape":["record"],"principles":["orientation","consistency"],"interaction":["scanning"],"density":"low","complexity":"low","status":"stable","visibility":"public","use_when":["The record moves through stages and different stages mean different work.","People ask \"what stage is X at?\" in conversation.","You are about to add a third boolean that interacts with the first two."],"avoid_when":["The record genuinely has independent flags — archived and starred are not stages of one lifecycle, they are two different facts.","There is exactly one state. Then it is not a lifecycle, it is a fact."],"alternatives":[{"slug":"object-identity-header","when":"You have the state and need somewhere prominent to put it."},{"slug":"linear-workflow","when":"The states are settled and you now need to move records between them."}],"ask_leo":"Replace the boolean flags on this model with one lifecycle status field.\n\n- Use a small closed set of named states, in the words the business already\n  uses. Six or fewer is usual; more than about eight means two lifecycles are\n  tangled together.\n- Store the state as a single value, not as several booleans that have to be\n  read in a precedence order.\n- Every state must have a plain-English name a non-technical person would use\n  in a sentence.\n- Render the state as a word everywhere it appears. Colour is optional and\n  must never be the only carrier of the meaning.\n- Reserve the attention colour for the states that need a human to act. States\n  that are simply where the record sits are neutral grey.\n- Keep flags that are genuinely independent — archived, flagged, starred — as\n  separate fields. Do not fold them into the lifecycle.\n","related":[{"title":"Color, Icons, and Contrast","url":"/cookbook/color-icons-and-contrast","summary":"How to render the state so colour never carries the meaning alone."},{"title":"One consistent pattern","url":"/patterns/consistency","summary":"One object, one vocabulary, everywhere it appears."}]},"body":"## Anatomy\n\n```\n     Draft  →  In review  →  Approved  →  Scheduled  →  Complete\n                   ▲                                        │\n                   └──────────── Changes requested ◀─────────┘\n\n  needs a human:  In review, Changes requested      → amber\n  just where it is: Draft, Approved, Scheduled      → grey\n  finished:        Complete                          → grey\n```\n\nTwo rules do most of the work:\n\n- **One field, closed set.** Not `approved?` plus `reviewed?` plus\n  `scheduled_at`. The moment state is derived from several fields, it has a\n  precedence order, and precedence orders live in one developer's head.\n- **The names are the business's, not the schema's.** If people say \"waiting on\n  the client\", the state is not `pending_external`. Vocabulary you have to\n  translate is vocabulary people get wrong.\n\n## Why it works\n\nIt makes \"what happens next\" a lookup instead of an inference. That is worth\nmore than it sounds: inference is where two colleagues quietly disagree, and the\ndisagreement only surfaces when something has already gone wrong.\n\nIt also gives every other pattern something to hold on to. A\n[dense table](/patterns/dense-operational-table) needs one column, not five\nbooleans. A review queue needs one filter. A notification needs one trigger. The\nlifecycle field is the join between the data model and how people talk.\n\nAnd it bounds colour. With a named set you can decide, once, which states need a\nhuman — and only those spend the attention colour. Without it, every flag argues\nfor its own badge and you end up with *badge soup*.\n\n## Getting it wrong\n\n- **Boolean creep.** Each flag was reasonable alone; together they encode a\n  state machine nobody wrote down.\n- **Rainbow status.** Six hues at equal saturation, some meaning two things.\n  Under a squint test the table becomes confetti and colour stops separating\n  anything.\n- **States that are really two lifecycles.** \"Draft, In review, Paid, Refunded\"\n  mixes an editorial lifecycle with a billing one. Split them.\n- **A state nobody can define.** If two people describe \"Pending\" differently,\n  it is not a state, it is a gap.\n\n## Exemplars\n\n**Stripe** keeps payment status to a handful of words — succeeded, pending,\nfailed, refunded, disputed — and the entire product is built on them. It is a\ngood demonstration that a small closed set scales further than a flexible one.\n\n**GitHub pull requests** have exactly three states (open, closed, merged) and\npush everything else — draft, review status, checks — into separate, clearly\nindependent fields. That separation is the discipline most products skip.\n\nThe extractable rule: **if you cannot name the states, you do not yet understand\nthe process — and no amount of interface will cover that.** Getting the\nvocabulary right is the design work; rendering it is the easy part.\n"}