Tuesday Isn’t a Valid State Transition

Calendars, evidence, and the difference between done and stuck

I expected my pear wine to spend two to three weeks in primary fermentation.

It spent nineteen days. The yeast went in on September 23, and on October 12 I siphoned the wine off its sediment and into a glass carboy.

So the calendar was right. That still isn’t why I moved it.

What the calendar is for

Every fermentation recipe comes with time estimates. Primary takes one to three weeks. Secondary takes a few more. Bulk age for a few months. Those numbers aren’t wrong. They’re averages across a lot of batches, and they’re useful for exactly one thing: planning. They tell you when to have a clean carboy ready, when to clear some space, when to stop making weekend plans.

What they can’t do is tell you that this batch is ready. Yeast doesn’t read recipes. It works at a speed set by temperature, sugar, nutrients, acidity, the particular strain, and the particular fruit, none of which the recipe author has ever met.

So the recipe gives you a forecast, and the fermentation gives you evidence. Only one of those gets to move the wine. If they happen to agree, as they did this time, that’s a pleasant coincidence, not an authorization.

The rule winemakers actually use

The traditional test for “primary is done, time to rack” is simple: take readings over several days, and when you get the same reading three times running, fermentation has finished.

What I like about that rule is what it isn’t. It isn’t a date. It also isn’t a single reading, because one measurement tells you where the fermentation is, not whether it’s still moving. It’s evidence sustained over a window. Time is part of the test, but only as the space in which the evidence has to hold still.

Here’s what the pear’s refractometer readings actually looked like:

Date Raw Brix
Sep 23 12.9 (before the yeast)
Sep 25 6.0
Sep 30 6.0
Oct 2 5.7
Oct 4 5.6
Oct 7 5.6
Oct 10 5.6
Oct 11 5.6
Oct 12 5.6

Two things in that table are worth a closer look.

First, the early plateau. Most of the sugar was gone within two days, and then the reading sat at exactly 6.0 for five days. If I’d been looking for stillness and nothing else, that looked a lot like the end. It wasn’t. The fermentation was slowing down, not stopping, and over the next week it crept down further before it finally held. Two matching readings were a pause, not a verdict, which is precisely why the rule asks for three.

Second, I didn’t stop at three. By October 10 I had three consecutive readings at 5.6, which satisfies the rule. I kept reading anyway, and waited until five readings across eight days agreed.

Then the racking itself handed me one more piece of evidence. There was more wine in the primary fermenter than the glass carboy could hold. Rather than pour the extra down the drain, I filled a hydrometer jar with it: a completely different instrument, measuring a completely different physical property. The refractometer, once corrected for alcohol, put the wine at about 1.004. The hydrometer said 1.002. Two independent instruments, close enough to agree, and the second one confirmed that the correction formula I’d been trusting for three weeks was doing its job.

I like that this happened by accident, because it isn’t really an accident. A hydrometer needs a large sample, which in a one-gallon batch is too expensive to take routinely. At the moment of a transition, when wine is being moved anyway, that same sample costs nothing. The transition is when the more trustworthy evidence becomes cheap.

None of that is the calendar talking. That’s a guard, and it was satisfied before the wine moved.

Status is a column anyone can write

In a lot of systems, state is a string in a database, and a transition is an UPDATE statement.

UPDATE orders SET status = 'shipped' WHERE id = 4417;

Nothing in that statement requires that anything was shipped. It’s a label, and anyone with write access can apply it for any reason: a cron job that marks orders shipped two days after payment because that’s usually true, a release train that promotes whatever’s on the branch because it’s Tuesday, a support tool that flips a flag to make a ticket go away.

Each of these is the software version of racking the wine because the recipe said two to three weeks. Most of the time it’ll even be right, just as my calendar was. The transition happens on schedule, the dashboard updates, and the system now asserts a state that nobody verified.

The fix isn’t elaborate. A transition should have a guard, a condition that has to be true, based on evidence, before the state can change. “Shipped” should require a carrier scan. “Released” should require the checks that define releasable. “Primary complete” should require stable readings. The state machine’s job is to refuse the transition when the evidence isn’t there, however much the calendar insists, and to keep refusing even on the days the calendar happens to be right.

Same evidence, two states

Here’s where it gets interesting. “Three consistent readings” is the signal for done. But a fermentation that has stalled also produces three consistent readings.

Mead is notorious for this. Honey has very little natural buffering, so as fermentation proceeds the pH drifts down, and past a certain point the yeast slows or quits entirely, often well short of dry. From the refractometer’s point of view, a stuck mead and a finished mead look exactly the same: a number that has stopped moving.

My fireweed mead is a few days from that question right now. Its pH started at 3.5 and dropped to 3.1 within the first four days, where it has mostly stayed. Here are its last few readings:

Date Raw Brix Corrected Brix Estimated ABV
Oct 7 11.8 4.3 10.4%
Oct 10 9.0 -0.3 12.8%
Oct 11 8.6 -1.0 13.1%
Oct 12 8.4 -1.3 13.3%

Look at the corrected column. The calculator now claims the mead contains less than no sugar, which is impossible. That’s a model being pushed past the range it was built for, and it’s a nice reminder that a derived number can confidently assert something no physical system can be. If my guard were “corrected Brix at or below zero,” I’d have racked the mead two days ago.

The raw readings tell a different story: they’re still falling, slowly. The mead isn’t still yet, so the guard isn’t satisfied, no matter what the corrected number says. And when it does go still, the question becomes where. The yeast I used, 71B, is generally rated to tolerate around 14% alcohol, and the estimate is already over 13%. A mead that stops near its yeast’s limit with the sugar it was expected to consume gone is probably done. One that stops early, with sugar left over and the pH low, is probably stuck.

The mead has already taught me this lesson once, in miniature. Mead yeast needs nutrients added in stages early on, and the textbook guidance ties the last addition to the “one-third sugar break,” the point where about a third of the sugar has been consumed. I scheduled my three additions by day instead. As it happened, the last one landed right around the sugar break anyway. Sometimes luck happens. But luck is exactly what it was: the schedule didn’t know where the sugar was, and if the mead had been a few days slower, the same calendar would have fed it at the wrong time.

In other words, the guard for “done” can’t just be “stable.” It has to be “stable where a finished fermentation should be,” which means the guard needs a model of the expected end state, not just a test for motion. And it needs to know which of its inputs to trust when they disagree.

Software has the same ambiguity everywhere. A batch job whose progress counter hasn’t moved in an hour has either finished or hung. A queue depth sitting at zero means either everything was processed or the consumer died. A replication lag of zero might mean the replica is caught up, or that it stopped receiving anything to lag behind. In every case, “nothing is changing” is evidence consistent with two opposite states, and a transition that only checks for stillness will eventually pick the wrong one.

A transition is an event with a receipt

The row in my pear log for October 12 reads, in effect: 11:10 a.m., 5.6 Brix, hydrometer 1.002, moved to glass carboy. The transition and the evidence that justified it are on the same line.

That matters more than the new status. Months from now, if the wine turns out strange, “racked on October 12” tells me almost nothing. “Racked after five stable readings, confirmed by a second instrument” tells me what I believed, what I based it on, and whether I might have been fooled by a stall.

Systems benefit from the same habit. A state change shouldn’t just overwrite the previous state. It should be recorded as an event: what the state was, what it became, what evidence satisfied the guard, and who or what made the call. Then the current state is something you can explain, not just something you can read. When “shipped” turns out to be wrong, you can see whether the guard was bad, the evidence was bad, or the guard was never checked at all.

What time is actually allowed to do

None of this means time is irrelevant. It plays three legitimate roles, and it’s worth keeping them straight.

Time can permit a transition. Some transitions shouldn’t happen before a minimum interval no matter what the evidence says, like a waiting period before a refund clears, or a wine that needs some minimum aging before bottling. Here time is a precondition alongside the evidence, not a replacement for it.

Time can create the conditions for evidence. The three-readings rule only works because readings are spread over days. Stability over a window requires a window. The pear’s false plateau is exactly why: two readings five days apart looked still, and only more time showed they weren’t.

Time can raise a question. If a fermentation hasn’t finished long after it should have, that’s worth investigating. But notice what the timeout actually tells you: something is unusual. It doesn’t tell you the batch is done, and it doesn’t tell you the batch has failed.

That last one is where software most often goes wrong. Timeouts routinely get wired straight to a terminal state: no response in thirty seconds means failed, no update in three days means complete. A timeout is legitimate evidence that you need to look. It is rarely evidence of what you’ll find when you do. A timeout should produce a question, not an answer.

What time shouldn’t do, on its own, is cause a transition. Nineteen days arriving was not a fact about the pear wine.

Guarding your transitions

Treat schedules as forecasts. Plan with them. Don’t let them move state, even when they’re right.

Put a guard on every transition that matters. If a state can be reached by writing a string, eventually it will be reached for the wrong reason.

Test for the expected end state, not just stillness. Stable and finished are different claims. A guard that can’t tell them apart will someday declare a stall a success.

Know which evidence to trust when it disagrees. A derived value can be confidently impossible. Raw observations and independent instruments are how you catch it.

Record the evidence with the transition. The new state is the conclusion. Keep the reasons next to it.

Let timeouts ask, not answer. Route them to “needs attention,” not to “done” or “failed.”

The pear is in its carboy now, clearing slowly. October 12 was the day it moved. It happened to be a Monday. That wasn’t the reason either.

Facebooktwitterredditlinkedinmail

Your Metric Is Not Your State

What a refractometer taught me about observability

In July, I wrote about a farmer walking a vineyard row at dawn, crushing a grape onto a refractometer prism, and typing “13.5 Brix” into a chat window. The agent on the other end didn’t care how the number arrived. To the Digital Scribe, I wrote, a number is just a number.

In the vineyard, that’s a defensible position. Fresh grape juice is exactly what a refractometer is built to read.

This fall, I’ve been pointing the same kind of instrument at juice that’s fermenting. It turns out the agent should have cared.

Three batches and a prism

Over the last several weeks I’ve started three small batches: a blackberry wine that’s now bulk aging, a pear wine that began fermenting on September 24, and a fireweed honey mead that got its yeast two days later. They’re one-gallon batches, which matters for this story, because at that size every sample you pull is wine you don’t get to drink and oxygen you’ve let in.

So I track them with a refractometer. A few drops of liquid on a glass prism, close the cover, hold it up to the light, read a number off the scale. The number is Brix, roughly the percentage of dissolved sugar. It’s fast, cheap, and costs a few drops per reading. During the blackberry’s primary fermentation I took readings twice a day, every time I punched down the cap of fruit.

And once fermentation starts, the refractometer is wrong.

The instrument isn’t lying, exactly

A refractometer measures how much light bends as it passes through a liquid. Dissolved sugar bends light, so in fresh juice the amount of bending is a good proxy for the amount of sugar. The scale on the instrument is calibrated on that assumption.

Fermentation breaks the assumption. Yeast converts sugar into alcohol, and alcohol bends light too. A few days into primary, the refractometer is reporting the combined effect of the sugar that remains and the alcohol that’s been produced, and presenting all of it as sugar. The reading comes out higher than the real sugar content. Taken at face value, it says the fermentation is further behind than it actually is.

To get something usable, you run each reading through a correction formula. Most home winemakers use calculators built on formulas originally developed for brewing, and every one of them needs the same extra input: the original reading, taken before the yeast went in.

Here’s what that looked like on the blackberry:

Day Raw refractometer (Brix) Corrected value
08/29/2026 20.7 20.7
08/30/2026 18.4 16.5
08/30/2026 17.0 14.3
08/31/2026 13.2 8.1
08/31/2026 10.8 4.1
09/01/2026 8.7 0.7
09/01/2026 8.3 0.1

Nothing is malfunctioning. The instrument is doing exactly what it was built to do. What changed is the system it’s pointed at, and a number that was a reliable proxy in one state became a misleading one in the next.

Neither instrument measures sugar

The traditional alternative is a hydrometer, a weighted glass float. The deeper it sinks, the less dense the liquid. Sugar makes liquid denser, so falling specific gravity means sugar is being consumed.

Except the hydrometer doesn’t measure sugar either. It measures density, and alcohol is less dense than water, so as fermentation proceeds the alcohol pulls the reading down on its own. A finished dry wine routinely reads below 1.000, lower than plain water, which would be impossible if the hydrometer were really a sugar meter.

So the refractometer measures how light bends and the hydrometer measures how heavy the liquid is. Neither measures what I actually care about: how much sugar is left, whether the yeast is still working, whether this wine is done. I infer those. The instruments give me evidence.

Even a steady reading is ambiguous. The traditional sign that fermentation has finished is the same reading several days running. But a fermentation that has stalled also produces the same reading several days running. Mead is notorious for this: honey has very little natural buffering, the pH drifts down as fermentation proceeds, and the yeast can slow or stop well short of dry. A flat line can mean done or stuck, and the instrument can’t tell you which. You tell them apart with other evidence: where the line flattened, what the pH is doing, what you expected to see.

That distinction between evidence and state sounds pedantic until you notice it’s one we get wrong in software constantly.

Your dashboard has the same problem

CPU utilization isn’t health. A service at 15% CPU can be deadlocked, and one at 90% can be doing exactly what you want. An HTTP 200 isn’t correctness; it tells you the server returned a response, not that the response was right. A green health check tells you the health check endpoint answered. Model confidence isn’t truth; it’s a number the model produced about its own output.

Each of these started as a reasonable proxy under a particular set of conditions. Then the system changed, a new failure mode appeared, or someone started optimizing the number itself, and the proxy drifted away from the state it was meant to represent. The dashboard kept rendering it with exactly the same authority.

That’s the refractometer problem. The metric was calibrated for one state of the system and is now being read in another, and nothing on the display tells you so. And like the flat fermentation line, a steady metric can mean two opposite things: a quiet error rate might mean a healthy service, or one that stopped receiving traffic an hour ago.

The failure isn’t collecting the metric. It’s treating the metric as the state instead of as evidence about the state.

A reading needs its history

Back to that correction formula. To interpret today’s reading, you need the reading from before fermentation began. Without it, the current number is close to uninterpretable. The same Brix value could describe a fermentation that has barely started or one that’s nearly finished, depending entirely on where it began.

The meaning of a measurement depends on its lineage.

This is where I think observability practice most often falls short. We store the value and the timestamp and call it a record. What we usually drop is everything needed to interpret it later: which instrument produced it, under what conditions, what calibration assumptions it carried, what baseline it should be read against, and what state the system was believed to be in at the time.

My fermentation log keeps the raw reading and the corrected value side by side. The corrected value is what I act on. The raw value is what I re-derive from if I later learn the correction was off, find a better formula, or discover I misread the original. A corrected value on its own is a conclusion with its evidence thrown away.

That’s a pattern worth stealing. Store the observation separately from the interpretation. Keep the provenance that makes the observation meaningful. Let state be something you derive, with its evidence attached, rather than something you overwrite. It’s the same idea behind the forensic receipt work I’ve been doing: a claim should carry what it was based on.

Watching isn’t free

One more thing the one-gallon batches have taught me. I use a refractometer because a hydrometer needs a much larger sample, and in a small carboy that sample is a real fraction of the batch, and pulling it lets oxygen in. The act of measuring changes the system being measured.

It also means deciding how often to look. The blackberry had to be punched down twice a day anyway, so twice-daily readings were free. The pear and the mead don’t need that kind of handling, so I’ve settled on a reading every couple of days: often enough to catch a stall, rare enough to leave the batch alone. That’s a measurement policy, and it means my log has deliberate gaps in it. Those gaps are their own story, and I’ll come back to them.

Software has the same trade-off: tracing overhead, probes that add latency, sampling that shifts timing. Usually the effect is small. Sometimes it isn’t, and how often to look is a design decision with costs on both sides.

Evidence, then state

Here’s what I’ve taken from a few weeks of squinting at a prism.

Separate what you observed from what you concluded. “The refractometer read 9 Brix” and “fermentation is about two-thirds done” are different kinds of statement. They belong in different places, and they should be allowed to disagree.

Keep the raw reading. Corrections, normalizations, and aggregations are interpretations. If you keep only the interpreted value, you can never revisit the interpretation.

Record the conditions that make a reading meaningful. Instrument, baseline, calibration assumptions, the believed state of the system. A metric without its context is a number waiting to be misread.

Treat state as a derived claim. “Fermentation is complete” isn’t a reading. It’s a conclusion drawn from several readings over time, plus whatever evidence rules out “stuck,” and it should be traceable back to all of it.

Notice when your proxy stops being a proxy. Every metric was calibrated for some range of system behavior. When the system leaves that range, the metric keeps reporting with exactly the same confidence.

The agent in July was right about one thing: it shouldn’t matter whether a number comes from a clipboard or a five-thousand-dollar probe. Where it was wrong was in believing the number could travel without its history.

The pear wine is still in primary as I write this. The raw refractometer reading says it has a long way to go. The corrected number says otherwise, and the corrected number is only trustworthy because I wrote down what the juice read before the yeast went in.

The number on the instrument isn’t the system. It never was.

Facebooktwitterredditlinkedinmail