<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Juno Vale</title>
<subtitle>Notes on the BEAM, backpressure, and the long tail of distributed systems.</subtitle>
<link href="https://cherrybomb.dev/t/topo/"/>
<link rel="self" href="https://cherrybomb.dev/t/topo/feed.xml"/>
<id>https://cherrybomb.dev/t/topo/</id>
<updated>2026-05-06T00:00:00Z</updated>
<author><name>Juno Vale</name></author>
<entry>
<title>A Year of Property Testing</title>
<link href="https://cherrybomb.dev/t/topo/a-year-of-property-testing/"/>
<id>https://cherrybomb.dev/t/topo/a-year-of-property-testing/</id>
<published>2026-05-06T00:00:00Z</published>
<updated>2026-05-06T00:00:00Z</updated>
<summary>What a year of stream_data actually caught, and the properties that turned out to be worth writing.</summary>
<content type="html">&lt;p&gt;A year ago I put property tests into a codebase that already had good
example-based coverage, mostly to find out whether the enthusiasm was
warranted. Here is what they actually caught.&lt;/p&gt;
&lt;h2 id="the-score"&gt;The score&lt;a href="#the-score" aria-label="Link to heading 'The score'" data-heading-content="The score" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Eleven genuine defects. Nine of them were in code that parsed, normalised, or
merged data — the boring seams between systems. Two were in domain logic. None
were in the places I expected.&lt;/p&gt;
&lt;p&gt;The single most productive property was also the dullest:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-function-call"&gt;property&lt;/span&gt; &lt;span class="l-string"&gt;&amp;quot;a document survives a round trip&amp;quot;&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;  &lt;span class="l-function-call"&gt;check&lt;/span&gt; &lt;span class="l-function-call"&gt;all&lt;/span&gt; &lt;span class="l-variable"&gt;document&lt;/span&gt; &lt;span class="l-operator"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="l-function-call"&gt;document_generator&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;    &lt;span class="l-function-call"&gt;assert&lt;/span&gt; &lt;span class="l-variable"&gt;document&lt;/span&gt; &lt;span class="l-operator"&gt;==&lt;/span&gt; &lt;span class="l-variable"&gt;document&lt;/span&gt; &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Codec&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;encode&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Codec&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;decode!&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;  &lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="5"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;That one found four bugs on its own: an empty tag list that came back as &lt;code&gt;nil&lt;/code&gt;,
a timestamp that lost microseconds, a nested map whose key order the encoder
depended on, and a Unicode normalisation difference that only appeared for
strings containing combining characters.&lt;/p&gt;
&lt;p&gt;Every one of those would have been possible to write as an example test. The
point is that nobody did, for years, because nobody thought of them.&lt;/p&gt;
&lt;h2 id="the-properties-worth-writing"&gt;The properties worth writing&lt;a href="#the-properties-worth-writing" aria-label="Link to heading 'The properties worth writing'" data-heading-content="The properties worth writing" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Four shapes covered nearly everything useful:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Round trips.&lt;/strong&gt; &lt;code&gt;decode(encode(x)) == x&lt;/code&gt;. Cheap, and it finds encoder bugs
example tests never reach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Invariants.&lt;/strong&gt; Something that must be true of every output, regardless of
input. Ours: a stock projection never disagrees with a fold over the same
events.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Oracles.&lt;/strong&gt; A second, slower, obviously correct implementation to compare
against. We had a naive O(n²) deduplicator; the fast one had a bug at exactly
the block boundary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Idempotence.&lt;/strong&gt; &lt;code&gt;f(f(x)) == f(x)&lt;/code&gt; for anything that normalises. This found a
sanitiser that stripped one layer of nesting per call.&lt;/p&gt;
&lt;h2 id="where-they-were-not-worth-it"&gt;Where they were not worth it&lt;a href="#where-they-were-not-worth-it" aria-label="Link to heading 'Where they were not worth it'" data-heading-content="Where they were not worth it" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I wrote properties for our HTTP handlers and deleted them a month later. The
generators were more code than the handlers, most of the interesting behaviour
was in the dependencies, and every failure took twenty minutes to interpret.
Example tests read better there and caught the same things.&lt;/p&gt;
&lt;p&gt;I also stopped trying to shrink my way to understanding. &lt;code&gt;stream_data&lt;/code&gt;'s
shrinking is good, but a minimal counterexample for a complex generator is
still a puzzle. Now, when a property fails, my first move is to write the
shrunk case down as an ordinary test with a name. The property found it; the
example test explains it.&lt;/p&gt;
&lt;p&gt;That split — properties to search, examples to document — is the habit I would
keep if I had to give up everything else from this year.&lt;/p&gt;</content>
</entry>
<entry>
<title>Reading a Supervision Tree Like a Map</title>
<link href="https://cherrybomb.dev/t/topo/reading-a-supervision-tree-like-a-map/"/>
<id>https://cherrybomb.dev/t/topo/reading-a-supervision-tree-like-a-map/</id>
<published>2026-01-20T00:00:00Z</published>
<updated>2026-01-20T00:00:00Z</updated>
<summary>A supervision tree is a diagram of what your system believes about failure. Most outages are places where that belief was wrong.</summary>
<content type="html">&lt;p&gt;When I join a BEAM codebase, the first file I open is &lt;code&gt;application.ex&lt;/code&gt;. Not the
router, not the schema — the supervision tree. It is the only place where the
system states, in code, what it thinks should happen when things break.&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-keyword-function"&gt;def&lt;/span&gt; &lt;span class="l-function"&gt;start&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-comment"&gt;_type&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-comment"&gt;_args&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;  &lt;span class="l-variable"&gt;children&lt;/span&gt; &lt;span class="l-operator"&gt;=&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;    &lt;span class="l-module"&gt;Ledgerbeam.Repo&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;    &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-module"&gt;Phoenix.PubSub&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;name: &lt;/span&gt;&lt;span class="l-module"&gt;Ledgerbeam.PubSub&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="5"&gt;    &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-module"&gt;Finch&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;name: &lt;/span&gt;&lt;span class="l-module"&gt;Ledgerbeam.HTTP&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="6"&gt;    &lt;span class="l-module"&gt;Ledgerbeam.Inventory.Supervisor&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="7"&gt;    &lt;span class="l-module"&gt;Ledgerbeam.Ingest.Pipeline&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="8"&gt;    &lt;span class="l-module"&gt;LedgerbeamWeb.Endpoint&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="9"&gt;  &lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="10"&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="11"&gt;  &lt;span class="l-module"&gt;Supervisor&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;start_link&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;children&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;strategy: &lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:one_for_one&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;name: &lt;/span&gt;&lt;span class="l-module"&gt;Ledgerbeam.Supervisor&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="12"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Six lines, and each one is a claim.&lt;/p&gt;
&lt;h2 id="what-the-order-tells-you"&gt;What the order tells you&lt;a href="#what-the-order-tells-you" aria-label="Link to heading 'What the order tells you'" data-heading-content="What the order tells you" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Children start in order and stop in reverse. So this tree claims the endpoint
should be the last thing up and the first thing down — traffic only arrives
once the repo, the pipeline, and the domain supervisors are ready, and traffic
stops before they go away.&lt;/p&gt;
&lt;p&gt;That is almost always what you want, and it is worth checking, because the
failure mode is subtle: put the endpoint too early and you accept requests
against a half-built system for a few hundred milliseconds on every deploy.&lt;/p&gt;
&lt;h2 id="what-the-strategy-tells-you"&gt;What the strategy tells you&lt;a href="#what-the-strategy-tells-you" aria-label="Link to heading 'What the strategy tells you'" data-heading-content="What the strategy tells you" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;:one_for_one&lt;/code&gt; says these six things are independent — if the ingest pipeline
dies, the endpoint should carry on. Is that true? For us it was: the site stays
up and serves reads while ingestion restarts.&lt;/p&gt;
&lt;p&gt;But look for a &lt;code&gt;:one_for_all&lt;/code&gt; and ask what it is protecting. It says the
children share state that cannot survive one of them restarting, which is a
real thing, and also a design smell worth a second look.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:rest_for_one&lt;/code&gt; is the interesting one: it says there is a dependency order
here, and everything after this child needs it. That is a map of your real
coupling, drawn by someone who had to think about it.&lt;/p&gt;
&lt;h2 id="where-the-tree-lies"&gt;Where the tree lies&lt;a href="#where-the-tree-lies" aria-label="Link to heading 'Where the tree lies'" data-heading-content="Where the tree lies" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The tree is a set of beliefs, and outages are where the beliefs were wrong. Two
that catch people:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A process that restarts fine but reconnects slowly.&lt;/strong&gt; The supervisor's job is
done in microseconds; the connection pool underneath takes eight seconds. The
tree looks healthy and the system is not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;restart: :temporary&lt;/code&gt; on something that matters.&lt;/strong&gt; It will not come back, ever,
and the supervisor will not tell you. This is correct for a one-shot task and
quietly catastrophic on a consumer.&lt;/p&gt;
&lt;p&gt;Neither shows up in the diagram. Both show up in &lt;code&gt;:observer&lt;/code&gt;, and in the first
five minutes of an incident, which is why I read the tree before I read
anything else.&lt;/p&gt;</content>
</entry>
<entry>
<title>The Kata I Keep Coming Back To</title>
<link href="https://cherrybomb.dev/t/topo/the-kata-i-keep-coming-back-to/"/>
<id>https://cherrybomb.dev/t/topo/the-kata-i-keep-coming-back-to/</id>
<published>2025-10-15T00:00:00Z</published>
<updated>2025-10-15T00:00:00Z</updated>
<summary>Parsing dates from strings looks trivial for about four minutes, which is what makes it a good exercise.</summary>
<content type="html">&lt;p&gt;Every so often I redo the string-date-parsing kata. Not because I have
forgotten how to parse a date, but because it is the shortest exercise I know
that punishes vagueness.&lt;/p&gt;
&lt;p&gt;The brief: given a string, return a date. Start with &lt;code&gt;&amp;quot;2025-10-15&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-keyword-function"&gt;def&lt;/span&gt; &lt;span class="l-function"&gt;parse&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;input&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;do: &lt;/span&gt;&lt;span class="l-module"&gt;Date&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;from_iso8601&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;input&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Four minutes in, you are done. Then the examples arrive.&lt;/p&gt;
&lt;h2 id="the-examples"&gt;The examples&lt;a href="#the-examples" aria-label="Link to heading 'The examples'" data-heading-content="The examples" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-plaintext" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&amp;quot;2025-10-15&amp;quot;     the easy one
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;&amp;quot;15/10/2025&amp;quot;     day first
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;&amp;quot;10/15/2025&amp;quot;     month first
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;&amp;quot;15 Oct 2025&amp;quot;    names, abbreviated
&lt;/div&gt;&lt;div class="l-line" data-line="5"&gt;&amp;quot;October 15th&amp;quot;   no year, ordinal suffix
&lt;/div&gt;&lt;div class="l-line" data-line="6"&gt;&amp;quot;yesterday&amp;quot;      relative to what?
&lt;/div&gt;&lt;div class="l-line" data-line="7"&gt;&amp;quot;&amp;quot; and nil       the caller has a bug
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing here is hard. What is hard is that &lt;code&gt;&amp;quot;01/02/2025&amp;quot;&lt;/code&gt; is genuinely
ambiguous, and you cannot resolve it inside the parser. It is January 2nd or
February 1st depending on a fact the string does not contain.&lt;/p&gt;
&lt;p&gt;The kata's real lesson is that this is where the design happens. Every attempt
I make eventually arrives at the same shape: the ambiguity becomes a parameter.&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-constant"&gt;@&lt;/span&gt;&lt;span class="l-constant"&gt;type &lt;/span&gt;&lt;span class="l-variable"&gt;locale_order&lt;/span&gt; &lt;span class="l-operator"&gt;::&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:day_first&lt;/span&gt; &lt;span class="l-operator"&gt;|&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:month_first&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;&lt;span class="l-constant"&gt;@&lt;/span&gt;&lt;span class="l-constant"&gt;spec &lt;/span&gt;&lt;span class="l-function-call"&gt;parse&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-module"&gt;String&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;t&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-function-call"&gt;keyword&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-operator"&gt;::&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:ok&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-module"&gt;Date&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;t&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt; &lt;span class="l-operator"&gt;|&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:error&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-function-call"&gt;term&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;&lt;span class="l-keyword-function"&gt;def&lt;/span&gt; &lt;span class="l-function"&gt;parse&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;input&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;opts&lt;/span&gt; &lt;span class="l-operator"&gt;\\&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="5"&gt;  &lt;span class="l-variable"&gt;order&lt;/span&gt; &lt;span class="l-operator"&gt;=&lt;/span&gt; &lt;span class="l-module"&gt;Keyword&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;get&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;opts&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:order&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:day_first&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="6"&gt;  &lt;span class="l-variable"&gt;today&lt;/span&gt; &lt;span class="l-operator"&gt;=&lt;/span&gt; &lt;span class="l-module"&gt;Keyword&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;get&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;opts&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:today&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-module"&gt;Date&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;utc_today&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="7"&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="8"&gt;  &lt;span class="l-variable"&gt;input&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="9"&gt;  &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;String&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;trim&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="10"&gt;  &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-function-call"&gt;attempt&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-function"&gt;iso&lt;/span&gt;&lt;span class="l-operator"&gt;/&lt;/span&gt;&lt;span class="l-operator"&gt;1&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-function-call"&gt;numeric&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-operator"&gt;1&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;order&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-function"&gt;named&lt;/span&gt;&lt;span class="l-operator"&gt;/&lt;/span&gt;&lt;span class="l-operator"&gt;1&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-function-call"&gt;relative&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-operator"&gt;1&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;today&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="11"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Two details that took me several attempts to arrive at:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;today&lt;/code&gt; is an argument.&lt;/strong&gt; A parser that reads the clock cannot be tested for
&lt;code&gt;&amp;quot;yesterday&amp;quot;&lt;/code&gt; without either freezing time or accepting flaky tests. Passing the
date in makes the whole thing a pure function, and the tests get boring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The strategies are a list.&lt;/strong&gt; Each one returns &lt;code&gt;{:ok, date}&lt;/code&gt; or &lt;code&gt;:error&lt;/code&gt;, and
&lt;code&gt;attempt/2&lt;/code&gt; walks them in order. Adding a format is adding a function, and the
precedence is visible in one place rather than distributed across a &lt;code&gt;cond&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="why-it-stays-useful"&gt;Why it stays useful&lt;a href="#why-it-stays-useful" aria-label="Link to heading 'Why it stays useful'" data-heading-content="Why it stays useful" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most katas train you to write code. This one trains you to notice that the
requirements are underspecified, and to put the missing decision somewhere the
caller can see it. That skill transfers to almost everything, which is more
than I can say for FizzBuzz.&lt;/p&gt;</content>
</entry>
<entry>
<title>Event Sourcing Taught Me to Delete Code</title>
<link href="https://cherrybomb.dev/t/topo/event-sourcing-taught-me-to-delete-code/"/>
<id>https://cherrybomb.dev/t/topo/event-sourcing-taught-me-to-delete-code/</id>
<published>2025-07-09T00:00:00Z</published>
<updated>2025-07-09T00:00:00Z</updated>
<summary>The best thing event sourcing did for our codebase was make a whole category of defensive code obviously unnecessary.</summary>
<content type="html">&lt;p&gt;I came to event sourcing expecting an audit log and got something more useful:
a reason to delete about a third of the inventory service.&lt;/p&gt;
&lt;h2 id="the-code-that-went-away"&gt;The code that went away&lt;a href="#the-code-that-went-away" aria-label="Link to heading 'The code that went away'" data-heading-content="The code that went away" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The old service had a table of stock levels and a great deal of code protecting
it. Compensating updates when a shipment was cancelled. A nightly reconciliation
job. A &lt;code&gt;stock_adjustments&lt;/code&gt; table nobody could explain, holding rows that existed
only to correct earlier rows.&lt;/p&gt;
&lt;p&gt;All of that was there because the current level was the only thing we stored.
When it went wrong, we had no way to ask &lt;em&gt;how&lt;/em&gt;, so we wrote code to guess.&lt;/p&gt;
&lt;p&gt;Storing decisions instead of state removed the guessing:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-keyword-function"&gt;defmodule&lt;/span&gt; &lt;span class="l-module"&gt;Inventory.Events&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;  &lt;span class="l-keyword-function"&gt;defstruct&lt;/span&gt; &lt;span class="l-module"&gt;StockReceived&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:sku&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:quantity&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:received_at&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:purchase_order&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;  &lt;span class="l-keyword-function"&gt;defstruct&lt;/span&gt; &lt;span class="l-module"&gt;StockPicked&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:sku&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:quantity&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:picked_at&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:order_id&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;  &lt;span class="l-keyword-function"&gt;defstruct&lt;/span&gt; &lt;span class="l-module"&gt;StockWrittenOff&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:sku&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:quantity&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:reason&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:approved_by&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="5"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The level is now a fold:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-keyword-function"&gt;def&lt;/span&gt; &lt;span class="l-function"&gt;level&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;sku&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;  &lt;span class="l-variable"&gt;sku&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;  &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Store&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;stream&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;  &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Enum&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;reduce&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-number"&gt;0&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-keyword"&gt;fn&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="5"&gt;    &lt;span class="l-punctuation-special"&gt;%&lt;/span&gt;&lt;span class="l-module"&gt;StockReceived&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;quantity: &lt;/span&gt;&lt;span class="l-variable"&gt;n&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;level&lt;/span&gt; &lt;span class="l-operator"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="l-variable"&gt;level&lt;/span&gt; &lt;span class="l-operator"&gt;+&lt;/span&gt; &lt;span class="l-variable"&gt;n&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="6"&gt;    &lt;span class="l-punctuation-special"&gt;%&lt;/span&gt;&lt;span class="l-module"&gt;StockPicked&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;quantity: &lt;/span&gt;&lt;span class="l-variable"&gt;n&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;level&lt;/span&gt; &lt;span class="l-operator"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="l-variable"&gt;level&lt;/span&gt; &lt;span class="l-operator"&gt;-&lt;/span&gt; &lt;span class="l-variable"&gt;n&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="7"&gt;    &lt;span class="l-punctuation-special"&gt;%&lt;/span&gt;&lt;span class="l-module"&gt;StockWrittenOff&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;quantity: &lt;/span&gt;&lt;span class="l-variable"&gt;n&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;level&lt;/span&gt; &lt;span class="l-operator"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="l-variable"&gt;level&lt;/span&gt; &lt;span class="l-operator"&gt;-&lt;/span&gt; &lt;span class="l-variable"&gt;n&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="8"&gt;  &lt;span class="l-keyword"&gt;end&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="9"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The reconciliation job had nothing left to reconcile. The adjustments table
became a write-off event with a &lt;code&gt;reason&lt;/code&gt; and an &lt;code&gt;approved_by&lt;/code&gt;, which is what
everyone had wanted from it in the first place.&lt;/p&gt;
&lt;h2 id="what-it-did-not-fix"&gt;What it did not fix&lt;a href="#what-it-did-not-fix" aria-label="Link to heading 'What it did not fix'" data-heading-content="What it did not fix" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It is worth being clear about what event sourcing cost us, because the posts
that skip this part are the ones that get people into trouble.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Versioning is forever.&lt;/strong&gt; The moment an event is persisted, its shape is a
public API. We now write upcasters, and we treat an event struct with the same
care as a database migration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Projections drift.&lt;/strong&gt; Every read model is eventually consistent with the
stream, and every part of the UI that pretended otherwise had to learn to say
&amp;quot;pending&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Streams grow.&lt;/strong&gt; A fold over five years of movements for a fast-moving SKU is
not free. Snapshots solve it, and snapshots are cache invalidation wearing a
hat.&lt;/p&gt;
&lt;p&gt;Those are real costs. I would pay them again for the deletions alone. The
service went from a thing we were afraid to touch on a Friday to one where the
question &amp;quot;how did this SKU get to negative four&amp;quot; has a literal answer.&lt;/p&gt;</content>
</entry>
<entry>
<title>A Rust NIF Without the Fear</title>
<link href="https://cherrybomb.dev/t/topo/a-rust-nif-without-the-fear/"/>
<id>https://cherrybomb.dev/t/topo/a-rust-nif-without-the-fear/</id>
<published>2025-04-22T00:00:00Z</published>
<updated>2025-04-22T00:00:00Z</updated>
<summary>A NIF can take down the whole VM. Here is the small set of habits that keep that from being your problem.</summary>
<content type="html">&lt;p&gt;The received wisdom about NIFs is that they can bring down the entire VM, which
is true, and that you should therefore avoid them, which does not follow.&lt;/p&gt;
&lt;p&gt;We needed SimHash over a few million documents. In pure Elixir it was about
40ms per document, which was not going to work. In Rust, via
&lt;a href="https://github.com/rusterlium/rustler"&gt;Rustler&lt;/a&gt;, it was under a millisecond.
That is the kind of gap worth taking a risk for — provided you know which risks
you are taking.&lt;/p&gt;
&lt;h2 id="rule-one-never-block-a-scheduler"&gt;Rule one: never block a scheduler&lt;a href="#rule-one-never-block-a-scheduler" aria-label="Link to heading 'Rule one: never block a scheduler'" data-heading-content="Rule one: never block a scheduler" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A NIF that runs longer than about a millisecond starves the scheduler it runs
on. The whole preemption model of the BEAM assumes it can take a process off a
core; a native function ignores that assumption entirely.&lt;/p&gt;
&lt;p&gt;Rustler makes the fix a single annotation:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--rust" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;rust&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-rust" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-punctuation-special"&gt;#&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-module"&gt;rustler&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;::&lt;/span&gt;&lt;span class="l-function-macro"&gt;nif&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;schedule&lt;/span&gt; &lt;span class="l-operator"&gt;=&lt;/span&gt; &lt;span class="l-string"&gt;&amp;quot;DirtyCpu&amp;quot;&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;&lt;span class="l-keyword-function"&gt;fn&lt;/span&gt; &lt;span class="l-function"&gt;simhash&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable-parameter"&gt;text&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;:&lt;/span&gt; &lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-type-builtin"&gt;str&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-punctuation-delimiter"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="l-type-builtin"&gt;u64&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;    &lt;span class="l-function-call"&gt;hash_tokens&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-function-call"&gt;tokenize&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;text&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Dirty schedulers exist precisely for work that cannot be preempted. Use them
for anything you have not personally measured under a millisecond, and measure
before you decide you are the exception.&lt;/p&gt;
&lt;h2 id="rule-two-do-not-panic"&gt;Rule two: do not panic&lt;a href="#rule-two-do-not-panic" aria-label="Link to heading 'Rule two: do not panic'" data-heading-content="Rule two: do not panic" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A Rust panic across the NIF boundary takes the VM with it. Every fallible path
must return an error term instead:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--rust" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;rust&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-rust" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-punctuation-special"&gt;#&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-module"&gt;rustler&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;::&lt;/span&gt;&lt;span class="l-function-macro"&gt;nif&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;schedule&lt;/span&gt; &lt;span class="l-operator"&gt;=&lt;/span&gt; &lt;span class="l-string"&gt;&amp;quot;DirtyCpu&amp;quot;&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;&lt;span class="l-keyword-function"&gt;fn&lt;/span&gt; &lt;span class="l-function"&gt;parse&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable-parameter"&gt;input&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;:&lt;/span&gt; &lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-type-builtin"&gt;str&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-punctuation-delimiter"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="l-type"&gt;Result&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;&amp;lt;&lt;/span&gt;&lt;span class="l-type"&gt;Document&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-type"&gt;Error&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;&amp;gt;&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;    &lt;span class="l-module"&gt;serde_json&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;::&lt;/span&gt;&lt;span class="l-function-call"&gt;from_str&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;input&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;map_err&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;|&lt;/span&gt;&lt;span class="l-variable-parameter"&gt;e&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;|&lt;/span&gt; &lt;span class="l-type"&gt;Error&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;::&lt;/span&gt;&lt;span class="l-constant"&gt;Term&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-type"&gt;Box&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;::&lt;/span&gt;&lt;span class="l-function-call"&gt;new&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;e&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;to_string&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Then the Elixir side gets an ordinary tagged tuple, and an ordinary supervisor
handles it:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-keyword"&gt;case&lt;/span&gt; &lt;span class="l-module"&gt;Native&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;parse&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-variable"&gt;payload&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;  &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:ok&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;document&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt; &lt;span class="l-operator"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:ok&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;document&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;  &lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:error&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;reason&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt; &lt;span class="l-operator"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Logger&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;warning&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-string"&gt;&amp;quot;unparseable payload: &lt;/span&gt;&lt;span class="l-string-special"&gt;#{&lt;/span&gt;&lt;span class="l-variable"&gt;reason&lt;/span&gt;&lt;span class="l-string-special"&gt;}&lt;/span&gt;&lt;span class="l-string"&gt;&amp;quot;&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;;&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;:skip&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Deny panics at the crate level and the compiler helps you keep the rule:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--file" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;toml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-toml" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-property"&gt;lints&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;.&lt;/span&gt;&lt;span class="l-property"&gt;clippy&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;&lt;span class="l-property"&gt;unwrap_used&lt;/span&gt; &lt;span class="l-operator"&gt;=&lt;/span&gt; &lt;span class="l-string"&gt;&amp;quot;deny&amp;quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;&lt;span class="l-property"&gt;expect_used&lt;/span&gt; &lt;span class="l-operator"&gt;=&lt;/span&gt; &lt;span class="l-string"&gt;&amp;quot;deny&amp;quot;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id="rule-three-keep-the-boundary-boring"&gt;Rule three: keep the boundary boring&lt;a href="#rule-three-keep-the-boundary-boring" aria-label="Link to heading 'Rule three: keep the boundary boring'" data-heading-content="Rule three: keep the boundary boring" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The NIF should do one computational thing and hold no state. No global
mutable structures, no threads you spawn yourself, no callbacks back into
Elixir. Everything that needs supervision, retries, or lifecycle belongs on the
BEAM side where those things already work.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="/t/topo/images/nif-boundary.svg" alt="Two boxes: the BEAM side holds supervision, retries and state; the Rust side holds one stateless computation"&gt;
&lt;figcaption&gt;The whole architecture, honestly.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;div class="markdown-alert markdown-alert-tip"&gt;
&lt;p class="markdown-alert-title"&gt;Where the fear lives&lt;/p&gt;
&lt;p&gt;Every NIF horror story is a violation of one of the three rules. Check the
story against the rules before checking your code against the story.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Three rules, and the fear mostly goes away. What is left is a normal
engineering tradeoff: a build dependency and a compile step, in exchange for
two orders of magnitude.&lt;/p&gt;</content>
</entry>
<entry>
<title>Backpressure Is a Product Decision</title>
<link href="https://cherrybomb.dev/t/topo/backpressure-is-a-product-decision/"/>
<id>https://cherrybomb.dev/t/topo/backpressure-is-a-product-decision/</id>
<published>2025-02-11T00:00:00Z</published>
<updated>2025-02-11T00:00:00Z</updated>
<summary>Every queue that fills up is answering a question somebody should have answered on purpose.</summary>
<content type="html">&lt;p&gt;We spent a week tuning a Broadway pipeline that kept falling behind, and the
fix turned out not to be a number at all.&lt;/p&gt;
&lt;p&gt;The pipeline ingested webhook deliveries from a payments provider. Under normal
load it kept up comfortably. During a merchant's flash sale it did not, and the
queue depth climbed until the delivery provider started retrying, which added
load, which climbed the queue further.&lt;/p&gt;
&lt;p&gt;The instinct is to add concurrency:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-keyword-function"&gt;def&lt;/span&gt; &lt;span class="l-function"&gt;start_link&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-comment"&gt;_opts&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;  &lt;span class="l-module"&gt;Broadway&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;start_link&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-constant-builtin"&gt;__MODULE__&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;    &lt;span class="l-string-special-symbol"&gt;name: &lt;/span&gt;&lt;span class="l-constant-builtin"&gt;__MODULE__&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;    &lt;span class="l-string-special-symbol"&gt;producer: &lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;module: &lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;{&lt;/span&gt;&lt;span class="l-module"&gt;SQSProducer&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;}&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-string-special-symbol"&gt;concurrency: &lt;/span&gt;&lt;span class="l-number"&gt;4&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="5"&gt;    &lt;span class="l-string-special-symbol"&gt;processors: &lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;default: &lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;[&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;concurrency: &lt;/span&gt;&lt;span class="l-number"&gt;80&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="6"&gt;  &lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="7"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Eighty processors did make the queue drain. It also pushed the database to the
point where unrelated requests started timing out. We had not removed the
bottleneck, we had moved it somewhere with worse failure behaviour.&lt;/p&gt;
&lt;h2 id="the-question-nobody-had-asked"&gt;The question nobody had asked&lt;a href="#the-question-nobody-had-asked" aria-label="Link to heading 'The question nobody had asked'" data-heading-content="The question nobody had asked" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What should happen when deliveries arrive faster than we can durably record
them?&lt;/p&gt;
&lt;p&gt;There are only a few honest answers, and each of them is a product decision:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Slow the producer down.&lt;/strong&gt; Correct when the upstream can wait. The payments
provider retried with backoff, so it could.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shed load.&lt;/strong&gt; Correct when late data is worthless. Ours was not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffer, and accept that recovery takes time.&lt;/strong&gt; Correct when the burst is
bounded and you can afford the storage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Degrade the work itself&lt;/strong&gt; — record the delivery now, enrich it later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We picked the last one, and the pipeline stopped being a tuning problem:&lt;/p&gt;
&lt;figure class="code-block"&gt;&lt;figcaption class="code-head"&gt;&lt;span class="code-icon code-icon--elixir" aria-hidden="true"&gt;&lt;/span&gt;&lt;span class="code-lang"&gt;elixir&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="lumis"&gt;&lt;code class="language-elixir" translate="no" tabindex="0"&gt;&lt;div class="l-line" data-line="1"&gt;&lt;span class="l-constant"&gt;@&lt;/span&gt;&lt;span class="l-constant"&gt;impl &lt;/span&gt;&lt;span class="l-boolean"&gt;true&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="2"&gt;&lt;span class="l-keyword-function"&gt;def&lt;/span&gt; &lt;span class="l-function"&gt;handle_message&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-comment"&gt;_processor&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;message&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-comment"&gt;_context&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="3"&gt;  &lt;span class="l-variable"&gt;message&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="4"&gt;  &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Message&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;update_data&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-module"&gt;Delivery&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function"&gt;parse!&lt;/span&gt;&lt;span class="l-operator"&gt;/&lt;/span&gt;&lt;span class="l-operator"&gt;1&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="5"&gt;  &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Message&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;put_batcher&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:record&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="6"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="7"&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="8"&gt;&lt;span class="l-constant"&gt;@&lt;/span&gt;&lt;span class="l-constant"&gt;impl &lt;/span&gt;&lt;span class="l-boolean"&gt;true&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="9"&gt;&lt;span class="l-keyword-function"&gt;def&lt;/span&gt; &lt;span class="l-function"&gt;handle_batch&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-string-special-symbol"&gt;:record&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-variable"&gt;messages&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-comment"&gt;_batch_info&lt;/span&gt;&lt;span class="l-punctuation-delimiter"&gt;,&lt;/span&gt; &lt;span class="l-comment"&gt;_context&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt; &lt;span class="l-keyword"&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="10"&gt;  &lt;span class="l-variable"&gt;messages&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="11"&gt;  &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Enum&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;map&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt; &lt;span class="l-operator"&gt;&amp;amp;&lt;/span&gt;&lt;span class="l-operator"&gt;1&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-variable"&gt;data&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="12"&gt;  &lt;span class="l-operator"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="l-module"&gt;Deliveries&lt;/span&gt;&lt;span class="l-operator"&gt;.&lt;/span&gt;&lt;span class="l-function-call"&gt;insert_all&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;(&lt;/span&gt;&lt;span class="l-punctuation-bracket"&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="13"&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="14"&gt;  &lt;span class="l-variable"&gt;messages&lt;/span&gt;
&lt;/div&gt;&lt;div class="l-line" data-line="15"&gt;&lt;span class="l-keyword"&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Enrichment moved to a separate pipeline reading from the recorded rows, with
its own concurrency and its own failure domain. The write path got simple
enough that eight processors outperformed the eighty we had before.&lt;/p&gt;
&lt;h2 id="what-the-numbers-were-hiding"&gt;What the numbers were hiding&lt;a href="#what-the-numbers-were-hiding" aria-label="Link to heading 'What the numbers were hiding'" data-heading-content="What the numbers were hiding" class="anchor"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;concurrency:&lt;/code&gt; looks like a performance knob and behaves like a policy. Setting
it high says &amp;quot;I would rather overwhelm my dependencies than fall behind.&amp;quot;
Setting it low says the opposite. Neither is wrong, but you should be able to
say which one you meant, and why, without opening the dashboard.&lt;/p&gt;
&lt;p&gt;The BEAM makes it unusually cheap to express any of these answers. It does not
tell you which one your product needs.&lt;/p&gt;</content>
</entry>
</feed>
