为会腐烂的事物设计软件
Designing software for things that rot

原始链接: https://drobinin.com/posts/designing-software-for-things-that-rot/

## 从发霉恐慌到发酵基础设施 十年的家庭发酵实践——从酸菜到萨拉米——让我意识到,将发酵视为烹饪是一种错误。它需要被视为基础设施,需要仔细监控和控制。最初的肉类腌制实验突显了风险,特别是湿度波动和潜在的危险霉菌,促使我转向配备温度和湿度控制器的控制室。 关键的挑战不是*测量*条件,而是理解这些测量结果的*含义*。受到食品行业HACCP计划(危害分析与关键控制点)的启发,我开发了一款名为“Fermento”的应用程序,用于跟踪发酵过程,识别潜在危害,并定义关键限制。 Fermento 作为一个动态决策树运作,适应每个发酵阶段并优先考虑相关的检查。它强调可追溯性而非精确性,重视记录*实际*发生的情况,而不是追求完美条件。虽然自动化是一个目标,但我认为手动数据输入很有价值,认识到发酵是关于受控变化,而不是防止变化。最终,Fermento 旨在提供安心——以及为质疑自制美食安全的邻居提供可靠的保障。

## 软件设计与腐烂:摘要 一位软件工程师详细介绍了他们深入研究如何精细控制萨拉米香肠和科帕肉等肉类的熟成过程,强调了“为腐烂的事物设计软件”出乎意料的复杂挑战。该项目涉及传感器、自动湿度控制(甚至包括安装冷水管!)、以及数据记录——所有这些都通过Grafana和Prometheus等工具进行监控。 作者强调要优化*变化*而非追求完美,承认这些传统工艺的容错性。他们戏谑地指出将复杂的软件解决方案应用于古老技术的不合理性,但认为数据和控制是有价值的。 这次讨论引发了热烈的讨论,评论者分享了他们自己的自动化项目(例如洗手液工厂!)、酸面包酵母的技巧,以及对工程师致力于这一小众但引人入胜的技术应用的赞赏。一个关键的收获是精度与直觉之间的平衡——知道何时测量,以及何时依靠“人类的舌头”作为传感器。
相关文章

原文

The white mold was good. The green-grey mold was probably fine. The fuzzy black spot was... well, that's when I ended up in a midnight rabbit hole of forum posts debating whether that particular shade of black meant penicillium or something that would send me to hospital. I had photos, I had descriptions, I had twelve different opinions from twelve different strangers on the internet.

What I didn't have, was the confidence. "Is that safe to eat?" my neighbour asked, eyeing the salami hanging in my garage. Fair question: it's raw meat that's been sitting at room temperature for six weeks.

That's when it clicked: I'd been treating fermentation like cooking when I should have been treating it like infrastructure. But how did I end up here in the first place?

Half of the first batch hanged Italian-style on the ceiling didn't survive Scottish humidity and got a few spots looking too similar to zygomycetes fungi, at which point I got too scared and built a proper chamber.

First, there was hardware

I started fermenting almost a decade ago, before sourdough starters became everyone's pandemic tamagotchi. You know the drill: shy attempts at sauerkraut, then mozzarella, then progressively weirder things and Sandor Katz books on the shelves. Tracking went from paper scraps to text files to an Obsidian vault. None of it helped because the problem wasn't tracking, it was knowing what to track and when it mattered.

Things escalated when I moved into a detached house–the kind where neighbors won't hear you checking on your sausage at 2 AM (which, reading that back, sounds worse than it is).

I bought a £150 larder fridge on eBay, added two Inkbird controllers, a reptile mat left after koji experiments, and went on a quest for a dehumidifier that resumes automatically. Most need a button press, which is useless for automation, and for some reason sellers never mention it in descriptions; cue exciting returns and very odd Amazon ads I keep getting three years later.

Diagram of the fermentation chamber wiring with fridge, humidifier, dehumidifier, and Inkbird controllers.

Temperature was easy. Humidity, not so much. Without a humidifier, the dehumidifier would overshoot and stall around ~75% RH, the case-hardening sweet spot where you build a meat Matryoshka you can't trust. The fix was bidirectional control: add moisture gently below target, remove gently above. Haven't had a case-hardening scare since.


Then, came the dashboard

I already had Home Assistant running, so getting temperature and humidity from the chamber into a snippet took an evening. But I didn't have a way to know if what I was seeing was actually a problem.

Home Assistant graph showing chamber humidity stepping down over time.
DevOps for guanciale.

Let's say, you're curing coppa and on day 12 humidity drops to 68% for six hours because you forgot to refill the reservoir. Is that fine? A disaster? The answer depends on cure phase, temperature, weight loss, and a dozen other variables. It's context, not a threshold.

Most of my "monitoring" was staring at graphs and trying to remember forum posts from three years ago. I had observability but no intelligence.

I read once: if you can measure it, you can manage it. But often a measurement without context is just noise.


What could go wrong?

The wake-up call came with 'nduja–spreadable salami, heavy on fat and chillies, delicious, and risky because of moisture content and long fermentation. Reading through the process I found someone mentioning HACCP plans: Hazard Analysis and Critical Control Points, the food industry framework for identifying what can go wrong and how to monitor it.

I'd assumed it was bureacracy paperwork. Turns out it's an elegant decision tree. Enumerate every hazard, figure out which you can control, define critical limits, document monitoring.

For 'nduja: biological hazard is Salmonella, Listeria, Clostridium. Control point is pH drop. Critical limit is pH 5.3 within 48 hours. Monitor at 24h and 48h. Corrective action: if pH > 5.3 at 48h, discard.

Obviously kraut, kimchi, quick pickles are forgiving: they'll look and smell wrong if they fail. Things change with low-acid, low-oxygen, long cures of meat and things like garlic-in-oil, where something poisonous won't have off-smells or flavours. HACCP isn't paperwork; it's how you don't roll dice you can't see.


Building the decision tree

Naturally, as an iOS developer I built a tracker. I started with projects and logs, then added "fermentation profiles" with hazards, limits, and check schedules.

The interesting bit was context: 65% humidity might be fine on day 30 but a problem on day 5, so the app has to know where you are in the process. Each fermentation profile is basically a small state machine where phases progress in order (ferment → dry → age), and each phase carries its own set of constraints that matter during that window.

struct Constraint {
let metric: Metric
let range: ClosedRange<Double>
let window: Interval
let severity: Severity
let action: () -> Void
}

struct Phase {
let name: String
let constraints: [Constraint]
let nextPhase: Phase?
let transition: Transition
}

The first version checked every constraint on every measurement, which was fine for a dozen control points but felt wasteful. The fix was simple: when a batch enters a new phase, grab just the constraints relevant to that phase and cache them. Most phases only care about three to five things–during fermentation you're watching pH and temperature, during drying it's relative humidity and weight loss, during aging it's mostly "is anything growing that shouldn't be". So when a new measurement arrives you only validate against the cached constraints for the current phase.

Some checks are time-bound ("verify pH by 48 hours" for 'nduja), so I keep those in a sorted list of due tasks. Nothing fancy, just enough to know what needs checking next without scanning everything.

LeetCode youth finally paid off: turns out all those "rebalance a binary search tree" problems were preparing me for salami, not FAANG interviews.


Automating compliance

Once checks worked, I figured I might as well generate the actual HACCP document, so the next time a neighbour suspeciously eyes me up and asks if the homemade Roquefort is safe to eat, I can pull up a binder that wouldn't look out of place in a Michelin kitchen.

Take the profile, enumerate hazards, build the control table, export as PDF. The useful bit was making it bidirectional–if you're logging anyway, the document fills itself. pH 5.2 at 48 hours? CCP-1 verified. Temperature excursion? Flagged with corrective note.

Could I have added blockchain for tamper-proofing? Sure. Am I going to give people that idea? Absolutely not.

A lot of tools in this space are beautifully precise and wonderfully narrow–great if you only brew beer, or only bake bread. I don't. I swing from grashopper garum to deer prosciutto to cedar mugolio, because you live only once and also have you ever tried to find a grasshopper garum in a corner shop? What I needed wasn't a tighter ruler; it was a truer diary.


Traceability over precision

In the world where most apps chase precision, I only wanted traceability: a record of what really happened, not what should have.

HACCP matters for edge cases: long windows, low-acid environments, meat–anywhere botulism is theoretically on the table. Not everything I ferment needs this rigour. For the rest, just keep an honest log: what did I do, what did I measure, what did I observe.

I've got batches where the note says "forgot to log pH, but it smelled right." That's data. Photos often help more than numbers. "Slightly more sour than batch #3" beats pH to three decimals.

Fermento detail preview with cheese tasting notes

The manual reality

Chamber monitoring runs in Home Assistant automatically. But the tracker doesn't talk to Home Assistant (yet?), so when I weigh a salami or measure pH I'm typing it in.

Dream setup: weigh the piece, measurement auto-logs, gets checked, flags me if the curve is wrong. But I don't have smart scales that talk to my phone, and even if I did I'd need to solve "which piece is on the scale". Barcode tags? RFID? A Vapor server with Apple's new Foundation models and VisionKit to run real-time OCR?

It might sound odd from someone who just spent a weekend reverse-engineering PureGym's API because I couldn't be bothered to remember an 8-digit PIN. But typing six numbers once a week? That's fine. Apparently my threshold is somewhere between "weekly number entry" and "daily PIN recall".


Designing for things that rot

Web services are designed around uptime and consistency, i.e deterministic behaviour, same in test as production. Fermentation is the opposite: controlled drift where things change slowly within acceptable boundaries. Your job isn't to prevent change, it's to make sure it happens in the right direction at roughly the right speed.

It's all about watching trends, not absolute values. A 2°C spike for ten minutes is irrelevant, but a 0.5°C drift over a week matters. And you can't rollback. Once you've over-salted or let it dry too fast, that's it.

Humidity graph showing a slow three-day descent after loading a fresh batch into the chamber.
Humidity spikes when a new batch goes into the chamber, and bringing it back takes time.

Every batch teaches you something, every deviation is data. I've got batches going back years: elderflower capers at ~7% brine (lower went mushy), ättika penny buns where blanch-then-brine kept their snap, dried barley koji where airflow beat temperature, duck prosciutto I ignored for ten days that still behaved, and the blue that didn't–because I didn't sanitise the box. All logged, all useful.

Designing software for things that rot means optimising for variance, memory, and timing–not perfection. It turns out the hardest part of software isn't keeping things alive. It's knowing when to let them age.


Smaller scale, same habits

These days I'm settling in Canada with most of the gear in storage across the pond. No chamber, no controllers, no midnight humidity checks–just a jar of pinecones buried in sugar slowly turning into mugolio, some freshly picked BC chanterelles in a quick brine, my wife's sourdough starter living on the counter and getting fed when she remembers. The app reminds her; she ignores the app; the starter survives anyway because sourdough is remarkably forgiving.

Different scale, same principles: note what you did, watch what changes, let time do its thing. Turns out you don't need the chamber to benefit from the habits.

I ended up calling the tracker Fermento. It keeps track of salt percentages, culture lineages, HACCP plans. Mostly it just tries not to get in your way.

Most of what people ferment at home doesn't need HACCP plans–kombucha and pickles will tell you loudly if they've gone wrong. For those, Fermento is just a diary with reminders. But when you're doing long cures or low-acid environments where bad outcomes are silent, having the decision tree makes the difference. The app scales to what you're making.

If you've read this far, you're probably my kind of person. The app is here. It's free for basic tracking and HACCP generation is in the commercial tier .

The best incident reports end with dinner. May yours end with something cured, crunchy, or quietly fizzing.


Got weird hardware and weirder requirements? I make unreasonable things work on iOS. [email protected]

联系我们 contact @ memedata.com