BlogDocsProduct Log in Start free
Behind Hawzu

A Release Can Be Complete and Not Ready

A release status records what a person decided. Readiness records what the evidence says. Hawzu keeps the two separate — and puts hard gates in front of the score, so an average can't launder an open blocker.

Two columns — a lifecycle dropdown set by a person on the left, and a readiness verdict assembled from coverage, executions, requirements and defects on the right.

A release can be marked Complete with three critical defects open against it.

Nothing in Hawzu stops that, and nothing should. Someone decided to ship. Maybe the defects are in a feature that got flagged off, maybe there’s a hotfix scheduled, maybe it was a bad call. A status field records what a person decided. It was never in a position to check.

The problem starts when one status field is quietly doing both jobs — recording a decision and implying a verdict — and readers can’t tell which one they’re looking at.

Two things wearing the same word

There are two genuinely different questions here.

Where is this release in its lifecycle? Not started, in progress, paused, completed, archived. This is scheduling, and scheduling is a human matter. A release paused because a customer moved a date isn’t a data condition; no amount of test evidence implies it. So in Hawzu this is set by a person, with only two rules: you can’t send a release back to not-started, and nothing leaves archived.

Is the evidence good enough to ship? That one has an answer in the data, and it should never be typeable. If a person can type it, then sooner or later it says Ready because someone was optimistic on a Friday, and it means nothing afterwards.

A status records a decision. It can’t check one.

Status — set by a person
  • Not started
  • In progress
  • Paused
  • Completed
  • Archived

Nothing feeds this. It records a decision.

Readiness — computed from data
CoverageExecutionsRequirementsDefects
Gates any one vetoes the score
Not ready cause: open critical defect

No one can type this. It reports a finding.

The same release, read two ways. A tool with one status field has to pick which of these it means — and then can't tell you the other.

Splitting them costs you the comfortable ambiguity of a single field and buys you a release page where the two disagree out loud. Completed, and Not ready. That’s not a contradiction to be designed away — it’s the most informative thing the screen can tell you.

Gates first, score second

Readiness is a fold over four dimensions — coverage, execution, requirements, defects — weighted, and renormalised when a dimension doesn’t apply to a release rather than scored as zero.

But a weighted score on its own launders blockers. A release with a 96% pass rate, good coverage and one open critical defect computes to a high number, and that number is a lie by arithmetic. Averages are built to let strong dimensions absorb weak ones. That’s the whole point of them, and it’s exactly wrong for a ship decision.

So gates run first and can veto the score outright: coverage below the bar, a failure rate above the threshold, any open critical, too many failures not linked to a defect, requirements verification below the bar. Each gate produces a verdict and names the cause. Only if nothing is gated does the score get to speak.

The number had to leave the browser

Readiness used to be computed in the front end. Three different screens each folded the same raw numbers into a verdict, in their own code.

Two of them disagreed.

Neither was wrong, exactly — they’d drifted through ordinary maintenance, the way three copies of anything drift. The bug wasn’t in either implementation. It was in there being three. So the whole calculation moved to one place on the server, as pure arithmetic with no database access at all: hand it counts, it hands back a verdict. Arithmetic hidden behind a database import is arithmetic nobody tests.

Porting it surfaced a trap worth knowing about if you ever move a calculation between languages. JavaScript and Python disagree about exact half-values: Math.round rounds a half upward, while Python’s round rounds a half to the nearest even number — so 2.5 becomes 3 in one and 2 in the other. Same inputs, same formula, different score, and it only shows up on exact halves, which is to say rarely enough to ship and often enough to be found by a customer rather than by us. The server now reproduces the browser’s rule deliberately.

The related discipline: every percentage recomputes from raw counts, never from another percentage that’s already been rounded. Round once, at the end, or you spend the rest of the calculation compounding a number you threw precision away on.

What the engine refuses to decide

Two things are deliberately outside the fold.

It doesn’t pick colours. A verdict comes back with a semantic tone — critical, caution, good, neutral — and the screen decides what that looks like. A calculation that names UI colours has opinions about a screen it can’t see, and the first time you render it somewhere else, the opinions come with it.

And it doesn’t own the bars. The thresholds are a parameter passed into the calculation, not constants baked into it. Two of them look interchangeable and aren’t: the share of your suite that’s been executed and the share of your requirements that have been verified are different claims that happened to be set to the same number. Keeping them as one constant would have been a bug waiting for the first team that cared about one and not the other.

To be straight about where this stands: every release today is scored against one shared set of defaults. Per-project acceptance criteria is a seam that exists and isn’t wired up yet — the calculation accepts them, nothing writes them.

What is shipped is that sign-off freezes the bars along with the numbers. When a release is signed off, the criteria it was judged against are frozen into the snapshot with it. Without that, editing your thresholds next quarter would retroactively re-judge every release you ever signed off — and a release that shipped as Ready would quietly start reading Not ready, with the sign-off certificate still saying otherwise.

Both fields, on purpose

The release page shows what a person decided and what the evidence found, side by side, and never lets one overwrite the other. The lifecycle is yours. The verdict isn’t for sale.

That’s the same commitment a frozen test run makes, one level up: the record of what happened doesn’t bend to what anyone would prefer it said.

If a person can type “Ready”, it stops meaning anything the day someone does.

Continue learning