<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Error Theory on ErrorZap</title><link>https://errorzap.com/categories/error-theory/</link><description>Recent content in Error Theory on ErrorZap</description><image><title>ErrorZap</title><url>https://errorzap.com/og.png</url><link>https://errorzap.com/og.png</link></image><generator>Hugo</generator><language>en-US</language><copyright>ErrorZap</copyright><lastBuildDate>Mon, 09 Nov 2026 09:00:00 -0700</lastBuildDate><atom:link href="https://errorzap.com/categories/error-theory/index.xml" rel="self" type="application/rss+xml"/><item><title>Quiet Alerts Are an Engineering Product</title><link>https://errorzap.com/posts/quiet-alerts-are-an-engineering-product/</link><pubDate>Mon, 09 Nov 2026 09:00:00 -0700</pubDate><guid>https://errorzap.com/posts/quiet-alerts-are-an-engineering-product/</guid><description>A system sent hundreds of versions of the same warning and proved that notification volume is not the same thing as safety.</description><content:encoded><![CDATA[<p>One device went offline and the system told me hundreds of times.</p>
<p>The first message might have been useful. The next several confirmed persistence. After that, the alerting system was no longer describing an incident. It was creating one.</p>
<p>This is how notification stacks fail: every producer believes its event deserves direct access to a human. The camera emails. The cloud pushes. The automation sends a second push. A monitoring job creates a task. Another agent posts a completion notice about the task that summarized the notifications.</p>
<p>The system becomes extremely observable and completely unreadable.</p>
<h2 id="events-are-not-incidents">Events are not incidents</h2>
<p>A device can emit dozens of events during one outage:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>offline
</span></span><span style="display:flex;"><span>still offline
</span></span><span style="display:flex;"><span>retry failed
</span></span><span style="display:flex;"><span>still offline
</span></span><span style="display:flex;"><span>cloud disconnected
</span></span><span style="display:flex;"><span>local monitor missed ping
</span></span><span style="display:flex;"><span>still offline
</span></span><span style="display:flex;"><span>online
</span></span></code></pre></div><p>A human needs two messages: one incident and one recovery.</p>
<p>The missing component is correlation. Group events by source, site, device, and failure type. Require persistence before escalation. Suppress unchanged repeats. Promote only when severity increases. Close the incident after a meaningful recovery window.</p>
<p>That logic is not cosmetic filtering. It is the alert product.</p>
<h2 id="severity-is-a-delivery-decision">Severity is a delivery decision</h2>
<p>Every alert needs a lane.</p>
<ul>
<li><strong>Page now:</strong> immediate danger, active compromise, total outage, or lost protection.</li>
<li><strong>Needs action:</strong> important but not seconds-sensitive; send once and wait.</li>
<li><strong>Digest:</strong> routine warnings, brief flaps, completed background work.</li>
<li><strong>Log only:</strong> searchable evidence that should not interrupt anyone.</li>
</ul>
<p>If every source chooses &ldquo;page now,&rdquo; the actual emergency competes with maintenance noise and status chatter.</p>
<p>Critical paging should remain deterministic. An AI can group, summarize, and rank lower-severity material, but smoke, panic, or hard protection loss should not depend on a model deciding whether the prose sounds urgent.</p>
<h2 id="recovery-deserves-one-message">Recovery deserves one message</h2>
<p>Bad alerting systems are obsessed with failure and vague about recovery.</p>
<p>They send twenty warnings, then either send twenty more &ldquo;online&rdquo; messages or quietly stop. A useful incident has a lifecycle:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>suspected -&gt; confirmed -&gt; acknowledged -&gt; recovering -&gt; resolved
</span></span></code></pre></div><p>The recovery message should say what recovered, how long it was affected, and whether any residual check remains. That is closure, not celebration.</p>
<h2 id="silence-has-to-be-proven-safe">Silence has to be proven safe</h2>
<p>Quieting a system can hide problems if the suppression rules are sloppy. A paused parent monitor may silence all its children. A failed notification route can look identical to a quiet day. A digest job can die and leave no evidence.</p>
<p>So the alert system itself needs tests:</p>
<ul>
<li>inject a synthetic event into each severity lane;</li>
<li>prove the intended destination receives it;</li>
<li>prove duplicate events collapse;</li>
<li>prove escalation occurs when state worsens;</li>
<li>prove one recovery arrives;</li>
<li>prove suppressed events remain searchable;</li>
<li>prove the broker reports its own failure through an independent path.</li>
</ul>
<p>Quiet is valuable only when it comes from correlation, not disconnection.</p>
<p>The best alert stack is not the loudest guardian. It is the one that earns the right to interrupt you.</p>
]]></content:encoded></item><item><title>Proof, Not Green Lights</title><link>https://errorzap.com/posts/proof-not-green-lights/</link><pubDate>Sat, 07 Nov 2026 09:00:00 -0700</pubDate><guid>https://errorzap.com/posts/proof-not-green-lights/</guid><description>APIs, queues, processes, builds, and dashboards kept telling me yes to questions I had not actually asked.</description><content:encoded><![CDATA[<p>Green is a dangerous color in software.</p>
<p>The API returns 200. The remote command says accepted. The print queue empties. The service process is running. The backup job completes. The build passes. Every dashboard is green, and the thing a person cares about still did not happen.</p>
<p>This is not because the indicators are fake. It is because they answer smaller questions.</p>
<h2 id="every-layer-proves-itself">Every layer proves itself</h2>
<p>Consider a remote print job:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>request accepted -&gt; job queued -&gt; spooler released -&gt; printer received -&gt; paper emerged
</span></span></code></pre></div><p>An empty queue proves the spooler no longer owns the job. It does not prove the printer had paper, the label stock was loaded correctly, the output was legible, or the page landed in the expected room.</p>
<p>The same collapse happens everywhere:</p>
<ul>
<li>a backup job proves data was written, not that restore works;</li>
<li>a process proves code is running, not that the service is useful;</li>
<li>a deployment proves bytes reached a target, not that traffic reaches them;</li>
<li>a SIP phone rings, but the human may hear nothing;</li>
<li>a smart light reports <code>on</code> at one-percent brightness and appears dark.</li>
</ul>
<p>The green light belongs to one layer.</p>
<h2 id="ask-what-a-human-would-observe">Ask what a human would observe</h2>
<p>The simplest way to improve acceptance tests is to finish this sentence:</p>
<blockquote>
<p>If this worked, a person at the destination would observe ______.</p>
</blockquote>
<p>Then design the test around that observation.</p>
<p>For a website: the new page loads from the public URL on phone and desktop, with the expected content and assets.</p>
<p>For audio: two humans hear each other in both directions.</p>
<p>For a restore: the isolated copy mounts, starts when authorized, serves expected data, and can be failed back without contaminating production.</p>
<p>For an alert: the intended person receives one actionable notification and one recovery—not merely an API receipt.</p>
<h2 id="keep-the-intermediate-lights">Keep the intermediate lights</h2>
<p>Intermediate signals are valuable. They locate failures.</p>
<p>If a print request was never accepted, do not troubleshoot the printer tray. If the process is dead, do not start with browser caching. Layered evidence is how we move from vague failure to exact boundary.</p>
<p>The mistake is promoting an intermediate success into a final claim.</p>
<p>A useful acceptance ledger looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>prepared: artifact exists
</span></span><span style="display:flex;"><span>acted: delivery attempted
</span></span><span style="display:flex;"><span>accepted: target system acknowledged
</span></span><span style="display:flex;"><span>verified: target state read back
</span></span><span style="display:flex;"><span>observed: user-visible outcome confirmed
</span></span></code></pre></div><p>Not every task requires all five. The labels prevent us from pretending it did.</p>
<h2 id="honest-partials-are-faster">Honest partials are faster</h2>
<p>There is a strange pressure in technical work to declare victory at the first green light. It feels efficient. In reality, it creates reopened work, confusing handoffs, and repeated troubleshooting.</p>
<p>&ldquo;The print job left the queue; physical output is unverified&rdquo; is more useful than &ldquo;printed.&rdquo;</p>
<p>&ldquo;The service is active and its health endpoint is ready; no real user flow was tested&rdquo; is a complete operational statement.</p>
<p>Precision is not pessimism. It tells the next person exactly where truth ends.</p>
<p>Green lights are clues. Proof is the outcome that survives the final mile.</p>
]]></content:encoded></item><item><title>Bad Rows Go to Quarantine</title><link>https://errorzap.com/posts/bad-rows-go-to-quarantine/</link><pubDate>Tue, 03 Nov 2026 09:00:00 -0700</pubDate><guid>https://errorzap.com/posts/bad-rows-go-to-quarantine/</guid><description>One malformed record should become one visible exception, not a reason to poison or stop an entire daily import.</description><content:encoded><![CDATA[<p>Batch jobs have a talent for turning one weird row into a full-day emergency.</p>
<p>The source sends a date that does not exist. A business name contains a character nobody tested. One detail page returns half an HTML document. A record claims it was updated in the year 2099. The importer reaches that row, throws an exception, and leaves the whole dataset stuck at yesterday.</p>
<p>The usual reaction is to make the parser more forgiving. That can be worse. A parser that accepts everything eventually turns corruption into official data.</p>
<p>The better answer is quarantine.</p>
<h2 id="a-bad-record-is-not-a-bad-run">A bad record is not a bad run</h2>
<p>A robust importer treats records as individual claims.</p>
<p>Each claim passes through structural, semantic, and consistency checks. Good records continue. Suspicious records move to a separate holding area with the reason they were rejected.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>record 4172
</span></span><span style="display:flex;"><span>  identifier: present
</span></span><span style="display:flex;"><span>  date: 2099-31-44       X invalid
</span></span><span style="display:flex;"><span>  required fields: pass
</span></span><span style="display:flex;"><span>  action: quarantine
</span></span><span style="display:flex;"><span>  reason: impossible date
</span></span></code></pre></div><p>The run can finish with 7,999 accepted records and one quarantined record. That is not a hidden partial success. It is an explicit result with a number, a reason, and something a human can inspect.</p>
<h2 id="quarantine-is-not-the-trash">Quarantine is not the trash</h2>
<p>Rejected records must remain recoverable. The point is to prevent them from contaminating the public dataset while preserving enough evidence to repair the rule or correct the source later.</p>
<p>A useful quarantine entry includes:</p>
<ul>
<li>source identifier and source URL when safe;</li>
<li>retrieval time;</li>
<li>validation rule that failed;</li>
<li>bounded copy or hash of the raw input;</li>
<li>parser version;</li>
<li>whether the record existed in the previous dataset;</li>
<li>review status and resolution.</li>
</ul>
<p>Do not dump an entire credential-bearing response into a log. Quarantine should preserve evidence, not create a second security incident.</p>
<h2 id="the-totals-must-add-up">The totals must add up</h2>
<p>The fastest way to lose trust in a pipeline is to make records disappear between counters.</p>
<p>Every run should satisfy a reconciliation equation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>retrieved = accepted + unchanged + quarantined + explicitly skipped
</span></span></code></pre></div><p>If it does not, the run is incomplete.</p>
<p>That equation sounds obvious. It is also where many pipelines fail. They log &ldquo;processed 8,000&rdquo; at the start, &ldquo;saved 7,997&rdquo; at the end, and never explain the missing three.</p>
<p>Thin logs turn those three records into ghosts. A complete ledger turns them into work.</p>
<h2 id="tomorrow-should-not-inherit-todays-wedge">Tomorrow should not inherit today&rsquo;s wedge</h2>
<p>Quarantine also protects future runs.</p>
<p>If one malformed row stays in the upstream source for a week, a crash-on-first-error collector will fail seven days in a row. A quarantine-aware collector will flag the same record, deduplicate the alert, and continue collecting everything else. When the source fixes it, the row can re-enter through the normal validation path.</p>
<p>The operational difference is enormous. One is a brittle daily ceremony. The other is a system that can live in the real world.</p>
<p>Bad rows are inevitable. Silent rows are dangerous. Quarantined rows are manageable.</p>
]]></content:encoded></item></channel></rss>