<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Lokesh Naga Sai Darla's Blog]]></title><description><![CDATA[Lokesh Naga Sai Darla's Blog]]></description><link>https://lokeshdarla.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 16 Sep 2026 13:36:45 GMT</lastBuildDate><atom:link href="https://lokeshdarla.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Postman Is Not a Scratchpad]]></title><description><![CDATA[Postman is not a scratchpad. If you treat it like one, your API testing will never scale.
Most teams open Postman to hit an endpoint, check the response, and move on. Requests are named randomly, URLs are hardcoded, tokens are pasted by hand, and the...]]></description><link>https://lokeshdarla.hashnode.dev/postman-is-not-a-scratchpad</link><guid isPermaLink="true">https://lokeshdarla.hashnode.dev/postman-is-not-a-scratchpad</guid><dc:creator><![CDATA[Lokesh Naga Sai Darla]]></dc:creator><pubDate>Tue, 23 Dec 2025 18:19:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766513929723/d65f6fd4-9346-4fc5-b1a7-cee226f2a50f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Postman is not a scratchpad. If you treat it like one, your API testing will never scale.</p>
<p>Most teams open Postman to hit an endpoint, check the response, and move on. Requests are named randomly, URLs are hardcoded, tokens are pasted by hand, and there isn’t a single assertion in sight.</p>
<p>It works - until it doesn’t. And when it breaks, no one knows what changed, what failed, or why.</p>
<p>This “<em>just test it quickly</em>” mindset turns Postman into a disposable tool instead of what it actually is: an API testing and automation platform. A scratchpad doesn’t need structure. An engineering tool does. When your Postman collection has no environments, no shared auth, no reusable scripts, and no tests, you’re not validating APIs - you’re hoping they work.</p>
<p>This article is about drawing a hard line: when to stop using Postman like a scratchpad, and how to start using it like an engineering tool.</p>
<p>Here are <strong>actually useful Postman tips</strong>.</p>
<h3 id="heading-use-collection-level-auth">Use <strong>Collection-level auth</strong></h3>
<p>If you’re copy-pasting <em>Authorization</em> headers into every request, your Postman collection is already broken.</p>
<p>Set authentication once at <em>Collection → Authorization</em> and let all requests inherit it automatically. Store tokens in environment variables and reference them from the collection. When the token format or auth scheme changes, you update it in one place - not twenty.</p>
<p>Override auth at the request level only when you truly need to. Otherwise, it’s noise and a maintenance risk.</p>
<p><em>Auth belongs to the collection, not individual requests<strong>**.</strong></em> (Purely depends on the module you are testing)</p>
<h3 id="heading-never-hardcode-values-use-environments">Never Hardcode Values : <em>Use Environments</em></h3>
<p>Hardcoding base URLs, tokens, or IDs is the fastest way to make a Postman collection brittle. The moment you switch from local to staging or production, everything breaks.</p>
<p>Instead, use environment variables like:</p>
<pre><code class="lang-plaintext">{{baseUrl}}
{{token}}
{{userId}}
</code></pre>
<p>Each environment (dev, staging, prod) defines its own values, while the collection stays unchanged. Switching environments then becomes a dropdown change, not a manual edit across requests.</p>
<p>If changing environments requires touching requests, your collection isn’t reusable - it’s fragile.</p>
<h3 id="heading-always-add-basic-tests-even-for-manual-testing">Always Add Basic Tests : <em>Even for Manual Testing</em></h3>
<p>If a request has no tests, Postman can’t tell you when something breaks - you have to notice it yourself. That doesn’t scale.</p>
<p>At the very least, assert the status code:</p>
<pre><code class="lang-javascript">pm.test(<span class="hljs-string">"200 OK"</span>, <span class="hljs-function">() =&gt;</span> {
  pm.response.to.have.status(<span class="hljs-number">200</span>);
});
</code></pre>
<p>This turns a manual check into an automatic guardrail. The moment an endpoint starts returning the wrong status, Postman flags it immediately. Small tests like this catch backend regressions early, long before they reach production.</p>
<p><strong><em>For Example</em></strong>: You have a <code>GET /users/{id}</code> API. Yesterday it returned <code>200 OK</code>. Today, due to a backend change, it starts returning <code>500</code> for some users.</p>
<p>Now the moment the API returns anything other than <code>200</code>, the request fails visibly. In the Collection Runner or CI, the run stops and the regression is caught immediately.</p>
<h3 id="heading-use-pre-request-scripts-for-dynamic-data">Use Pre-request Scripts for Dynamic Data</h3>
<p>Hardcoded values don’t survive real testing. Anything that changes per request should be generated <em>before the request is sent</em>.</p>
<p>Pre-request scripts are ideal for:</p>
<ul>
<li><p>timestamps</p>
</li>
<li><p>nonces</p>
</li>
<li><p>random emails / IDs</p>
</li>
<li><p>request signatures (HMAC, hashes)</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="lang-javascript">pm.environment.set(<span class="hljs-string">"ts"</span>, <span class="hljs-built_in">Date</span>.now());
</code></pre>
<p>That value can then be reused in headers, query params, or the request body. This keeps requests deterministic while still behaving like real traffic.</p>
<p>If your API requires freshness or uniqueness and you’re typing values manually, you’re testing unrealistically.</p>
<h3 id="heading-use-post-request-tests-scripts-properly">Use Post-Request (Tests) Scripts Properly</h3>
<p>Post-request scripts run <strong>after</strong> the API responds. This is where real validation and chaining should happen, not manual testing.</p>
<p>Post scripts are used to:</p>
<ul>
<li><p>Assert status codes and response structure</p>
</li>
<li><p>Validate business rules</p>
</li>
<li><p>Extract values for the next request</p>
</li>
<li><p>Fail fast when contracts break</p>
</li>
</ul>
<p>Example: validate and extract data</p>
<pre><code class="lang-javascript">pm.test(<span class="hljs-string">"200 OK"</span>, <span class="hljs-function">() =&gt;</span> {
  pm.response.to.have.status(<span class="hljs-number">200</span>);
});

<span class="hljs-keyword">const</span> res = pm.response.json();
pm.environment.set(<span class="hljs-string">"userId"</span>, res.id);
</code></pre>
<p>This turns responses into inputs for the next step in the workflow. No copy-pasting. No guessing.</p>
<p>If your post scripts only log responses or don’t exist at all, Postman isn’t testing anything → it’s just showing you JSON.</p>
<h3 id="heading-name-requests-like-real-workflows">Name Requests Like Real Workflows</h3>
<p>Request names should tell a story, not just repeat HTTP paths.</p>
<p>This is unhelpful:</p>
<pre><code class="lang-javascript">GET /users
</code></pre>
<p>This is usable:</p>
<pre><code class="lang-javascript"><span class="hljs-number">01</span> - Login
<span class="hljs-number">02</span> - Create User
<span class="hljs-number">03</span> - Fetch User
</code></pre>
<p>Clear, ordered names make collections readable and runnable. They also matter in Collection Runner and CI, where execution order defines the workflow. If someone can’t understand the flow by scanning the request list, the collection isn’t doing its job.</p>
<h3 id="heading-use-examples-as-documentation">Use Examples as Documentation</h3>
<p>Postman examples are not just mock responses. Used properly, they become living documentation.</p>
<p>Well-maintained examples act as:</p>
<ul>
<li><p>API documentation for consumers</p>
</li>
<li><p>A contract reference for frontend and backend teams</p>
</li>
<li><p>An onboarding tool for new engineers</p>
</li>
</ul>
<p>Examples show what a <em>correct</em> request and response look like, without reading specs or code. If examples are outdated or missing, teams guess - and guessing breaks contracts.</p>
<p>Treat examples as part of the API surface. Keep them accurate, review them when APIs change, and maintain them with the same discipline as code.</p>
<h3 id="heading-import-openapi-then-clean-it">Import OpenAPI → Then Clean It</h3>
<p>Importing an OpenAPI spec into Postman is a great starting point, but it should never be the final state. Auto-generated collections are always messy: cryptic request names, flat structures, no flow, and zero tests.</p>
<p>Treat the import as scaffolding, not a finished product.</p>
<p>After importing:</p>
<ul>
<li><p>Rename requests to reflect real actions</p>
</li>
<li><p>Group related requests into meaningful flows</p>
</li>
<li><p>Add basic tests and shared scripts</p>
</li>
</ul>
<h3 id="heading-version-control-your-postman-collections">Version Control Your Postman Collections</h3>
<p>Postman doesn’t give you real, inbuilt version control and that’s fine. You’re still expected to handle it like engineers do.</p>
<p>Export your collections as JSON files, keep them inside your project repository, and treat them like any other code artifact:</p>
<ul>
<li><p>Export collections as JSON</p>
</li>
<li><p>Commit them to Git</p>
</li>
<li><p>Review changes in pull requests</p>
</li>
</ul>
<p>This gives you history, accountability, and team visibility. You can see when an endpoint changed, when a test was added, or when a breaking update slipped in.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Use Postman to encode how your APIs are supposed to behave, not just to check whether they respond. That shift → from clicking to testing → is what separates quick experiments from reliable systems.</p>
<p>Postman is not a scratchpad. Treat it like one, and you’ll keep shipping uncertainty. Treat it like an engineering tool, and it will start paying for itself.</p>
]]></content:encoded></item><item><title><![CDATA[Redis Isn’t Dirty - Your Read Replica Is]]></title><description><![CDATA[You deploy a write.You clear Redis.The very next read still returns old data.
So you do what everyone does: blame Redis.
Except Redis did exactly what you asked it to do.
The real bug is small and far more common in production systems: your read repl...]]></description><link>https://lokeshdarla.hashnode.dev/redis-isnt-dirty-your-read-replica-is</link><guid isPermaLink="true">https://lokeshdarla.hashnode.dev/redis-isnt-dirty-your-read-replica-is</guid><dc:creator><![CDATA[Lokesh Naga Sai Darla]]></dc:creator><pubDate>Sat, 20 Dec 2025 18:27:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766255775101/448d4195-10f7-41f8-931a-6e7041500f0f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You deploy a write.<br />You clear Redis.<br />The very next read still returns old data.</p>
<p>So you do what everyone does: blame Redis.</p>
<p>Except Redis did exactly what you asked it to do.</p>
<p>The real bug is <em>small</em> and far more <em>common</em> in production systems: <strong><em>your read replicas are the culprit, and they’re lying to your cache</em></strong>.</p>
<p>In systems with async replication, <strong><em>clearing the cache on write is not enough</em></strong>. The next read can hit a lagging replica, fetch stale data, and happily re-poison Redis with it. From that point on, even after the replica catches up, your cache is already wrong.</p>
<p>This isn’t a Redis problem.<br />It isn’t a race condition.<br />It’s a <strong>read-after-write consistency failure caused by replica lag</strong> — and most cache-aside implementations are vulnerable to it by default.</p>
<p>To understand why this happens, let’s first look at an important database concept: <strong>replicas</strong>.</p>
<h3 id="heading-replicas">Replicas</h3>
<p>Replication means copying the same data to multiple databases and having a <strong>Primary (Leader)</strong> that handles writes, and one or more <strong>Replicas (Followers)</strong> that handle reads.</p>
<p>All replicas contain the same data - <strong>eventually</strong>.</p>
<h3 id="heading-the-pitfall">The Pitfall</h3>
<p>While replicas improve read scalability and availability, they come with trade-offs:</p>
<ul>
<li><p>Writes still go to a single primary</p>
</li>
<li><p>Replication lag is unavoidable</p>
</li>
<li><p>Replica lag causes <strong>read-after-write inconsistency</strong></p>
</li>
</ul>
<p>When combined with cache invalidation, this lag can lead to <strong>stale data being re-cached</strong>, resulting in what looks like a <em>“<strong><strong>dirty Redis read</strong></strong>”</em> , even though Redis did nothing wrong.</p>
<h3 id="heading-what-you-can-do">What You Can Do</h3>
<p><strong>1. Read from PRIMARY after write (Read Your Writes)</strong></p>
<p>After a write, route reads for that entity to the primary.<br />Switch back to replicas only after some time or once consistency is guaranteed.</p>
<p><strong>2. Write-through cache (update cache on write)</strong><br />Instead of deleting the cache:</p>
<p>Write to the database<br />Update Redis with the new value</p>
<p>Even if replicas lag, the cache already contains correct data, so there’s no stale repopulation.</p>
<p>Trade-offs:<br />Increased write complexity<br />You must ensure the DB write succeeded before updating cach</p>
<p><strong>To Conclude this</strong></p>
<p>Replication scales reads.<br />Caching speeds reads.<br />Correctness is your responsibility.</p>
<p>Once you internalize that, this entire class of bugs disappear</p>
]]></content:encoded></item><item><title><![CDATA[Understanding TOTP (Time-based One-Time Passwords): The Backbone of Modern 2FA]]></title><description><![CDATA[In a world where security threats are constantly evolving, protecting user accounts is no longer optional — it’s essential. One of the most effective and user-friendly ways to secure applications is by adding Two-Factor Authentication (2FA), and at t...]]></description><link>https://lokeshdarla.hashnode.dev/understanding-totp-time-based-one-time-passwords-the-backbone-of-modern-2fa</link><guid isPermaLink="true">https://lokeshdarla.hashnode.dev/understanding-totp-time-based-one-time-passwords-the-backbone-of-modern-2fa</guid><category><![CDATA[General Programming]]></category><category><![CDATA[authentication]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Lokesh Naga Sai Darla]]></dc:creator><pubDate>Thu, 18 Sep 2025 17:46:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1758219691395/fdb3bc4c-ddf0-4593-a970-f69f6cbfac46.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In a world where security threats are constantly evolving, protecting user accounts is no longer optional — it’s essential. One of the most effective and user-friendly ways to secure applications is by adding <strong>Two-Factor Authentication (2FA)</strong>, and at the heart of most 2FA systems lies <strong>TOTP (Time-based One-Time Passwords)</strong>.</p>
<p>We’ll explore <strong>what TOTP is, how it works, and how you can implement it in your applications</strong>.</p>
<h3 id="heading-what-is-totp">What is TOTP?</h3>
<p><strong>TOTP</strong> stands for <strong>Time-based One-Time Password</strong>.</p>
<blockquote>
<p>It is an algorithm that generates a unique, temporary numeric code that expires after a short period of time (typically 30 seconds).</p>
</blockquote>
<p>You’ve likely seen it in action when using apps like <strong>Google Authenticator, Authy, or Microsoft Authenticator</strong>. These apps generate a 6-digit (or sometimes 8-digit) code that refreshes every 30 seconds, and you need to enter it to complete login.</p>
<p>This makes TOTP a core building block of <strong>Two-Factor Authentication (2FA)</strong>.</p>
<h3 id="heading-how-totp-works-step-by-step">How TOTP Works (Step-by-Step)</h3>
<p>At its core, TOTP is built on top of <strong>HOTP (HMAC-based One-Time Password)</strong>, with time being the moving factor instead of a counter.</p>
<ol>
<li><p><strong>Secret Key Generation</strong></p>
<ul>
<li><p>When a user enables 2FA, the server generates a secret key (a random base32 string).</p>
</li>
<li><p>This secret is shared with the user and stored securely on the server.</p>
</li>
<li><p>The user scans a QR code that contains the secret, adding it to their Authenticator app.</p>
</li>
</ul>
</li>
<li><p><strong>Code Generation (Client-Side)</strong></p>
<ul>
<li><p>Every 30 seconds, the app:</p>
<ul>
<li><p>Takes the current Unix timestamp (e.g., <code>T = floor(current_time / 30)</code>).</p>
</li>
<li><p>Combines it with the shared secret key.</p>
</li>
<li><p>Uses <strong>HMAC-SHA1</strong> to compute a hash.</p>
</li>
<li><p>Truncates it to a 6-digit number — this is the code shown to the user.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Code Verification (Server-Side)</strong></p>
<ul>
<li><p>When the user enters the code, the server repeats the same calculation.</p>
</li>
<li><p>If the code matches what the server expects (for the current time window), authentication succeeds.</p>
</li>
</ul>
</li>
</ol>
<p>The beauty of TOTP is that <strong>no internet connection is needed for the authenticator app</strong> — both client and server just need synchronized clocks. This value will change every 30 seconds, so even if someone steals your password, they’d still need this code — making attacks significantly harder.</p>
<h3 id="heading-why-use-totp">Why Use TOTP?</h3>
<ul>
<li><p><strong>Stronger Security</strong> – Passwords alone can be stolen or guessed. TOTP adds a dynamic factor.</p>
</li>
<li><p><strong>No Internet Required</strong> – Works offline once the secret is set.</p>
</li>
<li><p><strong>Widely Supported</strong> – Compatible with Google Authenticator, Authy, Microsoft Authenticator, etc.</p>
</li>
<li><p><strong>Easy to Implement</strong> – Libraries like <strong>Speakeasy</strong> (Node.js) or <strong>pyotp</strong> (Python) make it simple.</p>
</li>
</ul>
<h3 id="heading-implementing-totp-nodejs-example-using-speakeasy">Implementing TOTP : Node.js example using Speakeasy:</h3>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> speakeasy <span class="hljs-keyword">from</span> <span class="hljs-string">"speakeasy"</span>;
<span class="hljs-keyword">import</span> qrcode <span class="hljs-keyword">from</span> <span class="hljs-string">"qrcode"</span>;

<span class="hljs-comment">// Generate a secret for the user</span>
<span class="hljs-keyword">const</span> secret = speakeasy.generateSecret({ <span class="hljs-attr">length</span>: <span class="hljs-number">20</span> });
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Secret:"</span>, secret.base32);

<span class="hljs-comment">// Generate QR code to scan in Authenticator app</span>
qrcode.toDataURL(secret.otpauth_url, <span class="hljs-function">(<span class="hljs-params">err, data_url</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"QR Code URL:"</span>, data_url);
});

<span class="hljs-comment">// Verify token provided by user</span>
<span class="hljs-keyword">const</span> isVerified = speakeasy.totp.verify({
  <span class="hljs-attr">secret</span>: secret.base32,
  <span class="hljs-attr">encoding</span>: <span class="hljs-string">"base32"</span>,
  <span class="hljs-attr">token</span>: <span class="hljs-string">"123456"</span>, <span class="hljs-comment">// User input</span>
  <span class="hljs-attr">window</span>: <span class="hljs-number">1</span> <span class="hljs-comment">// Allows small time drift</span>
});

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Verified:"</span>, isVerified);
</code></pre>
<h3 id="heading-best-practices-for-totp">Best Practices for TOTP</h3>
<ul>
<li><p><strong>Keep Secrets Safe</strong> – Store them securely (e.g., encrypted in your database).</p>
</li>
<li><p><strong>Allow Small Time Drift</strong> – Users’ devices might be a few seconds off, allow ±1 time step.</p>
</li>
<li><p><strong>Offer Backup Codes</strong> – Users may lose their device; backup codes prevent lockouts.</p>
</li>
<li><p><strong>Educate Users</strong> – Clearly explain how to set up and use 2FA.</p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>TOTP is one of the simplest and most effective ways to add a second layer of security to your application. It is <strong>stateless, offline, and easy to implement</strong>, making it ideal for modern web and mobile applications.</p>
<p>If you’re building any system where user accounts matter — <strong>implementing TOTP-based 2FA should be a no-brainer</strong>. It will drastically improve your security posture and build trust with your users.</p>
]]></content:encoded></item></channel></rss>