<?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: Bucabay</title>
    <description>The latest articles on DEV Community by Bucabay (@bucabay).</description>
    <link>https://dev.to/bucabay</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%2F4010551%2Fff81c40c-e957-4d68-bcc6-c6f8430f6c81.jpg</url>
      <title>DEV Community: Bucabay</title>
      <link>https://dev.to/bucabay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bucabay"/>
    <language>en</language>
    <item>
      <title>I've launched 4 SaaS startups this year - my template</title>
      <dc:creator>Bucabay</dc:creator>
      <pubDate>Sat, 18 Jul 2026 04:39:51 +0000</pubDate>
      <link>https://dev.to/bucabay/ive-launched-4-saas-startups-this-year-my-template-2eap</link>
      <guid>https://dev.to/bucabay/ive-launched-4-saas-startups-this-year-my-template-2eap</guid>
      <description>&lt;p&gt;I've been on a SaaS startup building and launching spree this year since I lost half my web development contracts to AI. I think many developers have been in the same boat. &lt;/p&gt;

&lt;p&gt;Instead of looking for more development work (which is in real flux at the moment) I decided to instead launch my own SaaSes.. SaaSs? &lt;/p&gt;

&lt;p&gt;The first thing I noticed as many have, is AI can scaffold a UI very quickly - in 30 minutes I had a SaaS website - yay! It was quite bland but well designed - ok - nothing fancy. Then came iterating on that website, which took days - then weeks to get it right. AI seems to fix something and break something else at the same time - this went on forever. &lt;/p&gt;

&lt;p&gt;Now the bigger development effort for a SaaS is the backend - the dashboard and the multi-tenancy, the stripe plans/payments integrations, the email sending, authentication, team management, ACL, privacy, security... the list get's long pretty quick - but it is finite if you want it to be. &lt;/p&gt;

&lt;p&gt;So if you spent one week on the website, prepare to spend a month on the dashboard and 6 months on the backend. &lt;/p&gt;

&lt;p&gt;Quickly I learnt the important rule. Make a few choices upfront that minimizes your development. Like the 80/20 rule - but also the discipline to follow through. &lt;/p&gt;

&lt;p&gt;Choose the most important features of your SaaS. Figure out your value proposition and the pain point you fix. Focus only on that and build just that and launch. Don't build anything else - truly - do not. &lt;/p&gt;

&lt;p&gt;If you are building an email sending platform, just build a backend that sends emails. Hook into into your necessary SaaS features, which are authentication, payments. &lt;/p&gt;

&lt;p&gt;The good part is that you realize quickly that you are building a few things over and over, payment, auth, dashboard UI, users profile, teams. (At first, don't even build teams - it's not needed for your main goal - just the fix to the pain point). &lt;/p&gt;

&lt;p&gt;Now - this story is sequential - from when I started to what I learned along the way. However, it's not how you should build a SaaS - it's just the story. To build a SaaS now focus first on the most important thing - your AI development environment. Spend 2-3 days on that. Seriously, your SaaS will take at least a month to build, 2-3 days on the environment your AI will work in will pay off 100 fold. &lt;/p&gt;

&lt;p&gt;I use Claude most the time so it pays to read up on the best practices - &lt;a href="https://platform.claude.com/docs/en/about-claude/use-case-guides/overview" rel="noopener noreferrer"&gt;https://platform.claude.com/docs/en/about-claude/use-case-guides/overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also use a number of other Agents for differnet purposes. GLM2.5 at this time is as good as latest Claude Opus but faster. I use it for smaller fixes that are isolated. For very quick generation - an idea I want to scaffold I will use three differnet agents at once - Claude, GLM, DeepSeek (I now US doesn't like DeepSeek but it's the fastest model almost on par with Claude just for one shot research), combine their ideas into one and then have Claude optimize and choose the best mix. &lt;/p&gt;

&lt;p&gt;The reason I do 3 models is that they open up the possibility space of the response. Claude is optimized for US users and tied to US laws and government controls - it will reply with that as the scoping context that limits the openess of it's reponse. This is critical to understand for marketing research. &lt;/p&gt;

&lt;p&gt;GLM and DeepSeek are open models, general and non US focused, they give broader replies and consider non-US views. Their reponse is not as limited. Especially if you're researching non-US markets their response is valuable. &lt;/p&gt;

&lt;p&gt;Now back to how to actually build your SaaS. &lt;/p&gt;

&lt;p&gt;The 20/20 vision of hindsight. To actually build your SaaS - research first. Start with where your visitors will be coming from. Are you going to focus on Google searches? SEO? Paid Ads? If it's paid Ads, what is your search phrases, how much are the CPC on those - how dense is that market and how rooted are the current leaders? When you answer these questions your choice of SaaS becomes a lot more likely to succeed. &lt;/p&gt;

&lt;p&gt;List the ways you will get customers&lt;/p&gt;

&lt;p&gt;Use my existing network (X, LinkedIn, Dev.to)&lt;br&gt;
Paid Google Ads&lt;br&gt;
Paid Posts on Instagram &lt;br&gt;
SEO &lt;br&gt;
Posting on Reddit, Hacker News&lt;br&gt;
Making blog posts&lt;br&gt;
Creating freebies (Templates, Open Source libraries)&lt;br&gt;
Listings on directories&lt;/p&gt;

&lt;p&gt;How will your target your specific customers &lt;/p&gt;

&lt;p&gt;My linkedin is mostly software engineers - my SaaS is AI image generator - not the right fit - I'll just spam 10K linkedin contacts...&lt;/p&gt;

&lt;p&gt;These answers start allowing you to see which paths will actually bring customers to your SaaS, which changes the choices you make for your SaaS. &lt;/p&gt;

&lt;p&gt;Paid Google Ads - How much is CPC on AI Image generator. What's the CPA if I can make at least 2% sign up and then 2% of those buy the paid plan which is $20. Anwser that and you'll realize AI Image Generator is too costly for google ads unless you've got a huge ad budget. &lt;/p&gt;

&lt;p&gt;Now this goes on.. you'll notice something. What you thought was a good idea for a SaaS usually isn't from a marketing first point of view. When you envision how your customers find your SaaS and how many will sign up and purhcase a subscription. &lt;/p&gt;

&lt;p&gt;You'll notice the good SaaS is one that you research well and find need that is not filled well and marketing for it is reasonable. What was hard was figuring out what that need was. &lt;/p&gt;

&lt;p&gt;Now I can't tell you what those needs are - I'm still researching and building. This is just where I've landed after launching 4 startups (one very 2-3 months) this year. &lt;/p&gt;

&lt;p&gt;To help you on your journey of building your SaaS I've created a boilerplate - &lt;a href="https://saas-startup.mailkite.dev/" rel="noopener noreferrer"&gt;https://saas-startup.mailkite.dev/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is purposefully basic, what's important is the framework is Agent research first. Take a look at the github - &lt;a href="https://github.com/mailkite/saas-startup" rel="noopener noreferrer"&gt;https://github.com/mailkite/saas-startup&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;and importantly the AGENTS.md (CLAUDE.md equivalent) - &lt;a href="https://github.com/mailkite/saas-startup/blob/main/AGENTS.md" rel="noopener noreferrer"&gt;https://github.com/mailkite/saas-startup/blob/main/AGENTS.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If forces the agent to research the market and build UI, UX, features based on it's research. Documents the research, catelogs and refines. Over time the development will stay linear - not become a big pile of spaghetti AI code. &lt;/p&gt;

&lt;p&gt;Please share your thoughts and experience in building SaaS - the new best thing this year. &lt;/p&gt;

</description>
      <category>startup</category>
      <category>ai</category>
      <category>development</category>
      <category>ui</category>
    </item>
    <item>
      <title>Build software that heals itself in the agentic era</title>
      <dc:creator>Bucabay</dc:creator>
      <pubDate>Wed, 01 Jul 2026 22:16:28 +0000</pubDate>
      <link>https://dev.to/bucabay/build-software-that-heals-itself-in-the-agentic-era-540p</link>
      <guid>https://dev.to/bucabay/build-software-that-heals-itself-in-the-agentic-era-540p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Disclosure: I build &lt;a href="https://mailkite.dev" rel="noopener noreferrer"&gt;MailKite&lt;/a&gt;, and the open-source &lt;code&gt;mail-parse&lt;/code&gt; library I use as the example is ours. But the pattern is the point — it isn't MailKite-specific, and you can apply it to anything that eats messy input.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Self-healing software is a system architected so that, when it hits input the real world throws at it, it doesn't crash and it doesn't stay broken: it records a structured, PII-free &lt;em&gt;failure signature&lt;/em&gt;, and that signature feeds a repair loop — increasingly, an AI agent — that turns the breakage into a permanent fix behind automated gates. In the agentic era the bottleneck is no longer &lt;em&gt;writing&lt;/em&gt; the fix; a capable agent can do that. The bottleneck is architecting your software so an agent's fix is safe, automatic, and cumulative. This post is that pattern. I'll use our open-source MIME parser (&lt;code&gt;mail-parse&lt;/code&gt;) as the running example — messy input is where software goes to die — but the shape applies to almost any system that eats hostile real-world data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two honesty notes before I start, because a post that blurs shipped and planned isn't worth reading. First: this is &lt;strong&gt;part one of a two-part series&lt;/strong&gt; — part one is the architecture and what runs today; part two comes after the fully autonomous loop ships and we've watched it heal real input in the wild. Second: I'll label each piece &lt;strong&gt;shipped&lt;/strong&gt; or &lt;strong&gt;in progress&lt;/strong&gt; as I go, and there's a status table at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop the agentic era changes
&lt;/h2&gt;

&lt;p&gt;The classic repair loop is slow and human-shaped: a bug slips into production → someone eventually files an issue → a human reproduces it, writes a patch, ships a release → weeks later every install benefits. It works, but it's measured in weeks and gated on a human being in the loop for every single fix.&lt;/p&gt;

&lt;p&gt;Agents change what's &lt;em&gt;possible&lt;/em&gt; here, not by being trusted to write perfect code, but by being fast and tireless at the boring middle. The interesting question stops being "can an agent write the fix?" (increasingly, yes) and becomes: &lt;strong&gt;when an agent can propose a fix in seconds, how do you build software so that letting it do so isn't reckless?&lt;/strong&gt; Answer that, and your system stops accumulating breakage — every new way the world is wrong becomes a one-time event.&lt;/p&gt;

&lt;p&gt;Five design moves make it work. I'll state each generally, then ground it in the parser.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Never crash — turn every failure into a structured signal
&lt;/h2&gt;

&lt;p&gt;The foundation of a self-healing system is that failure is a &lt;em&gt;first-class, structured output&lt;/em&gt;, not an exception that unwinds the stack. If your software dies on bad input, there's nothing to heal; if it silently mangles it, there's nothing to detect. The discipline is: always produce the best result you can, and alongside it a machine-readable record of everything you had to paper over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the parser (shipped):&lt;/strong&gt; &lt;code&gt;mail-parse&lt;/code&gt; never throws. An unclosed MIME boundary pops the orphaned context and emits &lt;code&gt;BOUNDARY_NOT_CLOSED&lt;/code&gt;; a &lt;code&gt;charset&lt;/code&gt; that won't decode falls back and emits &lt;code&gt;UNKNOWN_CHARSET&lt;/code&gt;. You always get a message &lt;em&gt;and&lt;/em&gt; a typed list of what was wrong with it. Those diagnostics aren't logging — they're the raw material every downstream loop runs on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mailkite/mail-parse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// parse() never throws — even on a broken message it returns a best-effort&lt;/span&gt;
&lt;span class="c1"&gt;// result *plus* a typed list of everything it had to paper over.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawMime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// decoded as far as it could&lt;/span&gt;
&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// whatever it could recover&lt;/span&gt;
&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diagnostics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// → [&lt;/span&gt;
&lt;span class="c1"&gt;//     { code: "BOUNDARY_NOT_CLOSED", scope: "structure" },&lt;/span&gt;
&lt;span class="c1"&gt;//     { code: "UNKNOWN_CHARSET",     scope: "part", contentType: "text/html" },&lt;/span&gt;
&lt;span class="c1"&gt;//   ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Make fixes additive, not surgery — a plugin seam
&lt;/h2&gt;

&lt;p&gt;If every fix means editing the core, fixes are risky, they collide, and no agent (or human) should be trusted to make them at speed. The move is a &lt;strong&gt;registry&lt;/strong&gt;: a seam where new behavior is a self-contained, narrowly-scoped, &lt;em&gt;contained&lt;/em&gt; unit — it can't take down the whole system, and it's obvious what it touches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the parser (shipped):&lt;/strong&gt; fixups are middleware in a PostCSS-style registry — each declares a phase, a match predicate, and a handler, and a middleware that throws becomes a contained &lt;code&gt;MIDDLEWARE_ERROR&lt;/code&gt; diagnostic while the chain keeps going. A new format quirk is &lt;em&gt;a new middleware with a narrow predicate&lt;/em&gt;, not a patch threaded through the core. That containment is exactly what later lets a &lt;em&gt;generated&lt;/em&gt; fix be admitted without betting the system on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A new format quirk is a self-contained middleware with a narrow predicate —&lt;/span&gt;
&lt;span class="c1"&gt;// not a patch threaded through the core.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tnef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/ms-tnef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;extractWinmailDat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tnef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// If handler throws, the parser records a contained MIDDLEWARE_ERROR&lt;/span&gt;
&lt;span class="c1"&gt;// diagnostic and the rest of the chain keeps running.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Name failures identically everywhere — without leaking data
&lt;/h2&gt;

&lt;p&gt;To fix a class of breakage you first have to &lt;em&gt;name&lt;/em&gt; it, the same way across every install, without ever collecting private data. That's a &lt;strong&gt;failure signature&lt;/strong&gt;: a deterministic hash over &lt;em&gt;structure only&lt;/em&gt;. It does two things at once — it lets a thousand installs hitting the same bug collapse into one prioritized signal, and it gives the repair loop a precise, shareable target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the parser (shipped):&lt;/strong&gt; the signature is an FNV-1a hash over PII-free features — diagnostic codes, content-type, transfer-encoding, a byte-shape fingerprint, mailer family, structure path — and &lt;em&gt;never&lt;/em&gt; bytes, addresses, or subjects. Two installs on opposite sides of the world hitting the same Outlook-TNEF quirk compute the same hash. A multi-granularity rollup lets you cluster loosely or tightly. (It's pinned identical across our TypeScript, Python, and Go ports by a golden-corpus test, so the herd can't drift.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FailureSignature&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                 &lt;span class="c1"&gt;// = fnv1a(canonicalize(features))&lt;/span&gt;
  &lt;span class="nl"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;envelope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;structure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;part&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;diagnosticCodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;  &lt;span class="c1"&gt;// e.g. ["UNKNOWN_CHARSET"]&lt;/span&gt;
    &lt;span class="nl"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// the offending leaf's declared type&lt;/span&gt;
    &lt;span class="nl"&gt;transferEncoding&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;byteSignature&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// hex magic of the first N bytes — never content&lt;/span&gt;
    &lt;span class="nl"&gt;mailerFamily&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// X-Mailer normalized → "Outlook/16"&lt;/span&gt;
    &lt;span class="nl"&gt;structurePath&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// "multipart/mixed&amp;gt;…&amp;gt;application/ms-tnef"&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing in there is content — no subject, no addresses, no body bytes — so the same broken email produces the same hash in every language:&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;from&lt;/span&gt; &lt;span class="n"&gt;mailparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;compute_signature&lt;/span&gt;

&lt;span class="n"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compute_signature&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scope&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;part&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;diagnosticCodes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNKNOWN_CHARSET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contentType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/plain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transferEncoding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;base64&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# "13586f32bb2840c6" — byte-identical in Node, Python, and Go
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Two loops: fix the core for everyone, patch the edge safely
&lt;/h2&gt;

&lt;p&gt;Self-healing has two speeds, and you want both.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The cold loop — fix the library for everyone.&lt;/strong&gt; &lt;em&gt;(Shipped.)&lt;/em&gt; When the parser degrades it emits a &lt;code&gt;FailureReport&lt;/code&gt;. Where it goes is the deployer's choice — reporting is &lt;strong&gt;opt-in, with no default phone-home&lt;/strong&gt; — but point the built-in reporter at the core repo and it files exactly &lt;em&gt;one deduplicated GitHub issue per signature&lt;/em&gt; (a hidden &lt;code&gt;parse-signature:&lt;/code&gt; marker makes it idempotent; N installs → 1 issue), containing the structural signature and, in writing, &lt;em&gt;no message content&lt;/em&gt;. A responder — a human, or an AI coding routine triggered by the issue — reproduces from the scrubbed signature, fixes the core, and opens a PR that CI won't merge unless a golden corpus and a benign-input regression set both stay green. The fix ships to every install, in every language.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The hot loop — patch one edge now.&lt;/strong&gt; &lt;em&gt;(In progress: designed, next.)&lt;/em&gt; A library release takes time, and some quirks are concentrated in a single tenant's weird upstream system. For those, the design is an agent, handed the sealed failing fixture, that writes a &lt;em&gt;narrowly-scoped&lt;/em&gt; middleware plus a golden test pinning its behavior — a stopgap that heals that edge immediately while the cold loop fixes the root cause for everyone.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Trust the gates, not the generator — the security crux
&lt;/h2&gt;

&lt;p&gt;Here's the part the agentic era forces you to get right, because the hot loop means &lt;strong&gt;running code a model wrote against real production data.&lt;/strong&gt; You do not make that safe by trusting the model. You make it safe by building an architecture where a &lt;em&gt;fully compromised or simply wrong&lt;/em&gt; generated fix still can't do harm. Almost the entire hot-loop design &lt;em&gt;(in progress)&lt;/em&gt; is that safety envelope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sandboxed execution.&lt;/strong&gt; Generated fixes run as &lt;strong&gt;Wasm (Extism)&lt;/strong&gt; with &lt;strong&gt;deny-by-default capabilities&lt;/strong&gt; and a hard CPU/fuel budget — no network, no filesystem, no ambient authority. A bad fix can transform its input or burn its fuel and die; it can't reach anything else. (Generation and CI run in a separate sandbox, isolated from production.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adversarial gates the model doesn't author.&lt;/strong&gt; A fix is admitted only if it clears &lt;em&gt;system-owned&lt;/em&gt; tests: it must fire zero times against a benign corpus of well-formed input (no collateral damage), it must satisfy the golden test generated &lt;em&gt;from the failing case&lt;/em&gt; (it actually fixes the thing), and it must clear a &lt;strong&gt;specificity floor&lt;/strong&gt; (its predicate is narrow, not a catch-all). The agent proposes; adversarial tests dispose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary, then commit.&lt;/strong&gt; An admitted fix rolls out at 5% → 25% → 100%, watched against a structural agreement metric — a bad fix is caught on a sliver of traffic, not all of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A kill switch per fix.&lt;/strong&gt; Every generated unit is individually disableable by config, no redeploy — instant, reversible rollback.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What the hot-loop agent generates (designed, next): a narrowly-scoped&lt;/span&gt;
&lt;span class="c1"&gt;// middleware that fires ONLY on the failing signature — plus a golden test.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;charset&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-user-defined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// the one quirk, nothing else&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;decodeAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;windows-1252&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// Admitted only if it fires zero times on the benign corpus, passes the&lt;/span&gt;
&lt;span class="c1"&gt;// golden test from the failing case, and clears the specificity floor —&lt;/span&gt;
&lt;span class="c1"&gt;// none of which the agent wrote.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's what makes autonomy defensible: a vetted fix can auto-promote &lt;strong&gt;with no human in the loop&lt;/strong&gt; — not because we trust the model, but because what stands between a generated fix and production isn't anyone's judgment, it's a sandbox it can't escape, a battery of adversarial tests it didn't write, a canary that bounds blast radius, and a switch that undoes it. This is the same thesis behind &lt;a href="https://mailkite.dev/blog/agent-inbox-security-by-design/" rel="noopener noreferrer"&gt;how we built our agent inbox&lt;/a&gt;: in the agentic era you stop trying to make the model un-foolable and instead bound what a fooled model is &lt;em&gt;allowed to do&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where else this pattern fits
&lt;/h2&gt;

&lt;p&gt;MIME is a vivid example because email is gloriously broken, but the pattern fits anywhere software meets messy, adversarial, or drifting real-world input. The same five moves — tolerant core, plugin seam, anonymous failure signature, cold/hot loops, gated sandbox — map cleanly onto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ingesting messy formats.&lt;/strong&gt; CSV and bank-statement imports, PDF/OCR extraction, HTML scraping, log parsing, address and phone normalization. Every one is a hostile-input boundary that today either throws or silently corrupts. Signature the failure, let an agent add a scoped normalizer, gate it on a golden corpus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party API and webhook adapters.&lt;/strong&gt; Upstream payloads drift or go malformed and your integration breaks in prod. An adapter that emits a &lt;em&gt;schema-drift signature&lt;/em&gt; instead of a 500 lets an agent write a narrow shim for that provider's quirk — sandboxed, canaried — while a core fix follows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data pipelines / ETL schema drift.&lt;/strong&gt; An upstream column gets renamed or a type changes; the pipeline emits a signature rather than poisoning the warehouse, and an agent proposes the mapping behind tests that must stay green on the historical data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abuse, spam, and fraud rules.&lt;/strong&gt; A new evasion pattern is exactly a new failure signature. An agent generates a candidate rule that must fire zero times against a known-good corpus before it's canaried — the benign-corpus gate is the whole safety story, and it's identical to the parser's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client and device compatibility shims.&lt;/strong&gt; Quirky browsers, email clients, IoT firmware, legacy POS terminals — each non-conforming client is a per-quirk plugin, added on demand, contained, and kill-switchable, instead of a growing tangle of &lt;code&gt;if (userAgent...)&lt;/code&gt; in the core.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In each case the expensive, human-shaped part — noticing, reproducing, scoping, testing — is what the pattern automates, and the sandbox-plus-gates is what makes automating it safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's live today vs. what's next
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tolerant core (never throws, typed diagnostics)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Additive plugin seam (registry, contained fixes)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PII-free failure signatures (deterministic, deduping)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-language parity (golden corpus + signature pinning)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shadow harness (observe-only, structure-only compare)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold loop (opt-in, anonymous, deduplicated GitHub issues)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hot loop (AI-generated fixes)&lt;/td&gt;
&lt;td&gt;🔧 Designed, next&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wasm sandbox + capability/fuel limits&lt;/td&gt;
&lt;td&gt;🔧 Designed, next&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adversarial gates, canary rollout, per-fix kill switch&lt;/td&gt;
&lt;td&gt;🔧 Designed, next&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What does "self-healing software" actually mean here today?&lt;/strong&gt;&lt;br&gt;
Today: the system never dies on bad input, it records a precise PII-free signature of what broke, and identical signatures across all installs collapse into one deduplicated GitHub issue that drives a fix shipped to everyone. The fully autonomous part — an agent generating and shipping a sandboxed fix with no human in the loop — is designed and coming next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isn't letting an AI agent patch production reckless?&lt;/strong&gt;&lt;br&gt;
It would be if you trusted the agent's output. The design doesn't: generated fixes run as capability-denied Wasm with a fuel budget, are admitted only by adversarial tests the agent didn't write (benign-corpus zero-fire, a golden test from the failing case, a specificity floor), are canaried, and are individually kill-switchable. You trust the gates and the isolation, not the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does any of this send my data anywhere?&lt;/strong&gt;&lt;br&gt;
No. Reporting is opt-in with no default phone-home, and the failure signature is structural only — codes, types, byte-shape, mailer family — never bytes, addresses, or subjects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I apply the pattern without a MIME parser?&lt;/strong&gt;&lt;br&gt;
Yes — that's the point. Any boundary where you eat messy real-world input (imports, scrapers, API adapters, ETL, abuse rules, compatibility shims) can adopt the same five moves: tolerant core, plugin seam, anonymous failure signature, cold/hot loops, and a gated sandbox for generated fixes.&lt;/p&gt;




&lt;p&gt;Software will always meet a new way the world is wrong; the agentic era is a chance to make each new way a one-time event instead of a permanent scar. &lt;code&gt;mail-parse&lt;/code&gt; is our open-source instance of the pattern, in TypeScript, Python, and Go — see the &lt;a href="https://mailkite.dev/docs/libraries" rel="noopener noreferrer"&gt;libraries&lt;/a&gt;, and if you'd rather get the parsed message without running any of it, &lt;a href="https://mailkite.dev/docs/quickstart" rel="noopener noreferrer"&gt;point a domain at MailKite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part two comes after the autonomous loop ships.&lt;/strong&gt; Everything labeled &lt;em&gt;in progress&lt;/em&gt; above — the AI hot loop, the Wasm sandbox, the adversarial gates and canary rollout — gets its own post once it's live and we've watched it heal real input. And that feedback is the whole point: it arrives only through the &lt;strong&gt;anonymous, opt-in&lt;/strong&gt; failure signal described above — structural, PII-free, and never sent unless you wire up a reporter — so part two will be written from what actually broke in the wild, not from a single byte of anyone's data.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was first published &lt;a href="https://mailkite.dev/blog/self-healing-software-agentic-era/" rel="noopener noreferrer"&gt;on the MailKite blog&lt;/a&gt;. Related: &lt;a href="https://mailkite.dev/blog/agent-inbox-security-by-design/" rel="noopener noreferrer"&gt;You can't prompt your way out of prompt injection&lt;/a&gt; applies the same "trust the architecture, not the model" philosophy to AI agents with email.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>security</category>
    </item>
  </channel>
</rss>
