<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Baldur</title>
    <description>The latest articles on DEV Community by Baldur (@baldurhq).</description>
    <link>https://dev.to/baldurhq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4098762%2F132ae213-c1b1-4d88-8218-a10f5aa9290b.png</url>
      <title>DEV Community: Baldur</title>
      <link>https://dev.to/baldurhq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/baldurhq"/>
    <language>en</language>
    <item>
      <title>Why I built Baldur</title>
      <dc:creator>Baldur</dc:creator>
      <pubDate>Fri, 28 Aug 2026 15:43:58 +0000</pubDate>
      <link>https://dev.to/baldurhq/why-i-built-baldur-2g5j</link>
      <guid>https://dev.to/baldurhq/why-i-built-baldur-2g5j</guid>
      <description>&lt;h2&gt;
  
  
  It started as a question, not an outage
&lt;/h2&gt;

&lt;p&gt;I was building a shopping app in Django. An ordinary CRUD project — products,&lt;br&gt;
cart, checkout. Somewhere around the payment step I asked myself something I&lt;br&gt;
could not answer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if the payment gateway fails right here?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not "what if it is slow". What if the call goes out, the charge maybe lands,&lt;br&gt;
and the request dies in the middle of it.&lt;/p&gt;

&lt;p&gt;I want to be exact about this, because it is the part people usually inflate: I&lt;br&gt;
have never lost a payment in production. I have never run this at production&lt;br&gt;
scale at all. It was a side project and nobody was paying me. I just could not&lt;br&gt;
stop looking at the hole once I had seen it.&lt;/p&gt;

&lt;p&gt;So I wrote a circuit breaker by hand.&lt;/p&gt;
&lt;h2&gt;
  
  
  Then I found out it already existed
&lt;/h2&gt;

&lt;p&gt;Some time later I discovered &lt;code&gt;pybreaker&lt;/code&gt; and &lt;code&gt;tenacity&lt;/code&gt;. That was a deflating&lt;br&gt;
afternoon.&lt;/p&gt;

&lt;p&gt;But reading them clarified something. A retry library answers &lt;em&gt;should I try&lt;br&gt;
again?&lt;/em&gt; A circuit breaker answers &lt;em&gt;should I call at all?&lt;/em&gt; Both are good answers&lt;br&gt;
to their own question. Neither answers the one that had bothered me in the&lt;br&gt;
first place:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the breaker is open and the call never happens, where does that&lt;br&gt;
customer's order go?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In most codebases the honest answer is: into a log line, and then nowhere. The&lt;br&gt;
outage ends, the dashboards go green, and the work that failed during it is&lt;br&gt;
simply gone. Recovering it means grepping logs and reconstructing orders by&lt;br&gt;
hand, if it can be done at all.&lt;/p&gt;

&lt;p&gt;That gap is the reason there is a project here rather than a &lt;code&gt;pip install&lt;br&gt;
tenacity&lt;/code&gt; in my own app.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I actually built
&lt;/h2&gt;

&lt;p&gt;One decorator that composes the patterns, and a place for the work that still&lt;br&gt;
fails at the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;baldur&lt;/span&gt;


&lt;span class="nd"&gt;@baldur.protected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;charge-customer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;payment_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of the individual patterns are novel — circuit breaker, retry, fallback,&lt;br&gt;
and dead-letter queues are all textbook. What is not textbook is having them&lt;br&gt;
wired to each other: the breaker knows what the retry did, the fallback covers&lt;br&gt;
both the exhausted retry and the open breaker, and the work that survives all&lt;br&gt;
of that lands somewhere durable instead of evaporating. Then it replays when&lt;br&gt;
the dependency comes back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four decisions I would defend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The dead-letter queue is free
&lt;/h3&gt;

&lt;p&gt;It did not start that way. Capture originally lived in the paid tier, and in&lt;br&gt;
July I moved it — capture, browsing, and single-entry retry, resolve, and&lt;br&gt;
force-redrive — into the open-source core.&lt;/p&gt;

&lt;p&gt;The reasoning is that not losing work is the entire premise of the project. If&lt;br&gt;
the premise is behind a license key, the free tier is a demo of a problem&lt;br&gt;
rather than a solution to it. Operating that queue at scale is still paid;&lt;br&gt;
capturing the work and getting it back is not.&lt;/p&gt;

&lt;h3&gt;
  
  
  A decorator, not a sidecar
&lt;/h3&gt;

&lt;p&gt;A service mesh retries HTTP between services, and it does that well. It cannot&lt;br&gt;
re-queue a Celery task, return a cached price instead of an error, or know that&lt;br&gt;
this particular failure belongs to order 12345. Those decisions need the&lt;br&gt;
arguments in hand, which means running inside the process.&lt;/p&gt;

&lt;p&gt;If you already run a mesh, this is not a replacement for it. It handles the&lt;br&gt;
layer the mesh cannot see.&lt;/p&gt;

&lt;h3&gt;
  
  
  It has to work with nothing installed
&lt;/h3&gt;

&lt;p&gt;The default backend is in-memory. No Redis, no Docker, no environment&lt;br&gt;
variables. Point it at Redis when you need workers to share state — which in&lt;br&gt;
production you will — but the first run has to work on a laptop with nothing&lt;br&gt;
else running.&lt;/p&gt;

&lt;p&gt;A reliability tool that requires infrastructure before you can try it is a tool&lt;br&gt;
you evaluate on a Friday afternoon and never open again.&lt;/p&gt;

&lt;h3&gt;
  
  
  I took a number off this site
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://baldur.sh/concepts/foundations/resource-budget/" rel="noopener noreferrer"&gt;resource budget&lt;/a&gt; page used to&lt;br&gt;
carry a saturation-knee overhead figure. It is not there any more, and the page&lt;br&gt;
says why.&lt;/p&gt;

&lt;p&gt;Two things happened to it. A change to the circuit breaker removed a state&lt;br&gt;
write that ran on every successful request — and that write turned out to be&lt;br&gt;
most of what the original measurement was measuring. Then the re-measurement&lt;br&gt;
that established this ran on a host that was not quiet enough, so its own&lt;br&gt;
comparison sat inside its own noise. Evidence against the old number, nothing&lt;br&gt;
licensed to replace it.&lt;/p&gt;

&lt;p&gt;So the page now says the figure is withdrawn pending re-measurement. I would&lt;br&gt;
rather publish nothing there than a number I have evidence against, and I would&lt;br&gt;
rather you know that this is how numbers are handled here than have you find&lt;br&gt;
out later.&lt;/p&gt;

&lt;p&gt;The one cost figure I do quote is &lt;strong&gt;~39 µs per protected call&lt;/strong&gt; on the default&lt;br&gt;
chain, in memory, with no network in the path.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is not battle-tested by a war story.&lt;/strong&gt; I have no production incident to
tell you about. What I can point at is the test suite, the architecture gates
that run on every commit, and the &lt;a href="https://baldur.sh/concepts/foundations/resource-budget/" rel="noopener noreferrer"&gt;measured
envelope&lt;/a&gt; with its methodology.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is early access.&lt;/strong&gt; The API surface is stable, but a minor version can
still carry a breaking change, with a changelog entry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It runs in a single region.&lt;/strong&gt; Redis Sentinel for high availability is a
PRO feature; anything wider than one region is out of scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is not a hosted service.&lt;/strong&gt; It runs inside your process, makes no calls
home, and sends no telemetry.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The part I still do not have
&lt;/h2&gt;

&lt;p&gt;If you have hit this yourself — the outage ended, the dashboards went green,&lt;br&gt;
and the work that failed during it was simply gone — I would like to know what&lt;br&gt;
you actually did about it. Grepped the logs and rebuilt the records by hand?&lt;br&gt;
Wrote a one-off replay script? Decided it was not worth recovering?&lt;/p&gt;

&lt;p&gt;I built around my own answer to that question. I have very little idea how&lt;br&gt;
common it is, and that is the thing I would rather learn from you than guess at.&lt;/p&gt;

&lt;p&gt;And if you do look at the thing itself, the two calls I would most like to be&lt;br&gt;
argued out of are the in-process decorator instead of a sidecar, and the&lt;br&gt;
in-memory default. Both are above. Both are the kind of decision that looks&lt;br&gt;
right until someone runs it in a shape I did not think of.&lt;/p&gt;

&lt;p&gt;If you use tenacity and want the honest version of where it stops,&lt;br&gt;
&lt;a href="https://baldur.sh/comparison/tenacity/" rel="noopener noreferrer"&gt;that comparison is here&lt;/a&gt; — it includes&lt;br&gt;
when tenacity is the better choice.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://baldur.sh/why-i-built-baldur/" rel="noopener noreferrer"&gt;baldur.sh&lt;/a&gt;. The code is at &lt;a href="https://github.com/baldurhq/baldur" rel="noopener noreferrer"&gt;github.com/baldurhq/baldur&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>opensource</category>
      <category>python</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
