<?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: Yuriy</title>
    <description>The latest articles on DEV Community by Yuriy (@jmelnikov).</description>
    <link>https://dev.to/jmelnikov</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%2F3774460%2Fba1059ff-843b-4688-a8b0-94b721d99ed2.jpeg</url>
      <title>DEV Community: Yuriy</title>
      <link>https://dev.to/jmelnikov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jmelnikov"/>
    <language>en</language>
    <item>
      <title>Why Webhooks get lost and why they sometimes arrive twice</title>
      <dc:creator>Yuriy</dc:creator>
      <pubDate>Mon, 29 Jun 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/adal-cloud/why-webhooks-get-lost-and-why-they-sometimes-arrive-twice-429b</link>
      <guid>https://dev.to/adal-cloud/why-webhooks-get-lost-and-why-they-sometimes-arrive-twice-429b</guid>
      <description>&lt;p&gt;Webhooks often look deceptively simple.&lt;/p&gt;

&lt;p&gt;One service sends an HTTP request. Another service receives it. The receiver returns &lt;code&gt;200 OK&lt;/code&gt;, processes the payload, and everyone moves on.&lt;/p&gt;

&lt;p&gt;That model works until the webhook becomes part of a real business process.&lt;/p&gt;

&lt;p&gt;A missed event can mean a customer message that never reaches a chatbot, an order that does not appear in a CRM, a subscription that is not updated, or a payment that succeeds but does not unlock access for the customer.&lt;/p&gt;

&lt;p&gt;But lost webhooks are only half of the problem. The other half is duplicate delivery. A reliable webhook integration must be prepared for both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it mean to lose a webhook?
&lt;/h2&gt;

&lt;p&gt;A webhook is lost when an event is sent but never successfully processed by the receiving system.&lt;/p&gt;

&lt;p&gt;There are many possible causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the receiving server is temporarily unavailable;&lt;/li&gt;
&lt;li&gt;a reverse proxy is misconfigured;&lt;/li&gt;
&lt;li&gt;the application crashes after accepting the request;&lt;/li&gt;
&lt;li&gt;the database is overloaded;&lt;/li&gt;
&lt;li&gt;a TLS certificate expires;&lt;/li&gt;
&lt;li&gt;a deployment introduces an unexpected error;&lt;/li&gt;
&lt;li&gt;a DNS or network change routes traffic to infrastructure that is no longer active.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a low-risk notification, this may be inconvenient but manageable. For a production integration, it can become a business problem.&lt;/p&gt;

&lt;p&gt;Imagine a payment provider sends an event confirming that a customer has paid. If your application never processes that event, the customer may be charged but never receive access to the product.&lt;/p&gt;

&lt;p&gt;That is not just a technical failure. It creates support work, refunds, manual reconciliation, and a loss of trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  DNS migrations can create both loss and duplication
&lt;/h2&gt;

&lt;p&gt;A common example is moving an application to a new server.&lt;/p&gt;

&lt;p&gt;The usual approach looks simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy the application to a new server.&lt;/li&gt;
&lt;li&gt;Update the DNS record.&lt;/li&gt;
&lt;li&gt;Wait for traffic to move.&lt;/li&gt;
&lt;li&gt;Shut down the old server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem is that DNS does not update everywhere at the same moment.&lt;/p&gt;

&lt;p&gt;Resolvers, ISPs, corporate networks, and individual machines may cache the old address for different amounts of time. Even when the DNS TTL is low, some traffic can continue reaching the old server after the record has been changed.&lt;/p&gt;

&lt;p&gt;During the transition, different webhook senders may reach different servers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook provider
        |
        +--&amp;gt; old server
        |
        +--&amp;gt; new server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the old server is already offline, webhooks sent there may fail or be lost. If both servers are active, the same event can potentially be processed twice. That creates a different class of failure.&lt;/p&gt;

&lt;p&gt;A chatbot might send two replies to the same message. A CRM integration might create duplicate records. A payment workflow might credit a balance twice or issue the same product twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why retries are necessary
&lt;/h2&gt;

&lt;p&gt;Webhook delivery should normally use an at-least-once model.&lt;/p&gt;

&lt;p&gt;That means a sender retries delivery when it cannot confirm that the receiving system accepted the event. This is much safer than silently dropping an event after one failed attempt.&lt;/p&gt;

&lt;p&gt;However, at-least-once delivery has an important consequence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The receiving system must assume that the same event can arrive more than once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A sender may retry because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the destination returned a &lt;code&gt;5xx&lt;/code&gt; response;&lt;/li&gt;
&lt;li&gt;the connection timed out;&lt;/li&gt;
&lt;li&gt;the destination was temporarily unavailable;&lt;/li&gt;
&lt;li&gt;the sender did not receive the response, even though the receiver processed the request;&lt;/li&gt;
&lt;li&gt;the provider intentionally retries to protect against transient network failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is normal behaviour. It is not necessarily a bug in the provider. The receiving side must be designed accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idempotency is the protection against duplicates
&lt;/h2&gt;

&lt;p&gt;The key concept is &lt;strong&gt;idempotency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An idempotent operation produces the same final result whether it runs once or several times.&lt;/p&gt;

&lt;p&gt;For webhook processing, this usually means storing the provider’s event identifier before running the business logic.&lt;/p&gt;

&lt;p&gt;A simplified flow might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Receive webhook
        |
        v
Validate signature
        |
        v
Check event ID
        |
        +--&amp;gt; already processed --&amp;gt; return 200 OK
        |
        v
Store event ID
        |
        v
Run business logic
        |
        v
Return 200 OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if a payment provider sends an event with the ID &lt;code&gt;evt_123&lt;/code&gt;, your application should record that ID after receiving it.&lt;/p&gt;

&lt;p&gt;If the same event arrives again, the application should recognise it as a duplicate and avoid repeating the business operation.&lt;/p&gt;

&lt;p&gt;Returning &lt;code&gt;200 OK&lt;/code&gt; is still important: it tells the sender that the event has been safely handled, even when no additional action was needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledge quickly, process safely
&lt;/h2&gt;

&lt;p&gt;Another useful pattern is to separate acceptance from processing.&lt;/p&gt;

&lt;p&gt;Instead of performing all business logic directly inside the webhook request handler, the application can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validate the request.&lt;/li&gt;
&lt;li&gt;Store the event durably.&lt;/li&gt;
&lt;li&gt;Return a successful response quickly.&lt;/li&gt;
&lt;li&gt;Process the event asynchronously.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This reduces the risk of timeouts and makes failures easier to recover from. It also gives you a durable record of what happened, which is essential when investigating an incident.&lt;/p&gt;

&lt;p&gt;Of course, this only works if the storage step itself is reliable. Returning &lt;code&gt;200 OK&lt;/code&gt; before the event has been saved somewhere durable can still cause data loss.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical webhook reliability checklist
&lt;/h2&gt;

&lt;p&gt;A production webhook integration should answer these questions clearly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when the destination is temporarily unavailable?&lt;/li&gt;
&lt;li&gt;Are failed deliveries retried?&lt;/li&gt;
&lt;li&gt;How long are retries performed?&lt;/li&gt;
&lt;li&gt;Can the same event be delivered more than once?&lt;/li&gt;
&lt;li&gt;Does the application use event IDs or idempotency keys?&lt;/li&gt;
&lt;li&gt;Are incoming requests stored before expensive processing starts?&lt;/li&gt;
&lt;li&gt;Can the delivery history be inspected later?&lt;/li&gt;
&lt;li&gt;Can a failed event be replayed safely?&lt;/li&gt;
&lt;li&gt;During a migration, how long will the old endpoint remain available?&lt;/li&gt;
&lt;li&gt;Can the team distinguish between an event that was never received and one that was received but failed during processing?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If these questions do not have explicit answers, reliability depends too heavily on everything going right.&lt;/p&gt;

&lt;p&gt;And in production, everything eventually does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reliability is not just an HTTP status code
&lt;/h2&gt;

&lt;p&gt;Returning &lt;code&gt;200 OK&lt;/code&gt; is not the same as handling an event reliably.&lt;/p&gt;

&lt;p&gt;Reliable webhook processing requires a clear delivery model, retry behaviour, idempotent business logic, durable event storage, and enough visibility to understand what happened when an incident occurs.&lt;/p&gt;

&lt;p&gt;That is why webhook infrastructure matters.&lt;/p&gt;

&lt;p&gt;Adal Cloud is designed to make webhook delivery predictable and observable: incoming requests are retained, delivery attempts can be inspected, failed deliveries can be retried, and stored requests can be replayed when needed.&lt;/p&gt;

&lt;p&gt;When webhooks carry events that matter to customers, payments, or business operations, the delivery path should not be the least reliable part of the system.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>webhooks</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why I made Adal CLI Open Source</title>
      <dc:creator>Yuriy</dc:creator>
      <pubDate>Wed, 24 Jun 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/adal-cloud/why-i-made-adal-cli-open-source-n6f</link>
      <guid>https://dev.to/adal-cloud/why-i-made-adal-cli-open-source-n6f</guid>
      <description>&lt;p&gt;When you build an app that runs on someone else’s machine, trust becomes a very practical issue.&lt;/p&gt;

&lt;p&gt;It is not just about branding, positioning, or saying the right things on a landing page. The user is allowing your software to run in their local environment. Depending on what the app does, that environment may include files, terminal commands, configuration, credentials, or work projects.&lt;/p&gt;

&lt;p&gt;So the question becomes simple: how can a user know that the app is doing only what it is supposed to do?&lt;/p&gt;

&lt;p&gt;For Adal CLI, I decided that the answer should not be “just trust me.”&lt;/p&gt;

&lt;p&gt;It should be verifiable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local apps need a different level of trust
&lt;/h2&gt;

&lt;p&gt;A web app usually runs on infrastructure controlled by the product team. A local app is different. It runs directly on the user’s device.&lt;/p&gt;

&lt;p&gt;That changes the trust model.&lt;/p&gt;

&lt;p&gt;If a CLI tool reads files, sends requests, modifies configuration, or interacts with local projects, users have every right to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What exactly does it read?&lt;/li&gt;
&lt;li&gt;What does it send over the network?&lt;/li&gt;
&lt;li&gt;Does it collect anything unnecessary?&lt;/li&gt;
&lt;li&gt;Can I build it myself instead of downloading a binary?&lt;/li&gt;
&lt;li&gt;What happens if the project stops being maintained?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are fair questions. And for developer tools, they are especially important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source makes trust verifiable
&lt;/h2&gt;

&lt;p&gt;This is one of the main reasons I made Adal CLI open source.&lt;/p&gt;

&lt;p&gt;Open source does not magically make a project secure or trustworthy. But it changes the relationship between the developer and the user.&lt;/p&gt;

&lt;p&gt;Instead of asking people to believe a claim, it gives them a way to verify it.&lt;/p&gt;

&lt;p&gt;Anyone can inspect the code and see what the app actually does. If someone does not want to use a prebuilt binary, they can build the project from source. If a developer wants to review how data is handled, they can do that. If someone finds a problem, they can open an issue or submit a pull request.&lt;/p&gt;

&lt;p&gt;That possibility matters, even if most users never read the source code themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  It also makes development more transparent
&lt;/h2&gt;

&lt;p&gt;Another benefit of open source is that people can see how the project evolves.&lt;/p&gt;

&lt;p&gt;They can look at the changelog, commits, issues, and pull requests. They can see which bugs were fixed, which features were added, and how decisions were made over time.&lt;/p&gt;

&lt;p&gt;For me, that transparency is important. A local tool should not feel like a black box.&lt;/p&gt;

&lt;p&gt;This is especially true for CLI tools, because they often operate close to the developer’s actual work: repositories, config files, scripts, and terminal workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source is not a shortcut
&lt;/h2&gt;

&lt;p&gt;There is one important caveat: publishing the code is not enough.&lt;/p&gt;

&lt;p&gt;Open source is not a substitute for responsibility. A project can be open and still be confusing, insecure, poorly documented, or hard to build.&lt;/p&gt;

&lt;p&gt;So if open source is meant to support trust, it should come with the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clear documentation;&lt;/li&gt;
&lt;li&gt;reproducible builds;&lt;/li&gt;
&lt;li&gt;a changelog;&lt;/li&gt;
&lt;li&gt;a license;&lt;/li&gt;
&lt;li&gt;instructions for building from source;&lt;/li&gt;
&lt;li&gt;an explanation of what data the app uses and why.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without those things, “the code is open” can become more of a slogan than a real advantage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducing dependency on one maintainer
&lt;/h2&gt;

&lt;p&gt;There is another reason I care about this: sustainability.&lt;/p&gt;

&lt;p&gt;If a tool becomes useful to people, they should not be completely dependent on one person maintaining it forever.&lt;/p&gt;

&lt;p&gt;With an open source project, the community can fork it, fix critical issues, adapt it to specific needs, or continue development if the original maintainer slows down or moves on.&lt;/p&gt;

&lt;p&gt;That does not guarantee anything, of course. But it gives the project a better chance to survive beyond a single author.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;Not every app has to be open source.&lt;/p&gt;

&lt;p&gt;But if you are building something that runs locally, touches files, works with terminal commands, reads configuration, or handles sensitive data, openness can become one of the strongest arguments for trust.&lt;/p&gt;

&lt;p&gt;Trust is hard to earn through promises.&lt;/p&gt;

&lt;p&gt;It is much easier to build through transparency, verifiability, and respect for the user.&lt;/p&gt;

&lt;p&gt;That is why Adal CLI is open source.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>privacy</category>
      <category>cli</category>
      <category>security</category>
    </item>
    <item>
      <title>When you just need to inspect a webhook before writing code</title>
      <dc:creator>Yuriy</dc:creator>
      <pubDate>Sat, 13 Jun 2026 04:16:51 +0000</pubDate>
      <link>https://dev.to/adal-cloud/when-you-just-need-to-inspect-a-webhook-before-writing-code-5efd</link>
      <guid>https://dev.to/adal-cloud/when-you-just-need-to-inspect-a-webhook-before-writing-code-5efd</guid>
      <description>&lt;p&gt;Webhooks are usually discussed as something your application should process automatically.&lt;/p&gt;

&lt;p&gt;A payment provider sends an event. A Git hosting service notifies you about a push. A SaaS product sends a status update. Your backend receives the request, verifies it, stores it, and runs the required logic.&lt;/p&gt;

&lt;p&gt;But before all of that, there is often a much simpler step: you just need to see what the webhook actually looks like. Not the example from the documentation. Not the expected payload in your head. The real request.&lt;/p&gt;

&lt;p&gt;What method does it use? Which headers are included? How is the body structured? Where is the event type stored? Are there signatures, timestamps, nested fields, IDs, or provider-specific details that are not obvious from the docs?&lt;/p&gt;

&lt;p&gt;For this first step, setting up a local server can be unnecessary work.&lt;/p&gt;

&lt;p&gt;You can use a tunneling tool, expose your local application, write a temporary handler, log the request, format the output, and then inspect the result. That works, but sometimes it is too much for a simple question: what exactly does this service send?&lt;/p&gt;

&lt;p&gt;This is one of the scenarios where Adal can help.&lt;/p&gt;

&lt;p&gt;Adal gives you a permanent webhook endpoint URL. You can paste that URL into the webhook settings of a service like Stripe, GitHub, Slack, Dodo Payments, or any other provider that sends webhook events.&lt;/p&gt;

&lt;p&gt;When the webhook arrives, Adal stores the request and shows it in the interface.&lt;/p&gt;

&lt;p&gt;You can inspect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP method&lt;/li&gt;
&lt;li&gt;path&lt;/li&gt;
&lt;li&gt;headers&lt;/li&gt;
&lt;li&gt;body&lt;/li&gt;
&lt;li&gt;payload structure&lt;/li&gt;
&lt;li&gt;request size&lt;/li&gt;
&lt;li&gt;delivery details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful before you write the real backend handler.&lt;/p&gt;

&lt;p&gt;You can send a test webhook, inspect the actual request, compare it with the documentation, and only then decide how your application should process it.&lt;/p&gt;

&lt;p&gt;This is also useful when debugging. Sometimes the problem is not in your handler at all. The webhook may not be reaching the expected URL. The payload may not match the event you selected. A header may be missing. A signature may be different from what you expected.&lt;/p&gt;

&lt;p&gt;Having the raw request visible makes this much easier to understand.&lt;/p&gt;

&lt;p&gt;This does not replace proper webhook processing. You still need your backend, validation, signature verification, retries, idempotency, and business logic for production workflows.&lt;/p&gt;

&lt;p&gt;But before that, there is a very practical first step: receive the webhook, look at it and understand what was actually sent.&lt;/p&gt;

&lt;p&gt;That is the small use case Adal is designed to make simple.&lt;/p&gt;

&lt;p&gt;No temporary server just to inspect a payload. No manual log formatting. No guessing based only on documentation. Just a real incoming webhook, shown in a way that is easy to inspect before you write the code that handles it.&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building Adal: predictable webhook delivery without black boxes</title>
      <dc:creator>Yuriy</dc:creator>
      <pubDate>Thu, 11 Jun 2026 16:29:50 +0000</pubDate>
      <link>https://dev.to/adal-cloud/building-adal-predictable-webhook-delivery-without-black-boxes-4an</link>
      <guid>https://dev.to/adal-cloud/building-adal-predictable-webhook-delivery-without-black-boxes-4an</guid>
      <description>&lt;p&gt;Hi DEV community,&lt;/p&gt;

&lt;p&gt;I'm Yuriy, a fullstack developer and DevOps engineer with 10+ years of experience. I'm currently building Adal — a webhook delivery and observability platform for developers.&lt;/p&gt;

&lt;p&gt;The idea came from a simple frustration: webhook delivery often feels like a black box.&lt;/p&gt;

&lt;p&gt;A provider sends a request. Something fails. Maybe the endpoint was down, maybe the payload was different than expected, maybe the retry happened later, maybe the logs are incomplete. As developers, we often end up guessing instead of debugging from clear facts.&lt;/p&gt;

&lt;p&gt;Adal is my attempt to make webhook delivery more predictable.&lt;/p&gt;

&lt;p&gt;The core principles are simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;permanent endpoint URLs;&lt;/li&gt;
&lt;li&gt;full request visibility;&lt;/li&gt;
&lt;li&gt;delivery logs that explain what happened;&lt;/li&gt;
&lt;li&gt;replay without waiting for the provider;&lt;/li&gt;
&lt;li&gt;retries that are visible and controlled;&lt;/li&gt;
&lt;li&gt;no hidden transformations;&lt;/li&gt;
&lt;li&gt;infrastructure that can be inspected and reasoned about.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm building Adal as a practical developer tool, but also as an engineering experiment: how much of webhook infrastructure can be made explicit, observable, and boring in a good way?&lt;/p&gt;

&lt;p&gt;The stack is mostly Go, PostgreSQL, Redis, React, Docker, Cloudflare R2, and a regional architecture for receiving and delivering webhook requests. The CLI is open source and written in Go.&lt;/p&gt;

&lt;p&gt;On this DEV organization page, I plan to write about the technical side of building Adal: webhook infrastructure, backend reliability, regional delivery, observability, product decisions, failures, trade-offs, and lessons from building a SaaS from scratch.&lt;/p&gt;

&lt;p&gt;This is still an early-stage project, but it is already public and usable.&lt;/p&gt;

&lt;p&gt;If you work with webhooks, build integrations, or care about predictable backend systems, I'd be happy to hear your feedback.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>saas</category>
      <category>webhooks</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
