<?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: lna_stub</title>
    <description>The latest articles on DEV Community by lna_stub (@lna_stub).</description>
    <link>https://dev.to/lna_stub</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%2F4166060%2Fffdc9f1d-2521-4d79-a5ac-ca7e5d59ef68.png</url>
      <title>DEV Community: lna_stub</title>
      <link>https://dev.to/lna_stub</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lna_stub"/>
    <language>en</language>
    <item>
      <title>Your integration's real bugs are in the events you can't trigger</title>
      <dc:creator>lna_stub</dc:creator>
      <pubDate>Tue, 06 Oct 2026 10:26:21 +0000</pubDate>
      <link>https://dev.to/lna_stub/your-integrations-real-bugs-are-in-the-events-you-cant-trigger-33n8</link>
      <guid>https://dev.to/lna_stub/your-integrations-real-bugs-are-in-the-events-you-cant-trigger-33n8</guid>
      <description>&lt;p&gt;Every integration I have written against a third-party API has the same shape.&lt;br&gt;
The request/response part is easy and I get it right on day one. The failures show up later, in the branches that only run on rare events: the chargeback that lands a month after a clean payment, the KYC rejection, the webhook whose signature my code accepted without really checking, the callback that arrives out of order.&lt;/p&gt;

&lt;p&gt;Those branches are exactly the ones a provider sandbox cannot help me exercise.&lt;br&gt;
I cannot ask a real sandbox to charge back a payment on cue, or to deliver a webhook twice, or to send &lt;code&gt;completed&lt;/code&gt; before I finished handling &lt;code&gt;pending&lt;/code&gt;. And a classic mock server does not help either: it answers one request at a time with no memory, so it cannot represent "this payment already succeeded, now it is disputed".&lt;/p&gt;

&lt;p&gt;So I wrote a small tool that treats an integration as what it actually is: a state machine.&lt;/p&gt;
&lt;h3&gt;
  
  
  The idea
&lt;/h3&gt;

&lt;p&gt;You describe a scenario in YAML. A session remembers where a client is in the flow, keyed by whatever identifies it in your API - a body field, a header, a query or a path parameter. The same endpoint then answers according to the current state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /v1/orders/ord_42/payment  -&amp;gt;  processing
   (a few seconds pass, a webhook fires)
GET /v1/orders/ord_42/payment  -&amp;gt;  succeeded
   (later, another webhook fires)
GET /v1/orders/ord_42/payment  -&amp;gt;  chargeback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entering a state can schedule webhooks: delayed, HMAC-signed in the Stripe format, with retries, a delivery journal and replay. That is the part I care about most, because the webhook path is where my code has the most untested branches. Does the handler reject a forged signature? Does it answer 2xx so the sender stops retrying? Does the same event delivered twice settle once?&lt;/p&gt;

&lt;p&gt;Two flags make it usable in CI. &lt;code&gt;--time-scale&lt;/code&gt; compresses "30 days later" into seconds, so a full lifecycle runs in a test. &lt;code&gt;--seed&lt;/code&gt; makes the template functions (ids, timestamps) reproducible, so snapshot assertions are stable.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it is not
&lt;/h3&gt;

&lt;p&gt;It does not record or proxy real traffic, and it has no GUI. If you need those, WireMock and Mockoon are the right tools. This one is aimed narrowly at deterministic, versionable simulation of event-driven APIs.&lt;/p&gt;

&lt;p&gt;And there is one honest limitation worth stating up front: a twin is only as accurate as the scenario you write. If the YAML mismodels the real response, the twin will happily repeat your mistake. The workflow that works for me is to confirm the response shape once against the provider's own sandbox, then use the tool to drive the hundred edge cases the sandbox cannot reach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical notes
&lt;/h3&gt;

&lt;p&gt;It is a single Go binary. No cloud, no account, MIT licensed. Every miss returns a diagnostic 404 that lists the closest matchers and the reason each one was rejected, which turns "why didn't this match" from guesswork into reading. Hot reload does not break active sessions - they finish on the config version they started with. Outbound webhook delivery has an SSRF guard and never follows redirects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it
&lt;/h3&gt;

&lt;p&gt;TwinStub is one binary that talks HTTP, so your integration stays in whatever language it already is. Install it with Docker, a prebuilt binary, or Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-p&lt;/span&gt; 9090:9090 ghcr.io/twinstub/twinstub
&lt;span class="c"&gt;# or: go install github.com/twinstub/twinstub/cmd/twinstub@latest&lt;/span&gt;
&lt;span class="c"&gt;# or: download a binary from the Releases page&lt;/span&gt;

twinstub init demo &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;demo
twinstub serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated project is the chargeback flow above. Repo, docs and a catalog of fintech scenarios: &lt;a href="https://github.com/twinstub/twinstub" rel="noopener noreferrer"&gt;https://github.com/twinstub/twinstub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you integrate with payment, logistics or CRM APIs, I would like to know where this model helps and where it breaks down for you.&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>testing</category>
      <category>api</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
