<?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: Divyakush Punjabi</title>
    <description>The latest articles on DEV Community by Divyakush Punjabi (@divyakush).</description>
    <link>https://dev.to/divyakush</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%2F4076031%2F6c15fac4-217a-46b7-a390-d13b22811267.jpg</url>
      <title>DEV Community: Divyakush Punjabi</title>
      <link>https://dev.to/divyakush</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/divyakush"/>
    <language>en</language>
    <item>
      <title>Don't fake the rider on the map</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 13 Sep 2026 13:35:40 +0000</pubDate>
      <link>https://dev.to/divyakush/dont-fake-the-rider-on-the-map-3o7o</link>
      <guid>https://dev.to/divyakush/dont-fake-the-rider-on-the-map-3o7o</guid>
      <description>&lt;p&gt;&lt;strong&gt;Open almost any delivery app and watch the tracking screen. An icon edges along a route, a countdown ticks, the status flips to "on the way." A surprising amount of that can be theatre — progress interpolated from a timer, because a moving icon feels better than a still one. On &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays&lt;/a&gt;, a food delivery platform, I made the opposite call: the tracking screen shows what the server knows, and nothing it doesn't.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why fake progress is tempting
&lt;/h2&gt;

&lt;p&gt;The honest state of a food order is lumpy. Nothing visible happens for minutes while a kitchen cooks, then several things happen at once. Product instinct says fill the gaps: animate a progress bar, advance the status on a schedule, drop a rider icon near the restaurant the moment the order is accepted.&lt;/p&gt;

&lt;p&gt;It works — until the timer and reality disagree. The screen says a rider is on the way and no rider has been assigned. The icon is moving and the food is still on the pass. Now the customer has been told something false by the one screen they trust to be accurate, which does more damage than a still screen ever could. They contact support, and support is looking at a different truth than the customer is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule: render events, not guesses
&lt;/h2&gt;

&lt;p&gt;Saturdays' tracking follows one principle: &lt;strong&gt;every change on the tracking screen corresponds to something that actually happened on the server.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Status comes from the state machine.&lt;/strong&gt; The order moves placed → confirmed → preparing → ready → picked up → out for delivery → delivered, and the screen shows a step only after that transition has committed. No client-side clock advances it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rider appears when a rider exists.&lt;/strong&gt; The rider icon shows up only when the server reports an assignment. Until then, the screen honestly says the order is being prepared.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An ETA is an estimate.&lt;/strong&gt; It's worth showing — but it's a number, not permission to animate a rider who isn't there.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Making "honest" also feel live
&lt;/h2&gt;

&lt;p&gt;The objection is that an honest screen feels dead. It doesn't have to. The fix is to make the truth arrive fast, not to invent it.&lt;/p&gt;

&lt;p&gt;Tracking runs over WebSockets, with Django Channels and Daphne on a Redis channel layer. When an order transitions on the server — the restaurant marks it ready, a rider is assigned, the rider picks it up — the event is pushed to the customer's open connection straight away:&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="c1"&gt;# after the transition has committed
&lt;/span&gt;&lt;span class="nf"&gt;async_to_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel_layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_send&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;public_id&lt;/span&gt;&lt;span class="si"&gt;}&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;type&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;order.update&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;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&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;&lt;em&gt;(Simplified.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The screen updates moments after the real event. There's no polling interval to hide behind and no timer to drift out of sync, because the timer never existed. The gaps where nothing changes are real gaps — and "your food is being prepared" is a perfectly good thing to show during them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where optimism does belong
&lt;/h2&gt;

&lt;p&gt;This isn't an argument against optimistic UI. When users act on their own data — adding to a cart, saving a preference — updating instantly and reconciling in the background is exactly right, because the user is the source of truth for their own intent.&lt;/p&gt;

&lt;p&gt;The line is ownership. &lt;strong&gt;Be optimistic about what the user just did. Be strictly honest about what other people and systems are doing&lt;/strong&gt; — the kitchen, the rider, the payment gateway. Order tracking is entirely the second kind.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;A status screen is a promise. Fill it with events the server has committed, deliver those events in real time so honesty never feels slow, and treat estimates as estimates. The screen will be quieter than a faked one — and it will be right every time someone looks at it.&lt;/p&gt;

&lt;p&gt;The full order flow behind that screen — the state machine, the rider layer, the realtime stack — is in the case study.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Read it:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;www.divyakush.com/projects/saturdays&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ux</category>
      <category>django</category>
      <category>react</category>
    </item>
    <item>
      <title>The last ten metres: verifying a food delivery at the door</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 13 Sep 2026 13:25:38 +0000</pubDate>
      <link>https://dev.to/divyakush/the-last-ten-metres-verifying-a-food-delivery-at-the-door-2ge</link>
      <guid>https://dev.to/divyakush/the-last-ten-metres-verifying-a-food-delivery-at-the-door-2ge</guid>
      <description>&lt;p&gt;&lt;strong&gt;A food delivery can be flawless for thirty minutes and fail in the last ten metres. The rider marks it delivered at the wrong door. Someone insists it never arrived. A handover gets recorded that didn't happen. On &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays&lt;/a&gt; — a food delivery platform where riders carry real, paid orders — the handover is verified with a one-time code, and the interesting engineering is in everything around those six digits.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the handover needs proof
&lt;/h2&gt;

&lt;p&gt;Nearly every other step of an order produces a system record: the payment settled, the restaurant accepted, the rider was assigned. The handover is the one moment that depends on two people standing at a door, and "delivered" is just a button on the rider's phone. Without proof, a dispute is one person's word against another's — and the platform, which is holding the money, has nothing to decide with.&lt;/p&gt;

&lt;p&gt;A handover OTP makes that moment verifiable. The customer has a code, the rider has to enter it, and "delivered" is only recorded when they match.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six digits is not the security
&lt;/h2&gt;

&lt;p&gt;The obvious objection: six digits is a million possibilities, and a script can try a million things. That's correct, which is why the code on its own isn't the control. The design around it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Store a hash, never the code.&lt;/strong&gt; The OTP is hashed at rest, the same way you'd treat a password. Anyone who reads the database — a leaked backup, an over-privileged query, an internal tool — sees nothing they could hand to a rider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lock out after repeated wrong entries.&lt;/strong&gt; This is what actually makes six digits sufficient. A million possibilities is trivial with unlimited attempts and hopeless with a handful. After repeated failures the handover locks, and a locked handover is a signal worth surfacing, not just a counter to reset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify on the server.&lt;/strong&gt; The rider app submits a guess and the server compares it. The code is never sent to the rider's device to be checked locally, because a check that runs on the client is a check the client can skip.&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;def&lt;/span&gt; &lt;span class="nf"&gt;verify_handover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;submitted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;otp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handover_otp&lt;/span&gt;          &lt;span class="c1"&gt;# hash, attempt count, locked flag
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HandoverLocked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;public_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;check_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;submitted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code_hash&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_failure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;        &lt;span class="c1"&gt;# may lock after repeated misses
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                     &lt;span class="c1"&gt;# single use
&lt;/span&gt;    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delivered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# still goes through the state machine
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(A simplified sketch of the flow.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Look at the last line. A correct code doesn't just set a field — it moves the order through the same state machine as every other transition, so "delivered" still has to be a legal next state.&lt;/p&gt;

&lt;h2&gt;
  
  
  One OTP system, not two
&lt;/h2&gt;

&lt;p&gt;Saturdays isn't the only product in the order flow: restaurants running the DineGuru POS work the same orders. The easy path would have been an OTP implementation in each product. Two implementations means two hashing choices, two lockout policies, two sets of edge cases — and eventually two different answers to "is this handover valid?"&lt;/p&gt;

&lt;p&gt;So there's one OTP system, shared across both. Change the attempt limit or how a lock is cleared, and it changes once, everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;When the weakest moment in a workflow is a human interaction, don't try to make it more trusted — make it verifiable. A short code is enough when it's hashed at rest, rate-limited by lockout, checked on the server and tied into your state machine. The strength comes from the system around the secret, not the length of it.&lt;/p&gt;

&lt;p&gt;The rest of the delivery flow — live tracking, the order state machine, doorstep payment collection — is in the case study.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Full case study:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays — Food Delivery Platform&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>backend</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Integrating two of your own products: draw a real boundary</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 13 Sep 2026 13:15:38 +0000</pubDate>
      <link>https://dev.to/divyakush/integrating-two-of-your-own-products-draw-a-real-boundary-3a8c</link>
      <guid>https://dev.to/divyakush/integrating-two-of-your-own-products-draw-a-real-boundary-3a8c</guid>
      <description>&lt;p&gt;&lt;strong&gt;I built two products that need to talk to each other constantly: &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays&lt;/a&gt;, a food delivery platform, and &lt;a href="https://www.divyakush.com/projects/dineguru" rel="noopener noreferrer"&gt;DineGuru&lt;/a&gt;, a restaurant POS. A restaurant running DineGuru has to be able to work its Saturdays orders from the POS it already uses. When you own both codebases, the shortcut is right there — point them at the same database and skip the API. I didn't, and the reasons apply to any two systems you happen to control.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "they're both mine" is the trap
&lt;/h2&gt;

&lt;p&gt;Owning both sides removes the &lt;em&gt;social&lt;/em&gt; pressure to design an interface. It doesn't remove the &lt;em&gt;engineering&lt;/em&gt; need for one. A shared database means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every schema change is a two-product deploy.&lt;/strong&gt; Rename a column in one and the other breaks at runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There is no authority.&lt;/strong&gt; Either product can write anything, so business rules enforced in one are silently bypassed by the other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can't reason about blast radius.&lt;/strong&gt; A bug in the POS can corrupt delivery data, and nothing in the architecture says it shouldn't be able to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The day the two products have different release cadences, different teams or different customers, all of that becomes a crisis. Designing the boundary up front is far cheaper.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the boundary looks like
&lt;/h2&gt;

&lt;p&gt;DineGuru drives day-to-day restaurant operation through a &lt;strong&gt;separate, versioned API surface&lt;/strong&gt; on Saturdays, and the design of that surface is where the real decisions are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authenticated by an API key, scoped to one restaurant.&lt;/strong&gt; The key doesn't identify "DineGuru." It identifies &lt;em&gt;one restaurant&lt;/em&gt; — and every request made with it can only see and act on that restaurant's data. There is no endpoint where a key reaches across restaurants, because the scoping is applied before any view logic runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identity is read-only across the boundary.&lt;/strong&gt; The POS can operate a restaurant's day. It cannot rename the restaurant, and it cannot clear a platform suspension. Those are platform decisions, and the integration surface simply doesn't expose them. A key that operates the day is a very different permission from a key that administers the account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same domain rules apply.&lt;/strong&gt; Orders changed through the POS pass through the same state machine and the same payment gate as orders changed anywhere else. The integration is one more caller of the domain layer, not a side door around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the key lives
&lt;/h2&gt;

&lt;p&gt;One decision ended up simplifying the whole integration. The API key is stored &lt;strong&gt;encrypted on DineGuru's server&lt;/strong&gt; and used server-to-server. It never reaches a browser.&lt;/p&gt;

&lt;p&gt;That's the right security posture on its own, but it had a useful side effect: because no browser ever calls Saturdays with that key, &lt;strong&gt;no cross-origin surface had to be opened&lt;/strong&gt; for the integration. No CORS allowlist for a partner domain, no preflight handling, no chance of a misconfigured origin rule exposing the API to arbitrary sites. The server-to-server design removed a category of configuration rather than securing it.&lt;/p&gt;

&lt;p&gt;In the other direction, events flow out through an HMAC-signed webhook relay, so the receiving side can verify a message genuinely came from Saturdays.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Integrate your own systems the way you'd integrate a stranger's: a versioned contract, credentials scoped to the smallest unit that makes sense, identity and administration kept off the operational surface, and every request routed through the same rules as everyone else. It's more work on day one, and dramatically less on the day the two products stop moving in lockstep.&lt;/p&gt;

&lt;p&gt;How that integration fits into the rest of the platform is covered in the case study.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;See it:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;www.divyakush.com/projects/saturdays&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>security</category>
    </item>
    <item>
      <title>Your primary key shouldn't be in the URL</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 13 Sep 2026 13:05:36 +0000</pubDate>
      <link>https://dev.to/divyakush/your-primary-key-shouldnt-be-in-the-url-35io</link>
      <guid>https://dev.to/divyakush/your-primary-key-shouldnt-be-in-the-url-35io</guid>
      <description>&lt;p&gt;&lt;strong&gt;Open an order confirmation in a lot of apps and the URL ends in something like &lt;code&gt;/orders/48213&lt;/code&gt;. That number tells a stranger more than you'd think: roughly how many orders you've taken, how fast you're growing, and — if a single authorization check is ever missing — exactly which URLs to try next. On &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays&lt;/a&gt;, a food delivery platform, the IDs that leave the server are never the ones the database uses internally.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What an auto-increment ID leaks
&lt;/h2&gt;

&lt;p&gt;A sequential integer primary key is a great internal identifier: compact, fast to index, naturally ordered. It is a poor &lt;em&gt;external&lt;/em&gt; one, for three reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's a business metric.&lt;/strong&gt; Place one order on Monday and another on Friday, then subtract. You now know the platform's weekly order volume. Competitors do exactly this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's a map.&lt;/strong&gt; If &lt;code&gt;/orders/48213&lt;/code&gt; is yours, &lt;code&gt;/orders/48212&lt;/code&gt; is someone else's. An attacker doesn't have to guess anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It turns one bug into a breach.&lt;/strong&gt; An endpoint that forgets to check ownership — an IDOR, one of the most common web vulnerabilities there is — goes from "exploitable if you happen to find an ID" to "download every order by counting."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Two identifiers, two jobs
&lt;/h2&gt;

&lt;p&gt;The fix is to stop making one column serve two audiences:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BigAutoField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# internal: joins, indexes
&lt;/span&gt;    &lt;span class="n"&gt;public_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UUIDField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;editable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;                                               &lt;span class="c1"&gt;# external: URLs, APIs, events
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The integer stays inside the database, doing what it's good at. Every URL, API response, event payload and webhook uses &lt;code&gt;public_id&lt;/code&gt;. A random UUID says nothing about volume, has no neighbour, and can't be enumerated.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is not access control
&lt;/h2&gt;

&lt;p&gt;It's worth being blunt about the limit, because this is exactly where teams talk themselves into a mistake: &lt;strong&gt;an unguessable ID is not a permission check.&lt;/strong&gt; URLs get shared, logged, pasted into support tickets and saved in browser history. If knowing an order's UUID is enough to read it, you haven't fixed the vulnerability — you've hidden it.&lt;/p&gt;

&lt;p&gt;Every lookup still has to be scoped to the caller: this order belongs to this customer, this restaurant, this rider's assignment. The public ID is &lt;strong&gt;defence in depth&lt;/strong&gt;. When the ownership check is correct, it changes nothing. When one is ever missing, it turns a catastrophic, enumerable leak into a narrow, hard-to-exploit one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it without regret
&lt;/h2&gt;

&lt;p&gt;A few details make the pattern cheap to live with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add it early.&lt;/strong&gt; Retrofitting a public ID onto a table that outside systems already reference means migrating every consumer. On day one it's a single field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index it.&lt;/strong&gt; &lt;code&gt;unique=True&lt;/code&gt; gives you the index, so lookups by public ID stay fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never expose both.&lt;/strong&gt; An API that returns &lt;code&gt;id&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; &lt;code&gt;public_id&lt;/code&gt; has undone the whole point. Serializers should simply leave the internal key out.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Separate the identifier your database needs from the identifier the world sees. It costs one column and removes a whole class of information leak — as long as you remember it's the second lock on the door, never the only one.&lt;/p&gt;

&lt;p&gt;More of the design decisions behind the platform — the data model, the payment path, the POS integration — are in the case study.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Read the case study:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays — Food Delivery Platform&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>database</category>
      <category>webdev</category>
      <category>django</category>
    </item>
    <item>
      <title>The maintenance switch that can't lock you out</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 13 Sep 2026 12:55:35 +0000</pubDate>
      <link>https://dev.to/divyakush/the-maintenance-switch-that-cant-lock-you-out-cb6</link>
      <guid>https://dev.to/divyakush/the-maintenance-switch-that-cant-lock-you-out-cb6</guid>
      <description>&lt;p&gt;&lt;strong&gt;Every live platform needs a way to stop taking orders — a payment provider incident, a bad deploy, a data migration you don't want customers writing through. On &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays&lt;/a&gt;, a food delivery platform taking real payments, that's a single flag. Building it took an afternoon. Getting right the one detail most implementations get wrong took longer.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The mechanism is small on purpose
&lt;/h2&gt;

&lt;p&gt;A kill switch should be boring, because the moment you need it is the moment you have the least patience for cleverness. The whole thing is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One config flag&lt;/strong&gt;, read at request time, flippable without a deploy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One middleware&lt;/strong&gt;, mounted ahead of every view, that checks it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One response&lt;/strong&gt;: a structured &lt;code&gt;503 Service Unavailable&lt;/code&gt; with a &lt;code&gt;Retry-After&lt;/code&gt; header.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaintenanceMiddleware&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_response&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;platform_paused&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;is_exempt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;JsonResponse&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;code&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;maintenance&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;detail&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;Temporarily unavailable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&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;Retry-After&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;120&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(A simplified sketch of the shape, not the production file.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each choice there is deliberate. &lt;strong&gt;Middleware, not a decorator,&lt;/strong&gt; because a decorator is opt-in per view, and the one view someone forgets is the one that keeps taking orders. &lt;strong&gt;503, not 500 or 200,&lt;/strong&gt; because clients, load balancers and crawlers all read 503 as "temporary, come back later" — and &lt;code&gt;Retry-After&lt;/code&gt; tells them when. &lt;strong&gt;Structured JSON,&lt;/strong&gt; because the frontend needs to render a maintenance screen, not a generic error toast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually matters: what's exempt
&lt;/h2&gt;

&lt;p&gt;A middleware that blocks &lt;em&gt;everything&lt;/em&gt; has an obvious flaw. If the admin API that flips the flag sits behind it, turning maintenance on removes your ability to turn it off. You've built a door that locks from the outside with the key still inside.&lt;/p&gt;

&lt;p&gt;So the switch needs an exemption list — and the discipline is keeping it &lt;strong&gt;narrow&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The admin control API&lt;/strong&gt;, so the switch that turned maintenance on can turn it off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health checks&lt;/strong&gt;, so your infrastructure doesn't conclude the service is dead and start restarting it mid-incident.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema docs&lt;/strong&gt; — read-only, harmless, and useful while you're debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. Every extra path on that list is a hole in the switch. "Just exempt the partner API too" sounds reasonable right up until the incident is &lt;em&gt;in&lt;/em&gt; the partner integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why narrow beats clever
&lt;/h2&gt;

&lt;p&gt;The temptation is to make the switch granular: pause checkout but not browsing, pause one city, pause one restaurant. Some of that is genuinely useful — Saturdays' operator console has separate restaurant holds with different blast radius. But those are operational tools. The platform switch is an emergency brake, and an emergency brake with ten settings is one you'll misconfigure under pressure.&lt;/p&gt;

&lt;p&gt;Keep the brake binary and the exemptions minimal. Put the nuance in separate, clearly named controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;When you build any control that can take a system offline, design the "how do I undo this" path first, and make sure the thing it controls can't affect it. Then keep the list of things that bypass the control as short as you can defend.&lt;/p&gt;

&lt;p&gt;The operator console, the restaurant holds, and the rest of how the platform is run day to day are covered in the case study.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Full write-up:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;www.divyakush.com/projects/saturdays&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>devops</category>
      <category>backend</category>
      <category>python</category>
    </item>
    <item>
      <title>The interface can hide a button. It can't stop a request.</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 13 Sep 2026 12:45:33 +0000</pubDate>
      <link>https://dev.to/divyakush/the-interface-can-hide-a-button-it-cant-stop-a-request-388n</link>
      <guid>https://dev.to/divyakush/the-interface-can-hide-a-button-it-cant-stop-a-request-388n</guid>
      <description>&lt;p&gt;&lt;strong&gt;Every web app eventually ships the same bug: the frontend correctly greys out an option, and someone sends the request anyway. A discount that shouldn't apply, a delivery window that's closed, a price from a stale cached menu. The UI did its job. The system didn't. On &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays&lt;/a&gt; — a food delivery platform with a customer app, a restaurant surface, a rider layer and an operator console all hitting one backend — I settled this with one rule, applied everywhere.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A disabled button is a suggestion
&lt;/h2&gt;

&lt;p&gt;It's worth being precise about what the client is. Your React app is one of many things that can call your API. So is &lt;code&gt;curl&lt;/code&gt;. So are the browser's dev tools, an old cached build, a request retried by a flaky network — and, in Saturdays' case, an entirely separate product: the DineGuru restaurant POS, which talks to the same backend over its own integration.&lt;/p&gt;

&lt;p&gt;Anything the frontend enforces, every one of those other callers skips. Client-side validation is for the person using the screen: fast feedback, fewer wasted round trips. It is not a control. An interface can only ever &lt;em&gt;hide&lt;/em&gt; an option; it cannot &lt;em&gt;stop&lt;/em&gt; a request.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule
&lt;/h2&gt;

&lt;p&gt;The line I drew is simple enough to apply without a meeting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If getting it wrong costs money or misleads a customer, the server decides and the client is told.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice that put a specific list of decisions in the domain layer rather than the interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prices and taxes&lt;/strong&gt; — computed from the order, never read from the request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment amounts&lt;/strong&gt; — the endpoint takes an order id, not a number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Charge waivers&lt;/strong&gt; — such as the dine-in charge waiver — decided by server rules, not a checkbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rider-assignment signals&lt;/strong&gt; — the customer sees a rider when the server has one, not when a timer guesses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery-time limits&lt;/strong&gt; — enforced where the order is created, not merely where it's displayed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One implementation per decision
&lt;/h2&gt;

&lt;p&gt;The rule only holds if each decision is made in exactly one place. Saturdays is sixteen Django domain apps, and the failure mode in a codebase that size isn't "nobody validates." It's &lt;em&gt;three slightly different&lt;/em&gt; validations in three views, drifting apart one bug fix at a time.&lt;/p&gt;

&lt;p&gt;So each decision lives in a service module that every caller goes through — the customer checkout, the partner portal, the POS integration, the payment webhook. When a tax rule changes, it changes once, and every path that reaches it gets the new behaviour on the same deploy.&lt;/p&gt;

&lt;p&gt;The frontend doesn't become dumb in this model. It still validates with typed schemas at the boundary, still renders sensible defaults, still hides what doesn't apply. It just stops being the thing anyone relies on for correctness.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Sort your rules into two piles. UX rules — formatting, hints, what's shown — belong in the client, where they make the product feel fast. Business rules — anything a malicious, stale or automated caller could exploit — belong on the server, in one place, called by everyone. The test isn't "does my app ever send a bad request?" It's "what happens when something that isn't my app does?"&lt;/p&gt;

&lt;p&gt;How that plays out across the whole platform — the order state machine, the payment gate, the POS boundary — is in the case study.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Case study:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays, a food delivery platform&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>backend</category>
      <category>react</category>
    </item>
    <item>
      <title>Never trust a payment webhook on its first word</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 13 Sep 2026 12:35:32 +0000</pubDate>
      <link>https://dev.to/divyakush/never-trust-a-payment-webhook-on-its-first-word-4ncm</link>
      <guid>https://dev.to/divyakush/never-trust-a-payment-webhook-on-its-first-word-4ncm</guid>
      <description>&lt;p&gt;&lt;strong&gt;A payment webhook is, structurally, a stranger sending your server a POST that says "this person paid." If your handler believes it, anyone who can reach that URL can mark orders as paid. In &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays&lt;/a&gt;, where PhonePe settles real money for food orders, the webhook handler is the most defensive code in the system — and every step in it exists because the step before it isn't enough.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The handler most tutorials give you
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;payment_webhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SUCCESS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_paid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Count the assumptions: that the request came from the gateway, that the body wasn't altered, that &lt;code&gt;SUCCESS&lt;/code&gt; means the money actually arrived, that the amount matches the order, that this event hasn't already been processed, and that nothing else is changing this order at the same moment. Every one of those is a separate way to lose money.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five steps, in this order
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Log the raw body before you parse it.&lt;/strong&gt; Not the parsed dict — the bytes. When a gateway disputes what it sent, or a parser throws on a malformed payload, the raw body is the only evidence you'll have. It costs one write and saves every investigation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Verify authentication.&lt;/strong&gt; Confirm the request really came from the gateway, using whatever the provider signs or authenticates with. A request that fails this isn't "a failed payment." It isn't a payment event at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Ask the gateway yourself.&lt;/strong&gt; Even an authentic webhook is a notification, not a ledger. Saturdays independently re-reads the payment status from the gateway's status API — and does it &lt;strong&gt;outside any database transaction&lt;/strong&gt;, because a network call can take seconds, and holding a row lock across it turns one slow response into a queue of blocked requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Lock, then re-check.&lt;/strong&gt; Only after that independent confirmation does the handler lock the order row and look again under the lock. Webhooks retry and can arrive concurrently, so the state you read before locking may already be stale. The re-check is what turns a duplicate delivery into a harmless no-op instead of a second fulfilment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Compare the amount — and fail loudly.&lt;/strong&gt; The confirmed amount is checked against the order's own stored total. A mismatch doesn't quietly succeed, and it doesn't silently fail either: it's flagged for manual review, because a payment that doesn't match its order is exactly the case a human needs to see.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rules underneath the sequence
&lt;/h2&gt;

&lt;p&gt;Two principles hold those steps together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The client never supplies the amount.&lt;/strong&gt; The endpoint that starts a payment takes an order id and computes the figure from the order's immutable total. The webhook then verifies against that same number. Nowhere in the flow is a price accepted from outside.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lock is ever held across a network call.&lt;/strong&gt; Confirm first, lock second. The same discipline covers refunds, which commit their pending state &lt;em&gt;before&lt;/em&gt; the gateway call goes out — so a crash mid-refund leaves a record that something was attempted, not a mystery.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Treat every inbound payment event as a claim to verify, not a fact to record. Keep the evidence, authenticate the sender, confirm with the source of truth, serialize the write, and make the mismatch case visible. None of it is clever. All of it is the difference between a checkout that works in a demo and one you can leave running with real money in it.&lt;/p&gt;

&lt;p&gt;The full payment path, and the order state machine it gates, are in the case study.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;See the build:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;www.divyakush.com/projects/saturdays&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>backend</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The notification that fired before the rollback</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 13 Sep 2026 12:25:31 +0000</pubDate>
      <link>https://dev.to/divyakush/the-notification-that-fired-before-the-rollback-2lol</link>
      <guid>https://dev.to/divyakush/the-notification-that-fired-before-the-rollback-2lol</guid>
      <description>&lt;p&gt;&lt;strong&gt;Here is a bug that passes every test you'll write for it. A customer's order is confirmed, the restaurant gets a "new order" ping, and a moment later the transaction that confirmed it rolls back. The restaurant is now cooking food for an order that doesn't exist. Nothing crashed. Two things that were supposed to happen together simply didn't. Building &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays&lt;/a&gt;, a food delivery platform where one order is shared by a customer, a restaurant, a rider and the platform itself, this was the class of bug I most wanted to make impossible.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two writes pretending to be one
&lt;/h2&gt;

&lt;p&gt;The naive version looks correct:&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;with&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;notify_restaurant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# HTTP call, queue publish, websocket push...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads like one unit of work. It isn't. The database write is transactional; the notification is not. They fail independently, and every combination is a production incident:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Notify succeeds, commit fails.&lt;/strong&gt; The restaurant hears about an order the database never kept.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit succeeds, notify fails.&lt;/strong&gt; The order is confirmed and nobody who has to act on it knows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notify fires while the transaction is still open.&lt;/strong&gt; A consumer reacting to the event reads the row and sees the &lt;em&gt;old&lt;/em&gt; state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moving the call after the &lt;code&gt;atomic()&lt;/code&gt; block fixes the first case and leaves the second fully in place. There is no ordering of two independent operations that makes them atomic. You have to turn them into one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write the intent, not the effect
&lt;/h2&gt;

&lt;p&gt;The transactional outbox does exactly that. Instead of sending the event, you &lt;em&gt;record&lt;/em&gt; it — in a table, in the same transaction as the state change that caused it:&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;with&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;OutboxEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order.confirmed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&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;order&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;public_id&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;Now the question "did we tell the restaurant?" has the same answer as "did the order get confirmed?" — because both are rows in one commit. Either both exist or neither does.&lt;/p&gt;

&lt;p&gt;A separate worker drains the table and does the actual delivery. In Saturdays that's a scheduled Celery task, and it's where all the unglamorous reliability lives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Attempt counts.&lt;/strong&gt; Every delivery try is recorded, so a flaky receiver is visible instead of silent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backoff.&lt;/strong&gt; A failing destination is retried on a widening interval, not hammered in a loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead-lettering.&lt;/strong&gt; After enough failures the event is parked for a human — not dropped, and not retried forever.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you're really buying
&lt;/h2&gt;

&lt;p&gt;The outbox doesn't make delivery instant, and it doesn't make it exactly-once. What it gives you is a guarantee that matters more: &lt;strong&gt;no event is ever sent for a state that didn't commit, and no committed state change is ever silently left unannounced.&lt;/strong&gt; The cost is a few seconds of delay. The alternative is a class of inconsistency you can't reproduce on your laptop and can't explain to a restaurant owner.&lt;/p&gt;

&lt;p&gt;It does push one responsibility onto the receiving side. Because a retry can deliver an event twice, consumers should be idempotent — handling the same &lt;code&gt;order.confirmed&lt;/code&gt; a second time must be a no-op. That's a far easier property to build than distributed atomicity, and it's the right trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Any time you see a database write and a network call in the same function, ask what happens if exactly one of them succeeds. If the answer is "someone acts on something that isn't true," the call doesn't belong there. Record the intent inside the transaction; deliver it afterwards.&lt;/p&gt;

&lt;p&gt;The rest of how Saturdays keeps four kinds of user in agreement about one order — the state machine, the payment path, the POS integration — is written up in the case study.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Read it:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/projects/saturdays" rel="noopener noreferrer"&gt;Saturdays — Food Delivery Platform&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>backend</category>
      <category>architecture</category>
      <category>python</category>
    </item>
    <item>
      <title>A completed foundation is a starting point, not a finish line</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 23 Aug 2026 18:59:10 +0000</pubDate>
      <link>https://dev.to/divyakush/a-completed-foundation-is-a-starting-point-not-a-finish-line-1d80</link>
      <guid>https://dev.to/divyakush/a-completed-foundation-is-a-starting-point-not-a-finish-line-1d80</guid>
      <description>&lt;p&gt;&lt;strong&gt;Completing a formal education in a field isn't the end of learning it — in something moving as fast as AI, it's barely the start. Having my Major in Artificial Intelligence conferred at IIT Ropar by Prof. Sudarshan Iyengar, and then spending a long conversation afterwards about what the field leaves open for the future, crystallized that for me. I wrote about it in &lt;a href="https://www.divyakush.com/insights/iit-ropar-complete" rel="noopener noreferrer"&gt;this reflection&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A completed foundation is a starting point, not a finish line
&lt;/h2&gt;

&lt;p&gt;There's a temptation to treat a credential as an arrival — the learning is done, the box is checked. In a field like AI, that mindset ages badly within months. The real value of a rigorous foundation isn't that it's complete; it's that it gives you the depth to keep learning the field intelligently as it changes.&lt;/p&gt;

&lt;p&gt;The conversation after the ceremony was, fittingly, not about what had been settled but about what the field leaves open — the unanswered parts, the direction of travel. That's the right frame for finishing any serious education: less "I now know this" and more "I'm now equipped to keep understanding this as it evolves."&lt;/p&gt;

&lt;h2&gt;
  
  
  What completion actually signals
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Depth enables adaptation.&lt;/strong&gt; A formal grounding is what lets you absorb the next development in a field with judgment instead of hype — you can tell what's genuinely new from what's repackaged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The best practitioners stay curious.&lt;/strong&gt; A conversation about open problems, at the moment of completion, is the healthy posture — the field's frontier matters more than its settled core.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning is a permanent posture.&lt;/strong&gt; Finishing a demanding program is best understood as building the capacity to keep going, not permission to stop.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Having the AI major conferred was a real milestone — but the most valuable thing I carried out of it was the orientation toward what comes next: treating a completed foundation as the platform for continuous learning in a field that will keep changing. The credential is the floor, not the ceiling.&lt;/p&gt;

&lt;p&gt;The full account, including that conversation, is on the page below.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Read it:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/insights/iit-ropar-complete" rel="noopener noreferrer"&gt;www.divyakush.com/insights/iit-ropar-complete&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>ai</category>
      <category>learning</category>
      <category>mindset</category>
    </item>
    <item>
      <title>The underrated professional trait: seeing a long thing through</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 23 Aug 2026 18:53:39 +0000</pubDate>
      <link>https://dev.to/divyakush/the-underrated-professional-trait-seeing-a-long-thing-through-5a9n</link>
      <guid>https://dev.to/divyakush/the-underrated-professional-trait-seeing-a-long-thing-through-5a9n</guid>
      <description>&lt;p&gt;&lt;strong&gt;Eighteen months is a long time to hold a commitment when the finish line is that far out and nothing forces you to keep going. Standing outside the hall in Rupnagar before the ceremony — in front of the banner for the Major in Artificial Intelligence, eighteen months after the first module opened — was a moment about one deeply underrated professional trait: the ability to see a long thing through. I set it down in &lt;a href="https://www.divyakush.com/insights/iit-ropar-convocation" rel="noopener noreferrer"&gt;this note&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Long commitments are where most people fall off
&lt;/h2&gt;

&lt;p&gt;Short bursts of intensity are common. Sustained commitment across many months, especially alongside everything else demanding your attention, is rare — and it's exactly what meaningful work requires. The interesting parts of a long program are the start and the end; the middle is just showing up, repeatedly, when novelty has worn off and no one is watching.&lt;/p&gt;

&lt;p&gt;Reaching the convocation for an eighteen-month AI major wasn't a single achievement so much as the sum of a hundred unremarkable days of continuing. That's the part worth respecting: not the ceremony, but the consistency that earned it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What seeing a long thing through teaches
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency outperforms intensity.&lt;/strong&gt; A steady standard held over eighteen months beats bursts of effort that don't last. Careers are built on the former.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The middle is the real test.&lt;/strong&gt; Anyone can be motivated at the beginning and the end. The professional trait that matters is continuing through the unglamorous middle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Completion compounds credibility.&lt;/strong&gt; Finishing a long, demanding commitment is evidence — to yourself and others — that you can be trusted with the next one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The convocation marked eighteen months of not stopping. I've come to believe that the capacity to sustain a commitment over a long horizon — to keep a standard through the quiet middle stretch — is one of the most valuable and least visible professional traits there is. It's what turns intention into a track record.&lt;/p&gt;

&lt;p&gt;The full note, taken outside the hall that day, is on the page below.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Read it:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/insights/iit-ropar-convocation" rel="noopener noreferrer"&gt;www.divyakush.com/insights/iit-ropar-convocation&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>discipline</category>
      <category>mindset</category>
    </item>
    <item>
      <title>End-to-end ownership is a different discipline from building a part</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 23 Aug 2026 18:48:08 +0000</pubDate>
      <link>https://dev.to/divyakush/end-to-end-ownership-is-a-different-discipline-from-building-a-part-4pd1</link>
      <guid>https://dev.to/divyakush/end-to-end-ownership-is-a-different-discipline-from-building-a-part-4pd1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Being shortlisted out of more than 400 teams at Smart India Hackathon 2025 came down to one thing more than any other: owning a system end to end. Not a component, not a slice — the whole architecture of an AI and IoT rockfall early-warning system, from the sensors in the field to the model to the alert. I wrote about what that scale of ownership taught me in &lt;a href="https://www.divyakush.com/insights/smart-india-hack" rel="noopener noreferrer"&gt;this reflection&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  End-to-end ownership is a different discipline
&lt;/h2&gt;

&lt;p&gt;There's a large gap between building a part well and being responsible for a whole system working. When you own the architecture end to end, no interface is someone else's problem — the sensors, the data pipeline, the model, and the way a warning finally reaches a human all have to fit together, and every seam between them is yours.&lt;/p&gt;

&lt;p&gt;Architecting the rockfall system across AI and IoT meant thinking about the entire chain: how noisy field data becomes a trustworthy signal, how the model's output becomes an actionable alert, how the pieces hold together under real conditions. Standing out among 400+ teams wasn't about one clever part — it was about the whole thing cohering.&lt;/p&gt;

&lt;h2&gt;
  
  
  What owning a full system teaches
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The seams are the system.&lt;/strong&gt; Individual components are the easy part; making them integrate reliably across two very different domains — machine learning and physical hardware — is where the real engineering lives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You design for the whole outcome.&lt;/strong&gt; End-to-end ownership forces you to keep the actual goal in view — an early warning someone can act on — rather than optimizing a piece in isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;National selection rewards coherence.&lt;/strong&gt; Being shortlisted at that scale reflects a system that works as a whole, which is a higher bar than any single impressive module.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Architecting a system end to end, across AI and IoT, taught me to think in whole outcomes and to treat the integration between parts as the core of the work rather than an afterthought. That systems-level ownership — being accountable for the entire chain, not a link in it — is exactly the capability serious engineering organizations are built around.&lt;/p&gt;

&lt;p&gt;The full account of the system and the selection is on the page below.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Read it:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/insights/smart-india-hack" rel="noopener noreferrer"&gt;www.divyakush.com/insights/smart-india-hack&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>systemdesign</category>
      <category>ai</category>
      <category>iot</category>
    </item>
    <item>
      <title>Finishing is a skill, and it is rarer than starting</title>
      <dc:creator>Divyakush Punjabi</dc:creator>
      <pubDate>Sun, 23 Aug 2026 18:42:36 +0000</pubDate>
      <link>https://dev.to/divyakush/finishing-is-a-skill-and-it-is-rarer-than-starting-5ff5</link>
      <guid>https://dev.to/divyakush/finishing-is-a-skill-and-it-is-rarer-than-starting-5ff5</guid>
      <description>&lt;p&gt;&lt;strong&gt;There's a half of engineering nobody photographs: the hour every deliverable is finally filed, every document is complete, and the unglamorous work of &lt;em&gt;finishing&lt;/em&gt; is actually done. It's not the demo or the award — it's the closeout. I marked that exact hour for my FPGA hardware project in &lt;a href="https://www.divyakush.com/insights/submissions-closed" rel="noopener noreferrer"&gt;this note&lt;/a&gt;, because that quiet moment is where a lot of real professional value lives.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finishing is a skill, and it's rarer than starting
&lt;/h2&gt;

&lt;p&gt;Starting is easy and exciting. Finishing — filing every deliverable, closing every loose end, meeting every requirement of a submission down to the last item — is neither, which is exactly why it separates people. Plenty of promising work never gets fully delivered because the last, tedious ten percent is where energy runs out.&lt;/p&gt;

&lt;p&gt;The hour the submissions went in wasn't glamorous, but it represented something professional work runs on: follow-through. The discipline to take a project all the way to genuinely complete, not just "basically working," is one of the most reliable markers of someone you can trust with real responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the closeout teaches
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Done means &lt;em&gt;done&lt;/em&gt;, including the paperwork.&lt;/strong&gt; In any serious setting, a deliverable isn't complete until every requirement around it is met — documentation, formatting, the lot. Treating that as part of the work, not an afterthought, is professionalism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The last ten percent is the test.&lt;/strong&gt; Anyone can carry a project through the interesting middle. Carrying it through the boring, exacting end is where reliability is proven.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quiet consistency compounds.&lt;/strong&gt; The moments nobody photographs — the diligent closeouts — are what build a reputation over time, far more than the visible highlights.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;I've learned to respect the unglamorous half of engineering as much as the visible half. Shipping isn't the moment of the demo; it's the moment every last deliverable is genuinely filed and the thing is complete. Being the person who reliably reaches that moment is worth more, professionally, than being the person with the flashiest start.&lt;/p&gt;

&lt;p&gt;The full note, taken outside the lab that hour, is on the page below.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Read it:&lt;/strong&gt; &lt;a href="https://www.divyakush.com/insights/submissions-closed" rel="noopener noreferrer"&gt;www.divyakush.com/insights/submissions-closed&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Divyakush Punjabi&lt;/strong&gt; — Full-Stack &amp;amp; AI Systems Engineer&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://www.divyakush.com" rel="noopener noreferrer"&gt;https://www.divyakush.com&lt;/a&gt;  ·  💼 &lt;a href="https://linkedin.com/in/divyakush-punjabi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;  ·  💻 &lt;a href="https://github.com/Divyakush2006" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>engineering</category>
      <category>discipline</category>
    </item>
  </channel>
</rss>
