<?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: Shashank Pandey</title>
    <description>The latest articles on DEV Community by Shashank Pandey (@shashankpandey04).</description>
    <link>https://dev.to/shashankpandey04</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%2F3766246%2Ff1170e46-5d45-4600-ae11-c6cf9d5573c6.jpg</url>
      <title>DEV Community: Shashank Pandey</title>
      <link>https://dev.to/shashankpandey04</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shashankpandey04"/>
    <language>en</language>
    <item>
      <title>Idempotency: The Bug You Don't Notice Until Production</title>
      <dc:creator>Shashank Pandey</dc:creator>
      <pubDate>Fri, 07 Aug 2026 09:36:43 +0000</pubDate>
      <link>https://dev.to/shashankpandey04/idempotency-the-bug-you-dont-notice-until-production-17aj</link>
      <guid>https://dev.to/shashankpandey04/idempotency-the-bug-you-dont-notice-until-production-17aj</guid>
      <description>&lt;p&gt;I didn't learn about idempotency from a system-design interview.&lt;/p&gt;

&lt;p&gt;I learned it while building one of my projects.&lt;/p&gt;

&lt;p&gt;Everything seemed to be working exactly as expected.&lt;/p&gt;

&lt;p&gt;A webhook arrived.&lt;/p&gt;

&lt;p&gt;My backend processed it.&lt;/p&gt;

&lt;p&gt;The database was updated.&lt;/p&gt;

&lt;p&gt;The request completed successfully.&lt;/p&gt;

&lt;p&gt;Then it happened again.&lt;/p&gt;

&lt;p&gt;And again.&lt;/p&gt;

&lt;p&gt;The same event was being processed more than once.&lt;/p&gt;

&lt;p&gt;At first, I thought the webhook provider was doing something weird.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My system simply wasn't designed for reality.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Assumption That Broke Everything
&lt;/h2&gt;

&lt;p&gt;When I first built the webhook flow, the mental model was pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook arrives
      ↓
Process webhook
      ↓
Update database
      ↓
Done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It feels reasonable.&lt;/p&gt;

&lt;p&gt;An event happens → the provider sends it → my server processes it.&lt;/p&gt;

&lt;p&gt;One event.&lt;/p&gt;

&lt;p&gt;One request.&lt;/p&gt;

&lt;p&gt;One operation.&lt;/p&gt;

&lt;p&gt;Except distributed systems don't work that cleanly.&lt;/p&gt;

&lt;p&gt;The actual world looks more like:&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
       |
       | POST /webhook
       ↓
    My API
       |
       ↓
   Processing
       |
       ↓
   Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And somewhere along the way, things can fail.&lt;/p&gt;

&lt;p&gt;The provider might send the request successfully.&lt;/p&gt;

&lt;p&gt;My server might process it successfully.&lt;/p&gt;

&lt;p&gt;But the response might never make it back.&lt;/p&gt;

&lt;p&gt;Maybe the connection times out.&lt;/p&gt;

&lt;p&gt;Maybe the server crashes immediately after processing.&lt;/p&gt;

&lt;p&gt;Maybe there is a transient network failure.&lt;/p&gt;

&lt;p&gt;From my application's perspective:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Everything worked."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the provider's perspective:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Did they receive it?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when the provider doesn't receive the expected response, it may retry.&lt;/p&gt;

&lt;p&gt;So now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event #123
    ↓
Request #1 → processed
    ↓
Response lost
    ↓
Request #2 → processed AGAIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's where the fun begins.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Duplicate Event
&lt;/h2&gt;

&lt;p&gt;Imagine my webhook receives this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"evt_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"something.completed"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first request arrives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evt_123
   ↓
Process
   ↓
Database update
   ↓
Success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the provider retries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evt_123
   ↓
Process
   ↓
Database update AGAIN
   ↓
Success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second request isn't a new event.&lt;/p&gt;

&lt;p&gt;It's another &lt;strong&gt;delivery of the same logical event&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That distinction is the key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             ONE EVENT
                 |
       ┌─────────┼─────────┐
       ↓         ↓         ↓
   Delivery 1 Delivery 2 Delivery 3
       |         |         |
       └─────────┼─────────┘
                 ↓
        ONE BUSINESS EFFECT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The transport layer can deliver the event multiple times.&lt;/p&gt;

&lt;p&gt;My application needs to make sure the &lt;strong&gt;business operation remains correct&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's idempotency.&lt;/p&gt;




&lt;h2&gt;
  
  
  So, What Actually Is Idempotency?
&lt;/h2&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;An operation is idempotent when repeating it produces the same final result as performing it once.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mathematically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f(f(x)) = f(x)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For webhooks, that means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Receive event
    ↓
Process event
    ↓
Receive same event again
    ↓
Don't create another side effect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part isn't necessarily preventing the second request.&lt;/p&gt;

&lt;p&gt;You usually can't control that.&lt;/p&gt;

&lt;p&gt;The important part is preventing the &lt;strong&gt;duplicate effect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's a subtle but important distinction.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Solution I Thought Of
&lt;/h2&gt;

&lt;p&gt;Once I understood what was happening, the obvious solution was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'll just store the webhook ID."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;processed_webhooks

+----------------+
| event_id       |
+----------------+
| evt_123       |
| evt_456       |
| evt_789       |
+----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook arrives
      ↓
Extract event_id
      ↓
Have I processed this?
    /       \
  YES        NO
   |          |
   ↓          ↓
Ignore      Process
             ↓
       Store event_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually, that's correct.&lt;/p&gt;

&lt;p&gt;But there's another trap hiding inside it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Race Condition
&lt;/h2&gt;

&lt;p&gt;Imagine two copies of the same webhook arrive almost simultaneously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A                    Request B
    |                            |
    ↓                            ↓
Check event_id              Check event_id
    |                            |
    ↓                            ↓
"Not found"                 "Not found"
    |                            |
    ↓                            ↓
Process                     Process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both requests checked before either one had recorded the event.&lt;/p&gt;

&lt;p&gt;Now both execute the side effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evt_123
  ↓
Worker A → Process
  ↓
Worker B → Process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this isn't enough:&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;already_processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;process_event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;mark_as_processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a gap between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;insert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And concurrent requests can exploit that gap without doing anything malicious.&lt;/p&gt;

&lt;p&gt;This is where the database becomes more than just storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let the Database Enforce the Rule
&lt;/h2&gt;

&lt;p&gt;Instead of trusting application logic alone, make the event ID unique.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;webhook_events&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;event_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;processed_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the database itself guarantees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evt_123 → allowed
evt_123 → rejected
evt_123 → rejected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if multiple workers race to insert the same event ID, only one can win.&lt;/p&gt;

&lt;p&gt;That changed how I thought about idempotency.&lt;/p&gt;

&lt;p&gt;It's not simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if already_processed:
    return
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a &lt;strong&gt;data consistency invariant&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The same logical event must not be able to create multiple copies of the same business effect.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And invariants are often much safer when your database enforces them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then I Found the Next Problem
&lt;/h2&gt;

&lt;p&gt;But there's a deeper issue.&lt;/p&gt;

&lt;p&gt;Suppose I do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Insert event ID
2. Process event
3. Return success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Insert event ID
      ↓
SUCCESS
      ↓
Process business operation
      ↓
💥 Server crashes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The event is already marked as processed.&lt;/p&gt;

&lt;p&gt;But the actual operation never finished.&lt;/p&gt;

&lt;p&gt;The provider retries.&lt;/p&gt;

&lt;p&gt;My application checks the event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evt_123
   ↓
Already exists
   ↓
Skip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I've prevented duplication...&lt;/p&gt;

&lt;p&gt;but I've also prevented recovery.&lt;/p&gt;

&lt;p&gt;That's when idempotency stopped looking like a simple duplicate-check problem.&lt;/p&gt;

&lt;p&gt;It became a &lt;strong&gt;failure-handling problem&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Idempotency Is About Failure
&lt;/h2&gt;

&lt;p&gt;This is probably the biggest lesson I took away from the experience.&lt;/p&gt;

&lt;p&gt;When everything works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook
   ↓
Process
   ↓
Database
   ↓
Success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Almost any implementation looks correct.&lt;/p&gt;

&lt;p&gt;The interesting cases are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What if the request arrives twice?

What if two copies arrive simultaneously?

What if the server crashes halfway through?

What if the database succeeds but the response fails?

What if the external API succeeds but my server crashes before recording it?

What if the worker retries?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These aren't weird edge cases.&lt;/p&gt;

&lt;p&gt;They're normal failure modes in distributed systems.&lt;/p&gt;

&lt;p&gt;And webhooks make them particularly obvious because &lt;strong&gt;retries are expected behavior&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mental Model I Use Now
&lt;/h2&gt;

&lt;p&gt;I no longer think about a webhook as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;request → function → response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think about it as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event → state transition
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The event has an identity.&lt;/p&gt;

&lt;p&gt;The system has a state.&lt;/p&gt;

&lt;p&gt;And processing the same event again shouldn't produce an invalid state.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECEIVED
   ↓
PROCESSING
   ↓
COMPLETED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If something fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECEIVED
   ↓
PROCESSING
   ↓
FAILED
   ↓
RETRY
   ↓
PROCESSING
   ↓
COMPLETED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the system has a way to reason about recovery instead of simply hoping the request succeeds.&lt;/p&gt;




&lt;h2&gt;
  
  
  And This Doesn't Stop at Webhooks
&lt;/h2&gt;

&lt;p&gt;This is where the lesson became much bigger than the original bug.&lt;/p&gt;

&lt;p&gt;The same problem exists with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Webhooks&lt;/li&gt;
&lt;li&gt;Message queues&lt;/li&gt;
&lt;li&gt;Background workers&lt;/li&gt;
&lt;li&gt;Scheduled jobs&lt;/li&gt;
&lt;li&gt;Serverless functions&lt;/li&gt;
&lt;li&gt;Payment processing&lt;/li&gt;
&lt;li&gt;Event-driven architectures&lt;/li&gt;
&lt;li&gt;Retry mechanisms&lt;/li&gt;
&lt;li&gt;Distributed APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anywhere something can be retried, you need to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What happens if this operation runs twice?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because eventually, it probably will.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Meaning of "Exactly Once"
&lt;/h2&gt;

&lt;p&gt;You'll sometimes hear people talk about &lt;strong&gt;exactly-once processing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It sounds like the perfect solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event → Process exactly once
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in real distributed systems, that's an extremely strong guarantee.&lt;/p&gt;

&lt;p&gt;Your application may not be able to control how many times a message or webhook is delivered.&lt;/p&gt;

&lt;p&gt;What it &lt;em&gt;can&lt;/em&gt; control is whether repeated delivery produces repeated side effects.&lt;/p&gt;

&lt;p&gt;So a much more useful design goal is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;At-least-once delivery
+
Idempotent processing
=
Correct business result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The event might arrive three times.&lt;/p&gt;

&lt;p&gt;The system should still produce the result intended by one logical event.&lt;/p&gt;

&lt;p&gt;That's a much more practical way to think about reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing an Idempotent Webhook
&lt;/h2&gt;

&lt;p&gt;If I were designing the webhook from scratch today, I'd start with these questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. What uniquely identifies the event?
&lt;/h3&gt;

&lt;p&gt;Prefer the provider's event ID when available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evt_123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timestamp + user_id + event_type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;unless you have a very specific reason to construct your own key.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Can the database enforce uniqueness?
&lt;/h3&gt;

&lt;p&gt;Use something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event_id UNIQUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or a primary key.&lt;/p&gt;

&lt;p&gt;Don't rely solely on:&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because concurrent workers exist.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. What happens if processing fails halfway through?
&lt;/h3&gt;

&lt;p&gt;You need a recovery strategy.&lt;/p&gt;

&lt;p&gt;Possible approaches include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;transactions
processing states
retry queues
dead-letter queues
outbox patterns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The right choice depends on the system.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. What about external side effects?
&lt;/h3&gt;

&lt;p&gt;This one is easy to overlook.&lt;/p&gt;

&lt;p&gt;Your database might be transactional.&lt;/p&gt;

&lt;p&gt;The external API you're calling probably isn't part of that transaction.&lt;/p&gt;

&lt;p&gt;If you call another service, ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can I safely retry this request?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that service supports idempotency keys, use them.&lt;/p&gt;

&lt;p&gt;Now you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook event ID
        ↓
Your idempotency key
        ↓
Downstream API idempotency key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The guarantee propagates through the system.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. What happens when the worker crashes?
&lt;/h3&gt;

&lt;p&gt;Assume it will.&lt;/p&gt;

&lt;p&gt;Because eventually it will.&lt;/p&gt;

&lt;p&gt;Design your processing state so that the system can distinguish between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;completed
failed
still processing
stuck
retryable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of treating everything as simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;processed = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What I Would Tell Myself Before Building It
&lt;/h2&gt;

&lt;p&gt;If I could go back to the beginning of that project, I'd ask one question before writing the webhook handler:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What happens if I receive this exact event twice?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then I'd ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What happens if I receive it twice at exactly the same time?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And finally:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What happens if my server crashes halfway through?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those three questions expose a surprising amount of architectural weakness.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bug Wasn't Really the Bug
&lt;/h2&gt;

&lt;p&gt;Looking back, the duplicate webhook wasn't the most interesting part.&lt;/p&gt;

&lt;p&gt;The interesting part was realizing that my original mental model was wrong.&lt;/p&gt;

&lt;p&gt;I was thinking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
   ↓
Code
   ↓
Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production forced me to think:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event
   ↓
Network
   ↓
Retries
   ↓
Concurrency
   ↓
Partial failure
   ↓
State
   ↓
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a very different way of thinking about backend systems.&lt;/p&gt;

&lt;p&gt;And that's probably why idempotency is one of those concepts that feels trivial when you first hear the definition...&lt;/p&gt;

&lt;p&gt;until you have to build it.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Retries aren't bugs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They're a normal part of distributed systems.&lt;/p&gt;

&lt;p&gt;The mistake is designing your application as if retries won't happen.&lt;/p&gt;

&lt;p&gt;A webhook provider can send the same event again.&lt;/p&gt;

&lt;p&gt;A queue can deliver the same message again.&lt;/p&gt;

&lt;p&gt;A worker can execute the same job again.&lt;/p&gt;

&lt;p&gt;An API client can retry the same request again.&lt;/p&gt;

&lt;p&gt;Your job isn't always to stop those things from happening.&lt;/p&gt;

&lt;p&gt;Your job is to make sure &lt;strong&gt;repeating them doesn't corrupt the system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's idempotency.&lt;/p&gt;

&lt;p&gt;And for me, it wasn't something I really understood because I read the definition.&lt;/p&gt;

&lt;p&gt;I understood it because one of my projects broke my assumptions.&lt;/p&gt;

&lt;p&gt;Sometimes production is a surprisingly effective teacher.&lt;/p&gt;

</description>
      <category>cloudcomputing</category>
      <category>backenddevelopment</category>
      <category>eventdriven</category>
      <category>sre</category>
    </item>
    <item>
      <title>Designing a Cost-Effective Serverless Certificate Generation System on AWS</title>
      <dc:creator>Shashank Pandey</dc:creator>
      <pubDate>Mon, 13 Jul 2026 10:56:56 +0000</pubDate>
      <link>https://dev.to/shashankpandey04/designing-a-cost-effective-serverless-certificate-generation-system-on-aws-3ec4</link>
      <guid>https://dev.to/shashankpandey04/designing-a-cost-effective-serverless-certificate-generation-system-on-aws-3ec4</guid>
      <description>&lt;p&gt;&lt;em&gt;How I used AWS Lambda and Amazon S3 to build a scalable certificate platform that minimizes storage costs while improving security.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my previous &lt;a href="https://dev.to/shashankpandey04/why-i-built-a-serverless-certificate-generation-system-3bn4"&gt;article&lt;/a&gt;, I shared why I decided to build a serverless certificate generation system for our student community. As our events grew, manually generating and distributing certificates became increasingly difficult because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gmail SMTP&lt;/strong&gt; had daily sending limits and required one of us to keep a laptop running until all 200-300 certificate emails were delivered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Apps Script&lt;/strong&gt; required storing every generated certificate in Google Drive, leading to unnecessary long-term storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traditional mailing APIs&lt;/strong&gt; still required certificate generation to run on our own infrastructure, making the process compute-intensive and consuming our Cloudflare Workers quota.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bulk email delivery&lt;/strong&gt; through these approaches also increased the risk of emails being delayed or marked as spam.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These limitations made us rethink the entire workflow. Instead of trying to optimize the existing solution, we designed a serverless architecture on AWS that could generate certificates on demand, store them securely, and significantly reduce operational overhead.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Every event concludes with hundreds of participants expecting their certificates within minutes.&lt;/p&gt;

&lt;p&gt;While generating a certificate isn't particularly difficult, doing it reliably for hundreds of users introduces several operational challenges.&lt;/p&gt;

&lt;p&gt;The system needed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scale automatically during peak demand.&lt;/li&gt;
&lt;li&gt;Deliver certificates quickly.&lt;/li&gt;
&lt;li&gt;Avoid long-running servers.&lt;/li&gt;
&lt;li&gt;Reduce long-term storage.&lt;/li&gt;
&lt;li&gt;Keep certificate files secure.&lt;/li&gt;
&lt;li&gt;Be inexpensive enough for a student-led community.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than permanently storing every generated PDF, I wanted a smarter approach that stored only what was necessary.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The platform uses a simple serverless architecture built entirely on managed AWS services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foytovy3lop295xbfpzgh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foytovy3lop295xbfpzgh.png" alt="Project Architecture" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1. High-level architecture of the serverless certificate generation workflow. AWS Lambda checks whether a certificate already exists in Amazon S3. If available, it generates a pre-signed URL for secure access. Otherwise, the certificate is regenerated, uploaded to Amazon S3, and served to the user. An Amazon S3 Lifecycle Rule automatically removes regenerated certificates after the configured retention period.&lt;/p&gt;

&lt;p&gt;The idea is straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a certificate already exists, serve it immediately.&lt;/li&gt;
&lt;li&gt;If it doesn't, regenerate it from stored metadata.&lt;/li&gt;
&lt;li&gt;Store it temporarily.&lt;/li&gt;
&lt;li&gt;Automatically remove it later using Amazon S3 Lifecycle Rules.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why AWS Lambda?
&lt;/h2&gt;

&lt;p&gt;Certificate generation is not a continuous workload.&lt;/p&gt;

&lt;p&gt;Immediately after an event, hundreds of participants may request certificates simultaneously, while on other days there may be no requests at all.&lt;/p&gt;

&lt;p&gt;AWS Lambda was the ideal choice because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eliminates server management.&lt;/li&gt;
&lt;li&gt;Automatically scales with demand.&lt;/li&gt;
&lt;li&gt;Charges only when code runs.&lt;/li&gt;
&lt;li&gt;Integrates easily with other AWS services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means we never have to keep a server running just to generate certificates.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Amazon S3?
&lt;/h2&gt;

&lt;p&gt;Certificates are simply generated PDF files.&lt;/p&gt;

&lt;p&gt;Amazon S3 provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highly durable object storage.&lt;/li&gt;
&lt;li&gt;Excellent scalability.&lt;/li&gt;
&lt;li&gt;Low storage costs.&lt;/li&gt;
&lt;li&gt;Fine-grained access control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of exposing the bucket publicly, users receive temporary pre-signed URLs that allow secure downloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  On-Demand Certificate Generation
&lt;/h2&gt;

&lt;p&gt;One of the most important design decisions was treating certificates as &lt;strong&gt;derived artifacts&lt;/strong&gt; instead of permanent data.&lt;/p&gt;

&lt;p&gt;The participant information, event details, and certificate template remain the source of truth.&lt;/p&gt;

&lt;p&gt;Whenever someone requests a certificate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS Lambda checks whether the PDF already exists in Amazon S3.&lt;/li&gt;
&lt;li&gt;If it exists, the system immediately returns a pre-signed URL.&lt;/li&gt;
&lt;li&gt;If it doesn't exist, Lambda regenerates the certificate using the stored metadata.&lt;/li&gt;
&lt;li&gt;The newly generated PDF is uploaded to Amazon S3.&lt;/li&gt;
&lt;li&gt;A pre-signed download URL is returned to the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because certificates can always be recreated, there is no need to keep every generated PDF forever.&lt;/p&gt;




&lt;h2&gt;
  
  
  Automatic Cleanup with Amazon S3 Lifecycle Rules
&lt;/h2&gt;

&lt;p&gt;Keeping generated certificates forever didn't make much sense.&lt;/p&gt;

&lt;p&gt;Most participants download their certificate only once or twice. Storing every generated file indefinitely would gradually increase storage usage while retaining documents that might never be accessed again.&lt;/p&gt;

&lt;p&gt;To solve this, regenerated certificates are uploaded to Amazon S3 temporarily.&lt;/p&gt;

&lt;p&gt;An Amazon S3 Lifecycle Rule automatically removes these files after a short retention period.&lt;/p&gt;

&lt;p&gt;This provides several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces unnecessary storage.&lt;/li&gt;
&lt;li&gt;Keeps the bucket clean.&lt;/li&gt;
&lt;li&gt;Limits the lifetime of generated certificate files.&lt;/li&gt;
&lt;li&gt;Ensures certificates are recreated only when needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the system always retains the certificate metadata, deleting the generated PDF never results in data loss.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;p&gt;Security was another important factor in the architecture.&lt;/p&gt;

&lt;p&gt;The platform follows a few simple principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon S3 buckets remain private.&lt;/li&gt;
&lt;li&gt;Users never receive direct bucket access.&lt;/li&gt;
&lt;li&gt;Downloads are provided using temporary pre-signed URLs.&lt;/li&gt;
&lt;li&gt;IAM roles follow the principle of least privilege.&lt;/li&gt;
&lt;li&gt;Generated certificate files are automatically removed after a limited period.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By reducing how long generated PDFs remain in storage, the platform also minimizes unnecessary exposure of sensitive documents.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cost Optimization
&lt;/h2&gt;

&lt;p&gt;As a student community, every cloud resource matters.&lt;/p&gt;

&lt;p&gt;Several architectural decisions help keep operational costs low:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Lambda runs only when certificates are requested.&lt;/li&gt;
&lt;li&gt;Amazon S3 stores objects efficiently.&lt;/li&gt;
&lt;li&gt;Lifecycle Rules automatically clean up generated files.&lt;/li&gt;
&lt;li&gt;Certificates are regenerated only when required instead of occupying storage forever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than paying to retain thousands of PDFs indefinitely, the platform stores only what is necessary and recreates generated artifacts on demand.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Building this system reinforced several architectural principles.&lt;/p&gt;

&lt;p&gt;First, not every generated file needs to be stored permanently. If it can be recreated reliably from source data, temporary storage is often the better choice.&lt;/p&gt;

&lt;p&gt;Second, managed AWS services significantly reduce operational complexity. Instead of maintaining servers or writing cleanup scripts, services such as AWS Lambda and Amazon S3 handle much of the infrastructure automatically.&lt;/p&gt;

&lt;p&gt;Finally, simple architectures are often the easiest to maintain. By focusing only on the services required for the workflow, the platform remains easy to understand, cost-effective, and scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Migrating our certificate platform to a serverless architecture solved several challenges that we had experienced with previous approaches.&lt;/p&gt;

&lt;p&gt;Instead of depending on laptops, SMTP servers, or long-running processes, the platform now generates certificates on demand using AWS Lambda, stores them securely in Amazon S3, and automatically cleans up generated files using Amazon S3 Lifecycle Rules.&lt;/p&gt;

&lt;p&gt;The result is a solution that is scalable, secure, and significantly easier to operate.&lt;/p&gt;

&lt;p&gt;Sometimes the best optimization isn't generating files faster—it's recognizing that you don't need to keep them forever.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>programming</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Why I Built a Serverless Certificate Generation System</title>
      <dc:creator>Shashank Pandey</dc:creator>
      <pubDate>Tue, 07 Jul 2026 10:25:35 +0000</pubDate>
      <link>https://dev.to/shashankpandey04/why-i-built-a-serverless-certificate-generation-system-3bn4</link>
      <guid>https://dev.to/shashankpandey04/why-i-built-a-serverless-certificate-generation-system-3bn4</guid>
      <description>&lt;h2&gt;
  
  
  Why I Built a Serverless Certificate Generation System
&lt;/h2&gt;

&lt;p&gt;When I was the Group Leader of the AWS Student Builders Group at Lovely Professional University, one of the biggest challenges wasn't organizing events.&lt;/p&gt;

&lt;p&gt;It was what happened &lt;strong&gt;after&lt;/strong&gt; they ended.&lt;/p&gt;

&lt;p&gt;Every event meant generating and distributing certificates to hundreds of attendees. Which sounds easy for now.&lt;/p&gt;

&lt;p&gt;That's exactly what I thought. Turns out, it wasn't.&lt;/p&gt;

&lt;p&gt;Especially when you're running a student community with little to no budget and infrastructure decisions directly affect what you can (and can't) afford.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Attempt
&lt;/h2&gt;

&lt;p&gt;Like every developer, I started with the quickest solution.&lt;/p&gt;

&lt;p&gt;Google Apps Script + Gmail.&lt;/p&gt;

&lt;p&gt;It was easy to build, easy to maintain, and honestly, it worked pretty well...until our events started getting bigger.&lt;/p&gt;

&lt;p&gt;Once we crossed the 200+ attendee mark, Gmail started becoming the bottleneck.&lt;/p&gt;

&lt;p&gt;Sending that many emails in a short period meant running into sending limits, risking spam filters, and hoping every participant actually received their certificate.&lt;/p&gt;

&lt;p&gt;What was supposed to be a one-click automation slowly became a process of checking failed deliveries, retrying emails, and occasionally answering, &lt;em&gt;"Hey, I still haven't received my certificate."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Clearly, this wasn't going to scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  The "Let's Put It on the Website" Phase
&lt;/h2&gt;

&lt;p&gt;My next thought was simple.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Why email certificates at all? Why not let attendees generate them themselves?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So that's exactly what I did.&lt;/p&gt;

&lt;p&gt;Participants could log into the event platform and generate their certificates whenever they wanted.&lt;/p&gt;

&lt;p&gt;Problem solved...or so I thought.&lt;/p&gt;

&lt;p&gt;The website was running on Cloudflare Workers, and generating PDF certificates turned out to be one of the most compute-heavy operations in the entire application.&lt;/p&gt;

&lt;p&gt;The more certificates people generated, the more Workers quota disappeared.&lt;/p&gt;

&lt;p&gt;At one point, the application felt like it was spending more time generating PDFs than actually serving the website.&lt;/p&gt;

&lt;p&gt;I had fixed the email problem. Now I had a compute problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Taking a Step Back
&lt;/h2&gt;

&lt;p&gt;Instead of looking for a bigger server or a more expensive plan, I asked myself a different question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is the website generating certificates in the first place?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The website's job is to serve users.&lt;/p&gt;

&lt;p&gt;Certificate generation is a completely separate task.&lt;/p&gt;

&lt;p&gt;It doesn't have to happen inside the same request. It doesn't even have to happen on the same infrastructure.&lt;/p&gt;

&lt;p&gt;That realization changed everything.&lt;/p&gt;

&lt;p&gt;Instead of making the website do all the heavy lifting, it would simply say,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Hey, someone requested a certificate."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And another system would take care of the rest.&lt;/p&gt;

&lt;p&gt;That's when I decided to move towards an event-driven, serverless architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Works Today
&lt;/h2&gt;

&lt;p&gt;The platform already had a QR-code-based attendance system.&lt;/p&gt;

&lt;p&gt;When attendees scanned the checkout QR code, their attendance was marked as complete.&lt;/p&gt;

&lt;p&gt;Once that happened, they became eligible for a certificate.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Generate Certificate&lt;/strong&gt; button appeared on their dashboard, allowing them to generate the certificate up to three times for that event.&lt;/p&gt;

&lt;p&gt;From the participant's perspective, that's all they ever see.&lt;/p&gt;

&lt;p&gt;Attend the event.&lt;/p&gt;

&lt;p&gt;Scan the QR code.&lt;/p&gt;

&lt;p&gt;Click one button.&lt;/p&gt;

&lt;p&gt;Download the certificate.&lt;/p&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Behind that single click, however, there's an event-driven workflow handling all the heavy processing without putting additional load on the main application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I'm Happy With This Approach
&lt;/h2&gt;

&lt;p&gt;The biggest lesson from this project wasn't learning a new AWS service.&lt;/p&gt;

&lt;p&gt;It was learning that sometimes the solution isn't making your application stronger.&lt;/p&gt;

&lt;p&gt;It's making your application responsible for &lt;strong&gt;less&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once I stopped treating certificate generation as a website feature and started treating it as an independent workflow, everything else started falling into place.&lt;/p&gt;

&lt;p&gt;The application became lighter. The workflow became more scalable.&lt;/p&gt;

&lt;p&gt;And the operational cost became so low that our student community could comfortably afford it ourselves.&lt;/p&gt;

&lt;p&gt;Literally around the cost of a few evening snacks.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This article was about &lt;strong&gt;why&lt;/strong&gt; this system exists.&lt;/p&gt;

&lt;p&gt;In the next one, I'll go full engineering mode.&lt;/p&gt;

&lt;p&gt;I'll walk through the complete architecture, explain why I chose a serverless event-driven approach, why each AWS service earned its place in the system, and the trade-offs I had to make along the way.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
    </item>
    <item>
      <title>Fine-Tuning Isn’t Enough Anymore | Amazon Nova Forge Changes the Game</title>
      <dc:creator>Shashank Pandey</dc:creator>
      <pubDate>Wed, 11 Feb 2026 13:33:54 +0000</pubDate>
      <link>https://dev.to/shashankpandey04/fine-tuning-isnt-enough-anymore-amazon-nova-forge-changes-the-game-2kgc</link>
      <guid>https://dev.to/shashankpandey04/fine-tuning-isnt-enough-anymore-amazon-nova-forge-changes-the-game-2kgc</guid>
      <description>&lt;p&gt;For the last two years, enterprise AI customization has revolved around three techniques:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt engineering&lt;/li&gt;
&lt;li&gt;Retrieval-Augmented Generation (RAG)&lt;/li&gt;
&lt;li&gt;Supervised fine-tuning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They work. But they all share the same limitation:&lt;/p&gt;

&lt;p&gt;They modify a model after its core intelligence is already formed. And that’s the real bottleneck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem with “Late-Stage” Customization&lt;/strong&gt;&lt;br&gt;
By the time you fine-tune a model, its:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Representations are already shaped&lt;/li&gt;
&lt;li&gt;Internal reasoning patterns are already formed&lt;/li&gt;
&lt;li&gt;Safety alignment is already baked in&lt;/li&gt;
&lt;li&gt;Generalization boundaries are already defined&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fine-tuning becomes a surface-level adjustment.&lt;/p&gt;

&lt;p&gt;Continued pre-training (CPT) on proprietary data goes deeper, but introduces another issue:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catastrophic forgetting.&lt;/strong&gt;&lt;br&gt;
When you train only on domain-specific data, the model starts losing foundational capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instruction following&lt;/li&gt;
&lt;li&gt;General reasoning&lt;/li&gt;
&lt;li&gt;Safety robustness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where Amazon Nova Forge fundamentally changes the game.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Starting From Early Checkpoints&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of customizing a fully trained model, Nova Forge allows organizations to start from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pre-training checkpoints&lt;/li&gt;
&lt;li&gt;Mid-training checkpoints&lt;/li&gt;
&lt;li&gt;Post-training checkpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters technically because representation learning is still malleable at earlier stages.&lt;/p&gt;

&lt;p&gt;You’re not just adjusting weights for task behavior; you’re influencing how the model forms abstractions.&lt;/p&gt;

&lt;p&gt;That’s a different class of customization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Data Mixing as a First-Class Strategy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A major innovation is structured dataset blending.&lt;br&gt;
Instead of training solely on proprietary corpora, Nova Forge blends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organization-specific data&lt;/li&gt;
&lt;li&gt;Nova-curated general training datasets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Training runs on managed infrastructure through Amazon SageMaker and integrates into Amazon Bedrock for deployment.&lt;/p&gt;

&lt;p&gt;This approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preserves general intelligence&lt;/li&gt;
&lt;li&gt;Reduces overfitting&lt;/li&gt;
&lt;li&gt;Mitigates catastrophic forgetting&lt;/li&gt;
&lt;li&gt;Maintains instruction-following capability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technically, this resembles controlled continued pre-training with safety-aware balancing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Reinforcement Learning in Your Own Environment&lt;/strong&gt;&lt;br&gt;
This is where it gets interesting.&lt;/p&gt;

&lt;p&gt;Nova Forge enables reinforcement learning using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom reward functions&lt;/li&gt;
&lt;li&gt;Multi-turn rollouts&lt;/li&gt;
&lt;li&gt;External orchestration systems&lt;/li&gt;
&lt;li&gt;Domain-specific simulators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of static supervised tuning, organizations can:&lt;/p&gt;

&lt;p&gt;Reward accurate molecular structures&lt;br&gt;
Penalize unsafe robotic behaviors&lt;br&gt;
Optimize multi-step agent workflows&lt;/p&gt;

&lt;p&gt;This moves enterprise AI closer to environment-aware, task-optimized frontier systems without training from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) Why This Is Strategically Important&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nova Forge is not just a feature release.&lt;/p&gt;

&lt;p&gt;It signals AWS moving beyond:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting foundation models&lt;/li&gt;
&lt;li&gt;Offering fine-tuning APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Toward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enabling organizations to co-develop frontier-level models without absorbing full pre-training costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s a big shift in the AI stack.&lt;/p&gt;

&lt;p&gt;What This Means for Builders and DevRel&lt;br&gt;
For engineers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This reframes customization from: “Which prompt works best?” to “Where in the training lifecycle should I intervene?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For DevRel and community leaders:&lt;br&gt;
Understanding this shift matters.&lt;br&gt;
Explaining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why catastrophic forgetting happens&lt;/li&gt;
&lt;li&gt;Why early checkpoint intervention matters&lt;/li&gt;
&lt;li&gt;Why RL environments change domain alignment &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;is the kind of depth that moves conversations beyond surface-level AI hype.&lt;/p&gt;

&lt;p&gt;Enterprise AI is evolving from prompt engineering to model engineering.&lt;/p&gt;

&lt;p&gt;And Nova Forge is a signal that customization is moving earlier deeper and closer to the foundation itself.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
