<?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[Nandan Kumar's Blog | Software Engineer & Startup Consultant]]></title><description><![CDATA[Nandan Kumar's Blog | Software Engineer | Featured on BBC, Saudi Gazette, Times Of India | Tech Speaker | Mentor | Techie who "Hacked" an airline to retrieve his luggage]]></description><link>https://blog.nandan.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1662287805664/D9Nb8vZpE.png</url><title>Nandan Kumar&apos;s Blog | Software Engineer &amp; Startup Consultant</title><link>https://blog.nandan.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 05 Oct 2026 11:23:00 GMT</lastBuildDate><atom:link href="https://blog.nandan.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What I Built in the Last Year: A List of Failures]]></title><description><![CDATA[A list of failures.
That's the most accurate title I could come up with. Here's the summary:
In the last year or so, I have built 4 web apps, 3 open-source tools, wrote one book, onboarded 250+ users ]]></description><link>https://blog.nandan.dev/what-i-built-in-the-last-year-a-list-of-failures</link><guid isPermaLink="true">https://blog.nandan.dev/what-i-built-in-the-last-year-a-list-of-failures</guid><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Mon, 05 Oct 2026 01:51:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/628769bf89168247f55ee8b0/5656449c-e499-4e17-9674-dfe635acc07f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>A list of failures.</em></p>
<p>That's the most accurate title I could come up with. Here's the summary:</p>
<p>In the last year or so, I have built 4 web apps, 3 open-source tools, wrote one book, onboarded 250+ users overall, and made 25 dollars overall.</p>
<p>Read that sentence a few times. Eight projects and 250+ users went in, and 25 dollars came out. That isn't a monthly number or a per-project number. It's the total.</p>
<p>I'm not writing this to ask for sympathy, or to pretend it was all a learning experience. Some of it was. Mostly, I want the numbers written down in one place, because the gap between building something and getting someone to pay for it is easy to ignore when you only look at one project at a time.</p>
<h2>The web apps</h2>
<p>These are the ones where revenue was supposed to happen.</p>
<ol>
<li><p><a href="https://bailoutt.com"><strong>Bailoutt.com</strong></a> <strong>- Fake AI calls and call reminders - live ~200 users, 2 paid</strong></p>
<p>This is the closest thing I have to a success. About 200 people signed up, which is more than everything else combined. Two of them paid. Most of the 25 dollars came from these two people.</p>
</li>
<li><p><strong><a href="https://chunkybabies.com">https://chunkybabies.com</a> - Baby health tracker - 2 users, didn’t take off. Offline now</strong></p>
<p>Not much to say. Two users, no traction, and I shut it down. It's the only project on this list that no longer exists.</p>
</li>
<li><p><a href="https://kitaaben.com">Kitaaben</a> <strong>- Book tracker, journal and AI reviews. - 60+ users - 1 active subscriber</strong></p>
<p>Sixty-odd people use it to track what they read, and one person pays for it. That one subscriber is the reason it's still running, and I'm grateful to them.</p>
</li>
<li><p><a href="https://wobuddy.app">WO Buddy</a> <strong>- Build your exercise plan, with illustrated guide - 10 users - none paid</strong></p>
<p>Ten users, none paid.</p>
</li>
</ol>
<p>Put those side by side, and you get a funnel that starts wide at "built" and ends at a few coins at "paid."</p>
<h2>The open source tools</h2>
<p>I never expected these to make money. They're free, they solved my own problems, and a couple of them might solve yours.</p>
<ol>
<li><p><strong>GWDAP - Automate verification and adding aliases for multiple domains.</strong></p>
<p>Adding domain aliases in Google Workspace means verifying and adding each domain by hand. That's fine for one domain. It gets tedious and error-prone once you have several. GWDAP automates it.</p>
</li>
<li><p><strong>COPE agent - A multi-model content repurposing tool that turns any provided source into a canonical brief, which in turn converts it into multi-format content, i.e. long form like LinkedIn, short form like Twitter, etc., carousel, both image and and pdf</strong></p>
<p>You give it a source, like a messy text file. It distils the source into one brief and turns that brief into posts for different formats. This post started that way.</p>
</li>
<li><p><strong>DoCk - A browser extension that routes your Google Workspace links to their respective PWAs rather than opening a new tab for each link.</strong></p>
<p>I built it because I was tired of a Docs link opening yet another browser tab when I already had the installed app. It's small and narrow, and it does exactly that.</p>
</li>
</ol>
<h2>The book</h2>
<ol>
<li><p><strong>Dad, Debriefed - Journaled my experience as a father and turned it into a book that works as a guide to new fathers.</strong></p>
<p>This one is different from the rest. I didn't start with a market in mind. I started with a journal. The journal grew into a book, and the book became something I wish I'd had when I started.</p>
</li>
</ol>
<h2>So where does that leave things?</h2>
<p>Here's the scoreboard: 8 projects, 250+ users, $25.</p>
<p>You can read that a few ways. Turns out I'm good at shipping. Getting anyone to pay is a separate skill, and the list shows I haven't learned it yet. Every one of these went live. Most of them found at least a handful of people. Almost none of them found people with a reason to pay.</p>
<p>A few things I'm taking from the year:</p>
<ol>
<li><p><strong>Shipping is not the bottleneck.</strong> I can build things. That clearly isn't what's holding me back.</p>
</li>
<li><p><strong>Users are not customers.</strong> Bailoutt has about 200 users and 2 paying ones. That ratio says a lot about what I've actually validated.</p>
</li>
<li><p><strong>One paying subscriber counts.</strong> Kitaaben's single subscriber tells me more than its 60+ free users do.</p>
</li>
<li><p><strong>Killing a project is fine.</strong> Chunkybabies is offline, and nothing broke.</p>
</li>
<li><p><strong>Not everything has to make money.</strong> The open source tools and the book were worth doing anyway. I just shouldn't confuse them with a business.</p>
</li>
</ol>
<p>So it's a list of failures, by the only number that pays rent. By the count of things that exist now and didn't a year ago, the year went better than that number suggests. Both readings are true. I'm just not going to pretend the second one cancels out the first.</p>
]]></content:encoded></item><item><title><![CDATA[Dock: Stops Letting Google Meet Links Open a New Window Every Time]]></title><description><![CDATA[Three Workspaces, infinite windows
I have three Google Workspaces: personal Gmail, my own Workspace, and my company's. That means three separate PWAs for Mail, three for Meet, three for Calendar, and ]]></description><link>https://blog.nandan.dev/dock-stops-letting-google-meet-links-open-a-new-window-every-time</link><guid isPermaLink="true">https://blog.nandan.dev/dock-stops-letting-google-meet-links-open-a-new-window-every-time</guid><category><![CDATA[Google Workspace]]></category><category><![CDATA[Google Meet]]></category><category><![CDATA[chrome extension]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[ocd ]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Fri, 25 Sep 2026 07:46:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/628769bf89168247f55ee8b0/71d7b2d1-ef0c-415a-a9e4-6eace0d6106b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Three Workspaces, infinite windows</h3>
<p>I have three Google Workspaces: personal Gmail, my own Workspace, and my company's. That means three separate PWAs for Mail, three for Meet, three for Calendar, and so on down the line.</p>
<p>Here's the thing: every time a Meet link landed in my chat, it opened a brand new window. Not focused an existing one — opened a new one. Multiply that by three accounts and a normal day of meetings, and you end up with a wall of Meet windows and duplicate Gmail tabs you can't tell apart. It got really hard to manage.</p>
<h3>What I built</h3>
<p>So I built Dock. If you click a link and there's already a matching PWA open, Dock redirects you to that window instead of spawning a new one. Gmail, Meet, Calendar, Drive, Docs, Sheets, Slides, or Chat — same idea for all of them. Holding Shift bypasses it for that one click, for the times you actually want a fresh window.</p>
<p>Dock also helps me manage all my open tabs. Say I've got six Meet windows open across accounts — I can close any of them, for any account, from any screen. It keeps everything decluttered, the mental load is reduced. You don't need five mailboxes open just to check personal email once. One mailbox for personal, one for work, one for the side Workspace — or whatever your split looks like.</p>
<p>And I gave it a cute mascot: Meet Doc-k</p>
<img src="https://cdn.hashnode.com/uploads/covers/628769bf89168247f55ee8b0/f2c1b0a5-a6bc-49f0-a00c-0420a963c906.png" alt="" style="display:block;margin:0 auto" />

<h3>Getting the account right</h3>
<p>The tricky part isn't catching the link. It's knowing <em>which</em> account's window to send you to. Dock routes to the window for the account the link actually names — via <code>authuser=</code>, <code>/u/N/</code>, or a stored mapping — and it keeps a window's identity correct even after you switch accounts from inside the PWA itself. It reuses the already-open window for that account instead of spawning a duplicate.</p>
<h3>The switcher</h3>
<p>Press Ctrl/Cmd+Shift+Space and you get a fuzzy-searchable popup listing every app window, grouped by account. Live calls are surfaced first. One-click close, per window or per whole account.</p>
<p>A few other things it does:</p>
<ul>
<li><p>Each login gets an automatically assigned, editable colour tag, shown on the toolbar icon and in the switcher.</p>
</li>
<li><p>You can add your own custom rules for apps Dock doesn't know about — Figma, Notion, whatever — with a match pattern, target behaviour, and account strategy.</p>
</li>
<li><p>Built-in Google Workspace rules can be disabled but not deleted.</p>
</li>
<li><p>Rules import/export as JSON.</p>
</li>
</ul>
<h3>No network requests</h3>
<p>It makes no network requests — nothing it does ever leaves your device. All the routing logic runs locally against windows Chrome already knows about.</p>
<h3>Try it</h3>
<p>I haven't put it in the Chrome Store yet. Not sure I will. So clone the repo, build it yourself, and load it as an unpacked extension: <a href="https://github.com/sirius93/dock">github.com/sirius93/dock</a>.</p>
<p>That's the whole idea, and so far it's worked wonders for me. If it works for you too, star the repo or leave feedback — and if enough people find it worth the hassle, I'll look at making it live.</p>
]]></content:encoded></item><item><title><![CDATA[Introducing COPE: Create Once Post Everywhere]]></title><description><![CDATA[COPE: Create Once Post Everywhere
So here's the thing I've been building. It's called COPE — Create Once Post Everywhere — and it's exactly what it sounds like: write your thought down once, let an ag]]></description><link>https://blog.nandan.dev/introducing-cope-create-once-post-everywhere</link><guid isPermaLink="true">https://blog.nandan.dev/introducing-cope-create-once-post-everywhere</guid><category><![CDATA[ai agents]]></category><category><![CDATA[claude]]></category><category><![CDATA[codex]]></category><category><![CDATA[content creation]]></category><category><![CDATA[Content Repurposing]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Fri, 18 Sep 2026 06:53:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/628769bf89168247f55ee8b0/896e0f46-c479-4189-8d62-24c022ae7a52.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>COPE: Create Once Post Everywhere</h1>
<p>So here's the thing I've been building. It's called COPE — Create Once Post Everywhere — and it's exactly what it sounds like: write your thought down once, let an agent turn it into content for every platform.</p>
<h2>What it actually is</h2>
<p>COPE is an agent that runs on Claude or Codex. You give it a rough text note — voice-to-text ramble, half-formed idea, whatever — or even a URL, like one of your own blog posts, and it repurposes that into a full set of platform-ready outputs: a LinkedIn post, an X thread, a blog version, images, and a carousel PDF.</p>
<p>It also generates threaded tweets or posts, so if you've got something meaty to say, it can spread it across multiple posts instead of cramming it into one.</p>
<h2>How you use it</h2>
<p>There's no hosted app or dashboard here. You download the repo from GitHub and run it inside Claude or Codex directly. From there you feed it your note or a URL for context, and it does the repurposing.</p>
<h2>Where the outputs go</h2>
<p>Once COPE generates everything, here's the flow:</p>
<ul>
<li><p>Carousel images → post to Instagram</p>
</li>
<li><p>Carousel PDF → post to LinkedIn</p>
</li>
<li><p>Short-form content → post to Threads or X</p>
</li>
</ul>
<h2>The current limitation</h2>
<p>Automatic posting is not supported yet. That's a real limitation right now — you still have to manually post everything COPE generates. It handles the repurposing, not the publishing.</p>
<h2>Best part</h2>
<p>Best part about it, it is open source. It's hosted on my GitHub at github.com/sirius93/cope, so anyone can pull it down, poke at it, or extend it.</p>
<h2>Try it</h2>
<p>If you've got a rough note sitting in a text file or a blog post you want to squeeze more mileage out of, download the repo from <a href="http://github.com/sirius93/cope">github.com/sirius93/cope</a> and run it inside Claude or Codex. See what it turns your mess into.</p>
]]></content:encoded></item><item><title><![CDATA[Bulk-Adding Google Workspace Domain Aliases Without Service-Account Keys]]></title><description><![CDATA[Managing one domain in Google Workspace is straightforward. Managing ten, twenty, or more domains across several brands is where the process starts to hurt.
For every domain, an administrator has to p]]></description><link>https://blog.nandan.dev/bulk-adding-google-workspace-domain-aliases-without-service-account-keys</link><guid isPermaLink="true">https://blog.nandan.dev/bulk-adding-google-workspace-domain-aliases-without-service-account-keys</guid><category><![CDATA[Google Workspace]]></category><category><![CDATA[Python]]></category><category><![CDATA[dns]]></category><category><![CDATA[godaddy]]></category><category><![CDATA[automation]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Sun, 16 Aug 2026 06:35:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/628769bf89168247f55ee8b0/2e42deb1-e52f-4ad2-82e4-e4bcacddb1de.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Managing one domain in Google Workspace is straightforward. Managing ten, twenty, or more domains across several brands is where the process starts to hurt.</p>
<p>For every domain, an administrator has to prove ownership, publish a DNS record, wait for propagation, return to Google Workspace, add the domain, configure mail routing, and verify the result. None of those steps is especially difficult. The problem is repetition — and repetition is where small mistakes become outages.</p>
<p>I recently needed a better workflow for attaching a collection of brand domains to one Google Workspace account. The domains used GoDaddy DNS, and the intended behavior was simple: every existing Workspace user should receive the same username on every brand domain.</p>
<p>That made the domains good candidates for Google Workspace user alias domains. It also made the process a good candidate for automation. The result is a CSV-driven Python CLI with three commands — <code>plan</code>, <code>apply</code>, <code>verify</code> — that connects the Google Workspace Admin SDK, the Site Verification API, and the GoDaddy DNS API, authenticates with OAuth instead of a service-account key, and treats every write as something to check for before doing. Code's open source at <a href="https://github.com/sirius93/google-workspace-domain-alias-provisioner">github.com/sirius93/google-workspace-domain-alias-provisioner</a>, link at the bottom too.</p>
<h2>The problem with doing it manually</h2>
<p>The manual workflow usually looks like this:</p>
<ol>
<li><p>Open the Google Admin console.</p>
</li>
<li><p>Add a domain alias.</p>
</li>
<li><p>Copy Google's verification token.</p>
</li>
<li><p>Open the DNS provider.</p>
</li>
<li><p>Create the TXT record.</p>
</li>
<li><p>Wait for DNS propagation.</p>
</li>
<li><p>Ask Google to verify the domain.</p>
</li>
<li><p>Configure MX records.</p>
</li>
<li><p>Repeat for the next domain. Do this once and it feels harmless. Do it twenty times and several risks appear:</p>
</li>
</ol>
<ul>
<li><p>A domain is skipped.</p>
</li>
<li><p>A TXT value is copied incorrectly.</p>
</li>
<li><p>The wrong parent domain is selected.</p>
</li>
<li><p>Existing MX records are replaced accidentally.</p>
</li>
<li><p>The spreadsheet and the real configuration drift apart.</p>
</li>
<li><p>Nobody has a reliable record of what succeeded. The goal was not merely to make the process faster. The goal was to make it repeatable, inspectable, and safe to rerun.</p>
</li>
</ul>
<h2>Domain alias or secondary domain?</h2>
<p>Before automating anything, it is important to choose the correct Google Workspace domain model.</p>
<p>A user alias domain gives existing users and groups equivalent addresses on another domain. If <code>brand.example</code> is an alias of <code>company.example</code>, then:</p>
<pre><code class="language-plaintext">alex@company.example  -&gt; alex@brand.example
sales@company.example -&gt; sales@brand.example
</code></pre>
<p>Both addresses point to the existing account or group. Users continue signing in with their primary address, and there is no separate mailbox.</p>
<p>A secondary domain is different. It is intended for a separate collection of users whose primary accounts live on that domain. Secondary-domain users normally require their own Workspace licences.</p>
<p>The automation described here is specifically for user alias domains. That keeps the mapping predictable: one parent Workspace domain, many brand domains, and matching usernames across all of them.</p>
<p>Google documents the distinction and current domain limits in its multiple-domain guidance.</p>
<h2>Turning the domain list into a source of truth</h2>
<p>The first useful improvement is surprisingly mundane: put the desired state in a CSV file.</p>
<pre><code class="language-csv">domain,type,parent_domain,dns_provider
brand-one.example,domain_alias,company.example,godaddy
brand-two.example,domain_alias,company.example,godaddy
brand-three.example,domain_alias,company.example,godaddy
</code></pre>
<p>This file becomes the input to every operation. It is easy to review, version, split into pilot batches, and compare with the actual environment.</p>
<p>The script validates that:</p>
<ul>
<li><p>Every domain is unique.</p>
</li>
<li><p>Every row has a parent domain.</p>
</li>
<li><p>The requested type is <code>domain_alias</code>.</p>
</li>
<li><p>The DNS provider is supported. That validation catches boring mistakes before an API call turns them into an operational problem.</p>
</li>
</ul>
<h2>The automation flow</h2>
<p>The workflow connects three APIs:</p>
<ul>
<li><p><strong>Google Site Verification API</strong> generates a DNS verification token and confirms ownership.</p>
</li>
<li><p><strong>GoDaddy Domains API v3</strong> reads the zone and creates missing TXT or MX records.</p>
</li>
<li><p><strong>Google Workspace Admin SDK Directory API</strong> creates and inspects domain aliases. For each CSV row, the tool performs this sequence:</p>
</li>
</ul>
<ol>
<li><p>Check whether the domain is already verified.</p>
</li>
<li><p>Check whether the Workspace alias already exists.</p>
</li>
<li><p>Read the current GoDaddy DNS records.</p>
</li>
<li><p>Request a Google verification token when needed.</p>
</li>
<li><p>Add the token as an apex TXT record if it is missing.</p>
</li>
<li><p>Poll public DNS until the token is visible.</p>
</li>
<li><p>Ask Google to verify ownership.</p>
</li>
<li><p>Create the Workspace domain alias under the configured parent.</p>
</li>
<li><p>Optionally add Google MX when — and only when — the domain has no MX records. The read-before-write behavior is essential. A rerun should continue an incomplete job, not create duplicates.</p>
</li>
</ol>
<h2>Why the CLI has plan, apply, and verify</h2>
<p>Infrastructure tools are much easier to trust when inspection and mutation are separate operations.</p>
<p>The CLI exposes three commands:</p>
<pre><code class="language-bash">python workspace_domain_aliases.py plan domains.csv
python workspace_domain_aliases.py apply domains.csv
python workspace_domain_aliases.py verify domains.csv
</code></pre>
<h3>plan</h3>
<p><code>plan</code> is read-only. It checks Google and GoDaddy, then prints a compact summary:</p>
<pre><code class="language-plaintext">DOMAIN                           DNS      VERIFIED  WORKSPACE  MX
brand-one.example                ok       no        missing    0
brand-two.example                ok       yes       missing    5
brand-three.example              ok       no        present    1
</code></pre>
<p>This answers the questions that matter before a change: Can the tool access the DNS zone? Does Google already recognize the owner? Does the Workspace alias already exist? Is mail already configured?</p>
<h3>apply</h3>
<p><code>apply</code> creates missing ownership records, verifies domains, and creates missing Workspace aliases.</p>
<pre><code class="language-bash">python workspace_domain_aliases.py apply domains.csv
</code></pre>
<p>DNS propagation is asynchronous, so the tool polls for the verification TXT record. If the record does not become visible before the timeout, the run reports the domain as pending. Nothing needs to be rolled back: wait, then rerun the same command.</p>
<h3>verify</h3>
<p><code>verify</code> reconciles the CSV with the live state, checking ownership, alias presence, parent-domain association, and whether the domain has at least one MX record. A non-zero exit code makes it usable in a shell script or a future CI workflow.</p>
<h2>Authentication without service-account keys</h2>
<p>My first instinct was to use a service account with domain-wide delegation. That is a common design for unattended Workspace administration, but the organization enforced this policy:</p>
<pre><code class="language-plaintext">iam.managed.disableServiceAccountKeyCreation
</code></pre>
<p>That was not a problem to work around. It was a useful security boundary.</p>
<p>Long-lived service-account JSON keys are portable credentials. If one leaks into a repository, backup, support ticket, or developer machine, it can be abused until revoked. Google recommends avoiding service-account keys when a safer authentication method fits the workload.</p>
<p>For a locally operated administrative tool, interactive OAuth is a good match. The application uses an OAuth 2.0 Desktop app client and asks the administrator to authorize two scopes:</p>
<pre><code class="language-plaintext">https://www.googleapis.com/auth/admin.directory.domain
https://www.googleapis.com/auth/siteverification
</code></pre>
<p>The Python flow is conceptually simple:</p>
<pre><code class="language-python">from google_auth_oauthlib.flow import InstalledAppFlow
 
scopes = [
    "https://www.googleapis.com/auth/admin.directory.domain",
    "https://www.googleapis.com/auth/siteverification",
]
 
flow = InstalledAppFlow.from_client_secrets_file(
    "oauth-client.json",
    scopes,
)
credentials = flow.run_local_server(port=0)
</code></pre>
<p>The first run opens a browser. The administrator signs in and approves access. The resulting refresh token is stored locally and reused on later runs.</p>
<p>That token must still be treated like a password. It should never be committed, pasted into an issue, or stored beside public example files.</p>
<h2>Connecting to Google Workspace</h2>
<p>Once OAuth credentials are available, the Python client can construct both Google services:</p>
<pre><code class="language-python">from googleapiclient.discovery import build
 
directory = build(
    "admin",
    "directory_v1",
    credentials=credentials,
    cache_discovery=False,
)
 
verification = build(
    "siteVerification",
    "v1",
    credentials=credentials,
    cache_discovery=False,
)
</code></pre>
<p>Creating a domain alias uses the Directory API:</p>
<pre><code class="language-python">directory.domainAliases().insert(
    customer="my_customer",
    body={
        "domainAliasName": "brand.example",
        "parentDomainName": "company.example",
    },
).execute()
</code></pre>
<p>Before inserting, the tool lists existing aliases and compares the live parent domain with the CSV. If an alias already exists under a different parent, the script stops instead of guessing how to repair it.</p>
<h2>Automating domain verification</h2>
<p>Google ownership verification has two distinct operations: obtain a token and verify the resource.</p>
<p>The request describes an internet domain and asks for DNS verification:</p>
<pre><code class="language-python">result = verification.webResource().getToken(
    body={
        "site": {
            "identifier": "brand.example",
            "type": "INET_DOMAIN",
        },
        "verificationMethod": "DNS",
    }
).execute()
 
token = result["token"]
</code></pre>
<p>The token is published as an apex TXT record. Once it is visible in public DNS, the tool asks Google to verify the resource:</p>
<pre><code class="language-python">verification.webResource().insert(
    verificationMethod="DNS",
    body={
        "site": {
            "identifier": "brand.example",
            "type": "INET_DOMAIN",
        }
    },
).execute()
</code></pre>
<p>Keeping these steps separate makes DNS propagation explicit. A successful API response from the DNS provider does not mean every resolver can see the record immediately.</p>
<h2>Adding records through GoDaddy safely</h2>
<p>GoDaddy's v3 DNS API supports individual record creation:</p>
<pre><code class="language-plaintext">POST /v3/domains/zones/{zone}/dns-records
</code></pre>
<p>A verification record looks like this:</p>
<pre><code class="language-json">{
  "type": "TXT",
  "name": "@",
  "data": "google-site-verification=...",
  "ttl": 600
}
</code></pre>
<p>The safe pattern is:</p>
<ol>
<li><p>List existing records of the relevant type.</p>
</li>
<li><p>Normalize record names and values.</p>
</li>
<li><p>Create the record only when an exact match is absent.</p>
</li>
<li><p>Never replace the entire DNS zone. This matters because DNS APIs differ. Some "set records" methods replace every record with a matching name and type. Others can replace the whole zone. A provisioning tool should use the narrowest operation available.</p>
</li>
</ol>
<p>GoDaddy also warns that record creation is not inherently idempotent: blindly replaying a failed POST can create duplicates. Reading the zone before each write significantly reduces that risk.</p>
<h2>Why MX changes are opt-in</h2>
<p>Verification TXT records are additive and relatively low-risk. MX records are different: they control where inbound email is delivered.</p>
<p>The tool therefore does not touch MX during the default <code>apply</code> operation. Mail configuration requires an explicit flag:</p>
<pre><code class="language-bash">python workspace_domain_aliases.py apply domains.csv --add-google-mx
</code></pre>
<p>Even with the flag, the tool adds <code>smtp.google.com</code> only when the domain has zero MX records. If any MX record already exists, it prints a warning and leaves the configuration untouched.</p>
<p>That conservative decision handles several real-world cases: the domain already uses Google's older five-record MX configuration, the domain uses Google's newer single-record configuration, another mail provider is still active, or the domain has intentional routing or migration records.</p>
<p>Automation should not interpret "different from my preferred configuration" as "safe to overwrite."</p>
<p>SPF, DKIM, and DMARC are also intentionally outside the first version. Each requires knowledge of the organization's complete mail-sending environment. For example, adding a second SPF record can make SPF invalid rather than more secure.</p>
<h2>Making reruns safe</h2>
<p>Bulk administrative work rarely completes in one flawless pass. DNS might be slow, one domain might belong to another account, an API can rate-limit a request, or an administrator might pause the rollout.</p>
<p>The tool is designed so that rerunning <code>apply</code> is normal:</p>
<ul>
<li><p>Existing TXT token: skip creation.</p>
</li>
<li><p>Already verified domain: skip verification.</p>
</li>
<li><p>Existing Workspace alias with correct parent: skip insertion.</p>
</li>
<li><p>Existing MX records: leave unchanged.</p>
</li>
<li><p>Pending DNS: retry later. This is the practical meaning of idempotency for the workflow. The entire run may involve APIs whose individual POST operations are not idempotent, but the program checks the current state before deciding whether a write is necessary.</p>
</li>
</ul>
<h2>A rollout strategy that minimizes surprises</h2>
<p>For a real environment, I would use the following sequence:</p>
<ol>
<li><p>Export or back up every DNS zone.</p>
</li>
<li><p>Put one non-critical domain in a pilot CSV.</p>
</li>
<li><p>Run <code>plan</code>.</p>
</li>
<li><p>Inspect any existing MX records.</p>
</li>
<li><p>Run <code>apply</code> without the MX flag.</p>
</li>
<li><p>Confirm the alias in the Google Admin console.</p>
</li>
<li><p>Test receiving mail after the approved MX change.</p>
</li>
<li><p>Confirm outbound authentication separately.</p>
</li>
<li><p>Expand to small batches.</p>
</li>
<li><p>Run <code>verify</code> and keep the output with the change record. The pilot is not ceremony. It confirms that the OAuth project, administrator privileges, Workspace tenant, GoDaddy account, authoritative nameservers, and API behavior all match the assumptions in the code.</p>
</li>
</ol>
<h2>What I would add next</h2>
<p>The first release solves one narrow problem well. Natural next steps include:</p>
<ul>
<li><p>DNS-provider adapters for Cloudflare, Route 53, TransIP, and others</p>
</li>
<li><p>Secondary-domain support</p>
</li>
<li><p>JSON output for CI and audit systems</p>
</li>
<li><p>Unit tests with mocked API responses</p>
</li>
<li><p>Explicit DNS backup and rollback workflows</p>
</li>
<li><p>SPF, DKIM, and DMARC validation without automatic enforcement</p>
</li>
<li><p>A keyless workload-identity design for approved unattended execution Any future provider should preserve the same safety contract: read before write, create only what is missing, never replace a zone implicitly, and require explicit intent for destructive actions.</p>
</li>
</ul>
<h2>Lessons learned</h2>
<p>Three ideas mattered more than the API calls themselves.</p>
<p>First, model the desired state before writing code. The CSV is small, but it turns an informal checklist into something reviewable and repeatable.</p>
<p>Second, treat security controls as design input. A blocked service-account key did not require disabling an organization policy. It led to an authentication flow better suited to a human-operated CLI.</p>
<p>Third, make dangerous behavior inconvenient. The tool can automate MX creation, but only through an explicit flag, and it refuses to modify existing mail routing. A little friction is valuable when a command can affect production email.</p>
<p>That's usually the sweet spot for internal automation. I've open sourced it — if you're wiring up your own multi-brand Workspace setup, the CLI and the client wrappers are there to fork.</p>
<h2>Resources</h2>
<ul>
<li><p><strong>Source code:</strong> <a href="https://github.com/sirius93/ggwdap">github.com/sirius93/ggwdap</a> — plan/apply/verify CLI, CSV schema, and the Google + GoDaddy client wrappers described above.</p>
</li>
<li><p>Google Workspace Admin SDK Directory API</p>
</li>
<li><p>Google Site Verification API</p>
</li>
<li><p>Google Directory API Python quickstart</p>
</li>
<li><p>Google Workspace multiple-domain guidance</p>
</li>
<li><p>GoDaddy DNS API guide</p>
</li>
<li><p>Google Cloud authentication guidance</p>
</li>
</ul>
<hr />
<p><em>This project is not affiliated with or endorsed by Google or GoDaddy. DNS and mail-routing changes can disrupt production services; test with a non-critical domain and keep current DNS backups.</em></p>
]]></content:encoded></item><item><title><![CDATA[How I Moved My Google AI Studio App to GitHub CI ]]></title><description><![CDATA[Kitaaben started life in Google AI Studio. No shame in that — it's genuinely the fastest way I know to get a full-stack app with Firebase wired up and live. But "fast to start" and "where your code sh]]></description><link>https://blog.nandan.dev/moving-google-ai-studio-app-to-github-ci</link><guid isPermaLink="true">https://blog.nandan.dev/moving-google-ai-studio-app-to-github-ci</guid><category><![CDATA[google ai studio]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[book]]></category><category><![CDATA[google cloud]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Sun, 12 Jul 2026 05:59:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/628769bf89168247f55ee8b0/f1429be5-56ed-463e-85e1-dd6d13779c27.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Kitaaben started life in Google AI Studio. No shame in that — it's genuinely the fastest way I know to get a full-stack app with Firebase wired up and live. But "fast to start" and "where your code should live forever" are two different things.</p>
<p>I wanted GitHub to be the source of truth. Real commit history. Real pull requests. A deploy pipeline I could reason about without a proprietary UI sitting in the middle. The service was already live on Cloud Run, so the plan was simple: point it at the GitHub repo and be done by lunch.</p>
<p>It took the whole afternoon. Just not the afternoon I expected.</p>
<h2>The plan that should have worked</h2>
<p>Cloud Run has a built-in feature for exactly this — <em>Continuously deploy from a repository</em>. Connect a GitHub repo, pick a branch, and it auto-generates a Cloud Build trigger that builds and deploys on every push. No YAML. Click, connect, push.</p>
<p><strong>Snag 1.</strong> The very first build failed, but for a boring reason. My repo had two lockfiles — <code>package-lock.json</code> and a stray <code>pnpm-lock.yaml</code> that got committed by accident at some point. Google's Buildpacks saw the pnpm lockfile and built with pnpm. And recent pnpm versions block dependency install scripts by default as a supply-chain protection. So esbuild's postinstall script (the one that downloads its platform binary) never ran, and the build died before it even reached my code.</p>
<p>Fix: delete the stray lockfile. The project was never pnpm-based anyway. Quick, forgettable, not the story.</p>
<h2>The error that refused to explain itself</h2>
<p>Next push got further — all the way to the deploy step — and then this:</p>
<pre><code class="language-plaintext">ERROR: (gcloud.run.services.update)
spec.template.metadata.annotations[run.googleapis.com/sources]:
Source annotation has sources that are not referenced by a container.
</code></pre>
<p>Read that sentence a few times. Nothing in it tells you what to do. No stack trace, no file, no line number. Just a Cloud Run validator rejecting a deploy against its own service definition.</p>
<h3>Wrong theory #1: strip the annotation</h3>
<p>The obvious read — some stale annotation is sitting on the live service, left over from however AI Studio originally provisioned it. So just remove it:</p>
<pre><code class="language-plaintext">$ gcloud run services update kitaaben --region us-west1 \
    --remove-annotations=run.googleapis.com/sources
 
ERROR: unrecognized arguments: --remove-annotations=run.googleapis.com/sources
(did you mean '--remove-env-vars'?)
</code></pre>
<p>Turns out <code>gcloud run services update</code> has no generic "edit any annotation" flag. Most of Cloud Run's raw Knative-style metadata isn't meant to be hand-edited through convenience flags — precisely to stop people from doing what I was about to do.</p>
<h3>Wrong theory #2: ditch Buildpacks entirely</h3>
<p>New theory. The <code>run.googleapis.com/sources</code> annotation is part of Cloud Run's source-deploy tracking — it maps source paths to containers so the console can show "this revision was built from commit X." A plain prebuilt Docker image needs none of that. So: write a Dockerfile, sidestep Buildpacks and its source-tracking machinery completely.</p>
<p>I wrote a multi-stage Dockerfile. Built it locally, ran it, confirmed it booted and served traffic. Pushed.</p>
<p>And this is where it got genuinely confusing. The build went green. The live site didn't change.</p>
<p>The build log explained why. Adding a Dockerfile made Cloud Build quietly switch strategies. Instead of the four-step Buildpacks template it had been using (build → pull → push → deploy), it fell back to Google's generic "there's a Dockerfile here, build and push an image" default, which has <strong>no deploy step at all</strong>. Green build. No deploy. Nobody tells you.</p>
<p>Fix for this part: a handwritten <code>cloudbuild.yaml</code> with three explicit steps. <code>docker build</code>, <code>docker push</code>, <code>gcloud run deploy --image</code>. Three commands, in order, in the repo. No more guessing what the pipeline does.</p>
<h3>Same error, completely different deploy method</h3>
<p>With a real deploy step finally in place, I pushed again — and got the <em>exact same annotation error</em>. This time from a plain <code>gcloud run deploy --image</code> call that had nothing to do with source builds or Buildpacks at all.</p>
<p>That was the moment the real shape of the problem clicked. It was never about <em>how</em> I was deploying. Buildpacks, Dockerfile, source-based, image-based — didn't matter. Something was wrong with the live service's own persisted state, and every deploy method was stepping on the same landmine.</p>
<h2>Exporting the body</h2>
<p>Time to look at what the service actually thought it was running:</p>
<pre><code class="language-plaintext">$ gcloud run services describe kitaaben --region us-west1 \
    --format export &gt; service.yaml
</code></pre>
<p>Buried in that YAML: the container's <code>image</code> field wasn't a real image reference. It was the literal string <code>scratch</code> — Cloud Run's internal placeholder for services that resolve their actual container indirectly, through that same <code>sources</code> annotation I'd been fighting all afternoon.</p>
<p>The service was <em>born</em> this way. AI Studio provisioned it through its own source-deploy pipeline on day one, and it had been carrying that placeholder ever since. A ghost container, quietly broken since birth, and it only mattered the day I tried to deploy any other way.</p>
<p>So every attempt was failing one of two ways: either the deploy tried to update the sources annotation and Cloud Run rejected the mismatch, or the leftover <code>scratch</code> placeholder had nothing left to resolve against.</p>
<h2>The actual fix</h2>
<p>Fix both in one edit, apply atomically:</p>
<pre><code class="language-plaintext"># in service.yaml:
#   1. delete the run.googleapis.com/sources annotation line
#   2. change image: scratch to the real image path
 
$ gcloud run services replace service.yaml
Applying new configuration to Cloud Run service [kitaaben]...
Deploying... done.
</code></pre>
<p>That was it. Not a flag. Not a build strategy. Not a YAML rewrite of the pipeline. A genuinely stuck field in the service's own state that no amount of retrying — from any deploy method — was ever going to clear.</p>
<h2>If you're doing this migration</h2>
<ul>
<li><p>If AI Studio (or any source-deploy tool) originally provisioned your Cloud Run service, don't assume a clean deploy method inherits a clean service. <code>describe --format export</code> and <em>read the spec</em> before you spend a day guessing.</p>
</li>
<li><p><code>run.googleapis.com/sources</code> and <code>image: scratch</code> travel together. See one, check for the other.</p>
</li>
<li><p>Cloud Build silently changes strategy based on what's in your repo. Dockerfile present vs absent produces different pipelines. If a build goes green but nothing deploys, check whether the deploy step still exists.</p>
</li>
<li><p>Not every Cloud Run field has a convenience flag. When <code>--remove-X</code> doesn't exist, the fallback is always: export the spec, hand-edit, <code>services replace</code>. Haven't touched the pipeline since. It's a Cloud Build trigger watching <code>main</code>, building a Docker image, deploying it. No mystery annotations.</p>
</li>
</ul>
<p>GitHub is the source of truth now. AI Studio is where <a href="https://kitaaben.com">Kitaaben</a> was born, not where it lives.</p>
<p>That’s all, folks! I hope you found this helpful. If you enjoyed this, check out more articles on my Blog, <a href="https://blog.nandan.dev/"><strong>https://blog.nandan.dev/</strong></a></p>
<p>Feel free to comment, email me at <a href="mailto:connect@nandan.dev"><strong>connect@nandan.dev</strong></a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a href="https://www.instagram.com/nandandotdev"><strong>Instagram</strong></a> | <a href="https://github.com/sirius93"><strong>Github</strong></a> | <a href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[I Got Tired of Goodreads. So I Built Kitaaben.com]]></title><description><![CDATA[I read a lot. Not as much as I'd like to, but enough that I needed a place to keep track of it all.
For years, Goodreads was that place. Then Amazon bought it in 2013 and... nothing happened. No meani]]></description><link>https://blog.nandan.dev/i-got-tired-of-goodreads-so-i-built-kitaaben-com</link><guid isPermaLink="true">https://blog.nandan.dev/i-got-tired-of-goodreads-so-i-built-kitaaben-com</guid><category><![CDATA[books]]></category><category><![CDATA[#BookReview]]></category><category><![CDATA[bookstagram]]></category><category><![CDATA[book review]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Wed, 24 Jun 2026 15:57:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/628769bf89168247f55ee8b0/3c426301-2da0-4903-adc1-1bfd487707d4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I read a lot. Not as much as I'd like to, but enough that I needed a place to keep track of it all.</p>
<p>For years, Goodreads was that place. Then Amazon bought it in 2013 and... nothing happened. No meaningful update. No design refresh. No new features worth talking about. Just a bloated, slow, social-feed-first app that cared more about what your friends were reading than what you actually wanted from a reading tracker.</p>
<p>So I stopped waiting and built my own. It's called <a href="https://kitaaben.com">Kitaaben</a>.</p>
<hr />
<h2>What Even Is Kitaaben?</h2>
<p>Kitaaben is a private bookshelf tracker. That's it, honestly.</p>
<p>You sign in with Google, add your books, and start tracking. No followers, no public profile, no algorithm telling you what to read next. Just your books, organized the way you want them.</p>
<p>I built it as a React SPA, backed by Firestore for real-time sync and Google OAuth for auth. It works as a PWA too, so you can install it directly on your phone or desktop and it behaves like a native app. Even logs notes offline and syncs when you're back online.</p>
<p>The stack is deliberately simple. I didn't want to over-engineer a reading tracker.</p>
<hr />
<h2>The Features That Actually Matter</h2>
<p><strong>Reading Progress Tracking</strong></p>
<p>You can log how many pages you've read, mark a book as "Reading", "To Read", or "Completed", and watch your progress bar move. Small thing, weirdly satisfying.</p>
<p><strong>Yearly Reading Goals</strong></p>
<p>Set a target at the start of the year. Kitaaben shows you a clean gauge of where you stand. No gamification, no streaks, no push notifications guilt-tripping you into reading. Just the number.</p>
<p><strong>The AI Review Assistant</strong></p>
<p>This one is my favourite. You know how you finish a book, you have all these raw thoughts, and then you sit down to write a review, and your mind goes blank?</p>
<p>Kitaaben fixes that. You log rough chapter notes as you read. When you're done, one click and the AI turns your messy notes into a proper, readable review. It uses Gemini under the hood, server-side, so nothing weird is happening with your data.</p>
<p><strong>Beautiful Bookshelf UI</strong></p>
<p>You can pick colour palettes for your book spines, attach local photos, or link directly to publisher dust jackets. Your shelf actually looks good. Goodreads shelf looks like it was designed in 2009 because it was.</p>
<p><strong>Cloud Sync + Offline PWA</strong></p>
<p>Everything syncs in real-time to Firestore. Works across your phone, tablet, and desktop. Add notes on a flight, they'll sync when you land.</p>
<p><strong>Your Data Is Yours</strong></p>
<p>No corporate sponsors. No ads. No one is analysing your reading habits to sell you something. The free tier is genuinely free, forever. If you want AI reviews and premium themes, there's an Elite plan, but the core is completely free.</p>
<hr />
<h2>Kitaaben vs Goodreads — Let's Be Honest</h2>
<table>
<thead>
<tr>
<th></th>
<th>Goodreads</th>
<th>Kitaaben</th>
</tr>
</thead>
<tbody><tr>
<td>Last major update</td>
<td>Years ago</td>
<td>Actively built</td>
</tr>
<tr>
<td>Social feed</td>
<td>Yes, and you can't remove it</td>
<td>No. Just your books.</td>
</tr>
<tr>
<td>Ads</td>
<td>Yes</td>
<td>Never</td>
</tr>
<tr>
<td>Data privacy</td>
<td>Amazon owns everything</td>
<td>Private, encrypted, yours</td>
</tr>
<tr>
<td>AI reviews</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Offline support</td>
<td>No</td>
<td>Yes (PWA)</td>
</tr>
<tr>
<td>Design</td>
<td>Dated</td>
<td>Clean, warm, actually nice</td>
</tr>
<tr>
<td>Free tier</td>
<td>Yes</td>
<td>Yes</td>
</tr>
</tbody></table>
<p>Goodreads isn't bad because it was badly built. It's bad because it stopped being built. Amazon has no incentive to fix it. It already has 150 million users who have nowhere to go.</p>
<p>Kitaaben is built for readers who are tired of that and just want something that works, looks good, and doesn't treat your reading life as a data point.</p>
<hr />
<h2>Who Is It For?</h2>
<p>Honestly? People like me.</p>
<p>If you read regularly and want a clean private place to track it, Kitaaben will feel like a breath of fresh air. If you rely on Goodreads mainly for its social features — seeing what friends are reading, community reviews, that sort of thing — Kitaaben isn't trying to replace that. It's not a social network. It never will be.</p>
<p>It's a personal reading sanctuary. That's the whole point.</p>
<hr />
<h2>Try It</h2>
<p><a href="https://kitaaben.com">kitaaben.com</a> — Sign in with Google, takes about 60 seconds to set up.</p>
<p>If you have feedback, brutal or otherwise, reach me at <a href="mailto:namastay@kitaaben.com">namastay@kitaaben.com</a> or find me on Twitter. I'm building this in the open and I actually read every message.</p>
<p>Thanks, Nandan</p>
]]></content:encoded></item><item><title><![CDATA[How to Convert Nested Objects to Flat Objects in JavaScript.]]></title><description><![CDATA[I am sure you have been asked this question in one of the interviews.
It looks simple at first. But the interviewer is actually checking whether you understand recursion, objects, and how JavaScript t]]></description><link>https://blog.nandan.dev/how-to-convert-nested-objects-to-flat-objects-in-javascript</link><guid isPermaLink="true">https://blog.nandan.dev/how-to-convert-nested-objects-to-flat-objects-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Objects]]></category><category><![CDATA[json]]></category><category><![CDATA[#js-interview]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Wed, 04 Mar 2026 00:59:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/628769bf89168247f55ee8b0/be8581d3-50f8-4780-915e-60b50525be70.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am sure you have been asked this question in one of the interviews.</p>
<p>It looks simple at first. But the interviewer is actually checking whether you understand recursion, objects, and how JavaScript treats arrays.</p>
<p>So basically, you are given a nested object, and you are expected to simplify it by removing the nesting and printing it in a non-nested format.</p>
<h3>Here is a simple input and output :</h3>
<pre><code class="language-javascript">const input = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
};
</code></pre>
<h3>Output:</h3>
<pre><code class="language-javascript"> {  a: 1, 
    b_c: 2, 
    b_d_e: 3, 
 }
</code></pre>
<h3>Now look at the solution :</h3>
<pre><code class="language-javascript">function flatten(obj, prefix = "", res = {}) {
    // Loop through every key in the object
    for (let key in obj) {

        // Skip properties coming from prototype chain
        if (!obj.hasOwnProperty(key)) return;

        // If prefix exists, append current key using _
        // Otherwise just use the key itself
        let objKey = prefix ? `\({prefix}_\){key}` : key;

        let value = obj[key];

        // If value is an object and not null,
        // recursively flatten it
        if (typeof value === 'object' &amp;&amp; value !== null) {
            flatten(value, objKey, res);
        } else {
            // If it's a primitive value, assign it directly
            res[objKey] = value;
        }
    }

    // Return the accumulated result
    return res;
}
</code></pre>
<p>Let us test it.</p>
<pre><code class="language-javascript">const input = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: [1, 2]
    }
  }
};

console.log(flatten(input));

//  {a: 1, b_c: 2, b_d_e_0: 1, b_d_e_1: 2 }
</code></pre>
<p>Now observe something interesting.</p>
<p>It flattens the nested object correctly.</p>
<p>But it also destructures the array and adds indexes as keys.</p>
<p><em>Why do you think that happened?</em></p>
<div>
<div>💡</div>
<div>As you know, everything in JavaScript is an object. Even <strong>Arrays</strong>.</div>
</div>

<p>So when we check:</p>
<pre><code class="language-javascript">typeof value === 'object'
</code></pre>
<p>Arrays also satisfy that condition.</p>
<p>That means our recursive call is going inside arrays as well, and treating indexes (0, 1) like object keys.</p>
<p>That is why we are getting:</p>
<pre><code class="language-javascript">b_d_e_0: 1
b_d_e_1: 2
</code></pre>
<h3><strong>So, how do we fix it?</strong></h3>
<p>Simple.</p>
<p>We add one more condition to ignore Arrays.</p>
<pre><code class="language-javascript">function flatten(obj, prefix = "", res = {}) {
    for (let key in obj) {

        // Ensure we are working only with object's own properties
        if (!obj.hasOwnProperty(key)) return;

        let objKey = prefix ? `\({prefix}_\){key}` : key;
        let value = obj[key];

        // Only recurse if:
        // 1. value is an object
        // 2. value is not null
        // 3. value is NOT an array
        if (
            typeof value === 'object' &amp;&amp;
            value !== null &amp;&amp;
            !Array.isArray(value)
        ) {
            flatten(value, objKey, res);
        } else {
            // Directly assign primitives and arrays
            res[objKey] = value;
        }
    }

    return res;
}
</code></pre>
<h2><strong>Testing Both Cases</strong></h2>
<pre><code class="language-javascript">const input1 = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: [1, 2]
    }
  }
};

const input2 = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
};

console.log(flatten(input1));
console.log(flatten(input2));

// { a: 1, b_c: 2, b_d_e: [ 1, 2 ] }
// { a: 1, b_c: 2, b_d_e: 3 }
</code></pre>
<p>That is it.</p>
<p>A simple recursion problem. But it tests whether you truly understand:</p>
<ul>
<li><p>How objects work</p>
</li>
<li><p>How arrays behave internally</p>
</li>
<li><p>How recursion accumulates results</p>
</li>
<li><p>And how small edge cases can break your logic</p>
</li>
</ul>
<p>This is why interviewers love this question.</p>
<p>That’s all, folks! I hope you found this helpful. If you enjoyed this, check out more articles on my Blog, <a href="https://blog.nandan.dev/"><strong>https://blog.nandan.dev/</strong></a></p>
<p>Feel free to comment, email me at <a href="mailto:connect@nandan.dev"><strong>connect@nandan.dev</strong></a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a href="https://www.instagram.com/nandandotdev"><strong>Instagram</strong></a> | <a href="https://github.com/sirius93"><strong>Github</strong></a> | <a href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[HTML Pitfalls and Gotchas Every Developer Should Avoid]]></title><description><![CDATA[HTML is easy, said every developer ever.
HTML is deceptively simple. You open a tag, you close a tag, and something appears on screen. But beneath that simplicity lurk gotchas that can silently break your layout, wreck your SEO, or make your site loo...]]></description><link>https://blog.nandan.dev/html-pitfalls-and-gotchas-every-developer-should-avoid</link><guid isPermaLink="true">https://blog.nandan.dev/html-pitfalls-and-gotchas-every-developer-should-avoid</guid><category><![CDATA[HTML5]]></category><category><![CDATA[Meta]]></category><category><![CDATA[Meta Tags]]></category><category><![CDATA[gotchas]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Fri, 05 Sep 2025 04:23:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757046158565/25b1173b-f99c-4e7e-b343-558b1b387698.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><mark>HTML is easy, said every developer ever.</mark></p>
<p>HTML is deceptively simple. You open a tag, you close a tag, and something appears on screen. But beneath that simplicity lurk gotchas that can silently break your layout, wreck your SEO, or make your site look unprofessional when shared on social media.</p>
<p>In this world of frameworks and libraries, it’s easy to miss some of the basic stuff that needs to be taken care of in your webpage, because your framework’s boilerplate code adds up most of the stuff and takes care of the basics. But it’s always good to know some of them that are important or aren’t added by your framework by default.</p>
<p>Here are the critical HTML pitfalls to avoid and how to fix them immediately.</p>
<h2 id="heading-1-forgetting-the-doctype-declaration">1. Forgetting the DOCTYPE Declaration</h2>
<p>Skipping the doctype triggers something called as the <strong>quirks mode</strong>, where browsers start behaving like it's 1999. This results in inconsistent CSS rendering and unpredictable JavaScript behaviour across browsers.</p>
<p><strong>Always declare it at the very top of your HTML file:</strong></p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
</code></pre>
<h2 id="heading-2-missing-the-character-encoding-meta-tag">2. Missing the Character Encoding Meta Tag</h2>
<p>Without proper character encoding, special characters turn into garbled symbols i.e. (�). This breaks accented letters, smart quotes, and emojis, and it makes your webpage content look broken and unprofessional.</p>
<p><strong>Place this inside the</strong> <code>&lt;head&gt;</code> tag:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
</code></pre>
<h2 id="heading-3-forgetting-the-viewport-meta-tag">3. Forgetting the Viewport Meta Tag</h2>
<p>Has it ever happened to you? Your page zooms awkwardly when users double-tap on mobile; That’s because you're missing the viewport meta tag. Without it, your site isn't truly responsive, regardless of your CSS.</p>
<p><strong>Add this to your</strong> <code>&lt;head&gt;</code> <strong>section</strong>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
</code></pre>
<h2 id="heading-4-skipping-open-graph-meta-tags">4. Skipping Open Graph Meta Tags</h2>
<p>Ever shared your blog on LinkedIn, Twitter, or Facebook only to see a bare URL with no preview card? That's because Open Graph tags are missing. These tags control how your content appears when shared on social media.</p>
<p><strong>Add these OG tags for rich sharing previews:</strong></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:title"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"My Blog Title"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"A quick guide to avoiding HTML gotchas."</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:image"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"https://example.com/preview.png"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:url"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"https://example.com/blog"</span>&gt;</span>
</code></pre>
<p><strong>Pro tip:</strong> Also consider adding Twitter Card tags for optimal Twitter/X previews.</p>
<h2 id="heading-5-ignoring-content-security-policy">5. Ignoring Content Security Policy</h2>
<p>Content Security Policy (CSP) helps prevent cross-site scripting (XSS) attacks and other code injection vulnerabilities. It tells browsers which scripts, styles, and resources are allowed to run on your page.</p>
<p><strong>Start with a basic CSP meta tag:</strong></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"Content-Security-Policy"</span> 
      <span class="hljs-attr">content</span>=<span class="hljs-string">"default-src 'self'; script-src 'self' 'unsafe-inline';"</span>&gt;</span>
</code></pre>
<p><strong>Note:</strong> This is a starting point. Adjust the policy based on your specific needs and third-party resources.</p>
<h2 id="heading-6-improper-nesting-of-elements">6. Improper Nesting of Elements</h2>
<p>Browsers are forgiving, but invalid HTML nesting leads to unpredictable rendering and can break JavaScript that relies on the DOM structure.</p>
<p><strong>Common nesting mistakes:</strong></p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Inline elements shouldn't contain block elements --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Wrong!<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-comment">&lt;!-- Paragraphs can't contain divs --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Also wrong!<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p><strong>Follow these nesting rules:</strong></p>
<ul>
<li><p>Inline elements (<code>&lt;span&gt;</code>, <code>&lt;a&gt;</code>, <code>&lt;strong&gt;</code>) should <strong>never</strong> contain block-level elements (<code>&lt;div&gt;</code>, <code>&lt;section&gt;</code>, <code>&lt;p&gt;</code>)</p>
</li>
<li><p><code>&lt;p&gt;</code> Tags should only wrap inline content, never block-level elements</p>
</li>
<li><p>When in doubt, check the HTML specification for content models</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>These six HTML gotchas might seem minor, but they're the difference between a professional website and one that breaks in subtle, embarrassing ways. Take five minutes to audit your HTML templates—your future self (and your users) will thank you.</p>
<p><strong>Quick checklist for your next project:</strong></p>
<ul>
<li><p>DOCTYPE declaration at the top</p>
</li>
<li><p>UTF-8 character encoding</p>
</li>
<li><p>Viewport meta tag for mobile</p>
</li>
<li><p>Open Graph tags for social sharing</p>
</li>
<li><p>Basic Content Security Policy</p>
</li>
<li><p>Valid HTML nesting</p>
</li>
</ul>
<p>Remember: browsers are forgiving, but that doesn't mean we should be sloppy. Clean HTML is the foundation of a reliable web experience.</p>
<p>That’s all, folks! I hope you found this helpful. If you enjoyed this, check out more articles on my Blog, <a target="_blank" href="https://blog.nandan.dev/">https://blog.nandan.dev/</a></p>
<p>Feel free to comment, email me at <a target="_blank" href="http://mailto:connect@nandan.dev/"><strong>connect@nandan.dev</strong></a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.instagram.com/nandandotdev"><strong>Instagram</strong></a> | <a target="_blank" href="https://github.com/sirius93"><strong>Github</strong></a> | <a target="_blank" href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Front-End Developer Interviews: The Crucial Aspect of Accessibility]]></title><description><![CDATA[Let me start with this: Accessibility is not a buzzword; it’s a crucial feature that every web application should have.
It should not be mandated by the government, but rather it should be taken as the responsibility of every developer to make their ...]]></description><link>https://blog.nandan.dev/front-end-developer-interviews-the-crucial-aspect-of-accessibility</link><guid isPermaLink="true">https://blog.nandan.dev/front-end-developer-interviews-the-crucial-aspect-of-accessibility</guid><category><![CDATA[Accessibility]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[frontend]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Sat, 09 Aug 2025 16:57:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754758620254/32fa9796-0a0b-4228-943b-e2f20303e2a2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let me start with this: Accessibility is not a buzzword; it’s a crucial feature that every web application should have.</p>
<p>It should not be mandated by the government, but rather it should be taken as the responsibility of every developer to make their websites accessible to all users.</p>
<p>Making a website accessible will not just help you avoid government penalties, it will also give you a wider user base. Accessibility is all about making your website accessible to everyone.</p>
<p>On that note, let’s get this blog started -</p>
<h2 id="heading-what-is-accessibility">What is Accessibility</h2>
<p>Accessibility is ensuring that your web application is usable by people with disabilities, including people with</p>
<ul>
<li><p>Visual impairment (Blindness, Colour Blindness) -</p>
</li>
<li><p>Hearing loss</p>
</li>
<li><p>Motor Impairment ( Difficulty using the mouse)</p>
</li>
<li><p>Cognitive limitation (Dyslexia, Attention Disorder)</p>
</li>
</ul>
<h2 id="heading-accessibility-core-principle-pour">Accessibility Core Principle (POUR)</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Principle</strong></td><td><strong>Description</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>P</strong>erceivable</td><td>Content must be perceivable by all (e.g., alt text, captions)</td></tr>
<tr>
<td><strong>O</strong>perable</td><td>All functionality must work via keyboard or assistive devices</td></tr>
<tr>
<td><strong>U</strong>nderstandable</td><td>Content should be readable and predictable</td></tr>
<tr>
<td><strong>R</strong>obust</td><td>Must work with various assistive technologies</td></tr>
</tbody>
</table>
</div><h2 id="heading-how-can-people-with-disabilities-use-the-applications">How can people with disabilities use the applications</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Visual impairment</td><td>Should be able to use with screen readers, VoiceOver, and Font Scaling support</td></tr>
</thead>
<tbody>
<tr>
<td>Hearing loss</td><td>Close Captions for Video and Audio</td></tr>
<tr>
<td>Motor Impairment</td><td>Keyboard Accessibility Support</td></tr>
<tr>
<td>Cognitive limitation</td><td>Simple Language</td></tr>
</tbody>
</table>
</div><h2 id="heading-accessibility-standards">Accessibility Standards</h2>
<h3 id="heading-web-content-accessibility-guidelines-wcag">Web Content Accessibility Guidelines (WCAG)</h3>
<p>Defines Accessibility levels as <strong>A</strong>, <strong>AA</strong> &amp; <strong>AAA</strong></p>
<p>Read More: <a target="_blank" href="https://www.w3.org/WAI/standards-guidelines/wcag/">https://www.w3.org/WAI/standards-guidelines/wcag/</a></p>
<h3 id="heading-accessible-rich-internet-applications-aria"><strong>Accessible Rich Internet Applications (ARIA)</strong></h3>
<p>Adds accessibility support to non-semantic elements.</p>
<p>Read More: <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label">https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/</a></p>
<h3 id="heading-americans-with-disabilities-act-ada">Americans with Disabilities Act (ADA)</h3>
<p>Legal mandate in the US for accessible digital content</p>
<p>Read More: <a target="_blank" href="https://www.ada.gov/">https://www.ada.gov/</a></p>
<blockquote>
<p><strong>AA</strong> is the most commonly required level of compliance.</p>
</blockquote>
<h2 id="heading-how-to-make-a-web-application-accessible">How to make a web application Accessible</h2>
<h3 id="heading-use-semantic-tags">Use Semantic Tags</h3>
<ul>
<li><p>Use tags like <code>&lt;button&gt;</code> , <code>&lt;nav&gt;</code>, <code>&lt;header&gt;</code>, <code>&lt;section&gt;</code> etc.</p>
</li>
<li><p>Avoid using div and span all over your application</p>
</li>
</ul>
<h3 id="heading-support-keyboard-accessibility">Support Keyboard Accessibility</h3>
<ul>
<li><p>Actions should be usable via. keyboard, including opening modals, dropdowns, and toasts.</p>
</li>
<li><p>Use <code>tabindex</code> to align focus correctly.</p>
</li>
<li><p>Use focus, keydown event listeners correctly</p>
</li>
</ul>
<h3 id="heading-use-aria-roles-when-needed">Use Aria Roles when needed</h3>
<ul>
<li><p>Use aria-label, aria-hidden, role="dialog", etc., when native semantics are not sufficient</p>
</li>
<li><p><a target="_blank" href="https://w3c.github.io/aria/">https://w3c.github.io/aria/</a></p>
</li>
</ul>
<h3 id="heading-focus-management">Focus Management</h3>
<ul>
<li><p>Trap focus in modals</p>
</li>
<li><p>Restore focus when modals close</p>
</li>
<li><p>Visibly indicate focus using focus-visible or outline</p>
</li>
</ul>
<h3 id="heading-maintain-colour-contrast">Maintain Colour Contrast</h3>
<ul>
<li><p>4.5: 1 for normal text in <strong>AA</strong> Standard</p>
</li>
<li><p>7:2 for <strong>AAA</strong></p>
</li>
<li><p>Check contrast: <a target="_blank" href="https://webaim.org/resources/contrastchecker/">https://webaim.org/resources/contrastchecker/</a></p>
</li>
</ul>
<h3 id="heading-using-images-and-other-media">Using Images and Other Media</h3>
<ul>
<li><p>Use alt text for images</p>
</li>
<li><p>Add transcripts/captions for videos/audio</p>
</li>
<li><p>Avoid autoplaying media files. i.e. Video</p>
</li>
</ul>
<h2 id="heading-tools-for-accessibility-testing">Tools for Accessibility Testing</h2>
<p>The tools listed below are some of the most common tools for testing accessibility in a web application.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Tool</strong></td><td><strong>Use Case</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>axe DevTools</strong></td><td>Chrome extension to audit a11y</td></tr>
<tr>
<td><strong>Lighthouse</strong></td><td>Built-in browser audits</td></tr>
<tr>
<td><strong>NVDA / VoiceOver</strong></td><td>Screen reader testing</td></tr>
<tr>
<td><strong>Tab key</strong></td><td>Test keyboard navigation</td></tr>
</tbody>
</table>
</div><blockquote>
<p>WCAG <strong>AAA is harder to implement, and most website are expected to maintain AA compliant.</strong></p>
</blockquote>
<p>I hope by the end of this post, you have a good idea of why accessibility is important and how you can add accessibility support to your application.</p>
<p>That’s all, folks! I hope you found this helpful. If you enjoyed this, check out more articles on my Blog, <a target="_blank" href="https://blog.nandan.dev/">https://blog.nandan.dev/</a></p>
<p>Feel free to comment, email me at <a target="_blank" href="http://mailto:connect@nandan.dev/"><strong>connect@nandan.dev</strong></a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.instagram.com/nandandotdev"><strong>Instagram</strong></a> | <a target="_blank" href="https://github.com/sirius93"><strong>Github</strong></a> | <a target="_blank" href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Instagram front-end system design: RADIO framework walkthrough (2026)]]></title><description><![CDATA[Let me start by putting one thing forward: System design is not about writing a memorised answer on paper. It’s more about thinking, planning and strategising.
Hence, there is no correct answer, but a]]></description><link>https://blog.nandan.dev/how-to-design-instagram-front-end-system-design</link><guid isPermaLink="true">https://blog.nandan.dev/how-to-design-instagram-front-end-system-design</guid><category><![CDATA[System Design]]></category><category><![CDATA[frontend]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Meta]]></category><category><![CDATA[instagram]]></category><category><![CDATA[instagram-front-end]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Fri, 01 Aug 2025 05:19:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754025466156/4dc4da77-f23b-42df-b348-af563ce429ae.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let me start by putting one thing forward: System design is not about writing a memorised answer on paper. It’s more about thinking, planning and strategising.</p>
<p>Hence, there is no correct answer, but a correct Framework. Understanding and approaching system design with the RADIO <strong>RADIO framework</strong> (Requirements, Architecture, Design, Implementation, Optimisation) helps you streamline your answer.</p>
<p>Additionally, you can treat this blog as a framework for everything that requires building a feed, i.e. Twitter, Facebook, or Orkut, for that matter.</p>
<p>But everything beyond that is dependent on your knowledge and understanding of the system.</p>
<p>Designing a platform like Instagram might sound overwhelming at first, but breaking it down using the <strong>RADIO framework</strong> (Requirements, Architecture, Design, Implementation, Optimisation) makes it manageable and actually kinda fun. I recently took a shot at this, and here's what I came up with, plus a few tweaks and lessons learned along the way.</p>
<hr />
<h2>Requirements</h2>
<h3>🔧 Functional Requirements:</h3>
<ul>
<li><p>News Feed that shows text, images, and videos</p>
</li>
<li><p>User info (photo, username) on each post</p>
</li>
<li><p>Like, comment, and share buttons</p>
</li>
<li><p>Pagination or infinite scroll</p>
</li>
<li><p>Lazy loading for media</p>
</li>
</ul>
<h3>🚫 Non-Functional Requirements:</h3>
<ul>
<li><p>Fully responsive (mobile + desktop)</p>
</li>
<li><p>Accessibility compliant</p>
</li>
<li><p>Optimised for performance</p>
</li>
<li><p>Supports A/B testing for features</p>
</li>
<li><p>Localisation and translation support</p>
</li>
</ul>
<p>Bonus points if it supports offline mode and has a fallback UI.</p>
<hr />
<h2>Architecture</h2>
<p>Start by defining your core building blocks:</p>
<h3>📂 Components:</h3>
<pre><code class="language-javascript">PostCard
├── TextContent
├── ImageGallery / VideoPlayer
├── UserHeader (avatar + name)
├── PostActions (like/comment/share)

Feed
└── PostCard[]

CommentSection
LikeButton
ShareButton
</code></pre>
<p>Each of these is reusable and maintainable — the key to scalable front-end architecture.</p>
<hr />
<h2>Design</h2>
<p>Keep your data models clean and normalised. Here's what worked well:</p>
<pre><code class="language-ts">type Post = {
  id: string;
  text?: string;
  images?: string[];
  video?: string;
  userId: string;
  comments: Comment[];
  likes: Like[];
  createdAt: string;
};

type User = {
  id: string;
  name: string;
  username: string;
  avatar: string;
  postIds: string[];
};

type Like = {
  userId: string;
  postId: string;
};

type Comment = {
  id: string;
  userId: string;
  postId: string;
  text: string;
  createdAt: string;
};
</code></pre>
<ul>
<li><p>Avoid embedding full <code>User</code> objects inside posts</p>
</li>
<li><p>Use IDs to reference everything</p>
</li>
<li><p>Support multiple images per post (carousel style)</p>
</li>
</ul>
<hr />
<h2>Implementation</h2>
<p>Here's how the main flow looks:</p>
<ul>
<li><p><strong>Feed Page</strong>: loads posts using a custom hook (<code>useFetchPosts</code>)</p>
</li>
<li><p>Loop through <code>PostCard</code> components to render UI</p>
</li>
<li><p>Use libraries like React Query or SWR for cache + fetch</p>
</li>
<li><p>Defer comment loading until user clicks "View all comments"</p>
</li>
</ul>
<h3>Suggested folder structure:</h3>
<pre><code class="language-javascript">/components
  /Post
    PostCard.tsx
    PostMedia.tsx
    PostFooter.tsx
  /User
  /Comment
/hooks
  useFetchPosts.ts
/pages
  index.tsx // News Feed
</code></pre>
<hr />
<h2>Optimization</h2>
<p>Performance matters more than ever. Here's what to prioritise:</p>
<ul>
<li><p>📸 Lazy load images and videos</p>
</li>
<li><p>🔄 Use <code>IntersectionObserver</code> for infinite scroll</p>
</li>
<li><p>🏔️ PWA support (with service workers)</p>
</li>
<li><p>📑 Bundle splitting + code-splitting</p>
</li>
<li><p>🚀 Skeleton loaders while fetching</p>
</li>
<li><p>🌍 Localised text using <code>react-i18next</code></p>
</li>
<li><p>📉 Memoise unchanging components with <code>React.memo</code></p>
</li>
</ul>
<hr />
<h2>TL;DR</h2>
<p>Designing Instagram from the front-end isn’t about copying their UI; it's about building a thoughtful system that can scale, perform, and feel native on any device. Use the RADIO framework to break down the chaos into manageable parts, and you'll be surprised how fast it comes together.</p>
]]></content:encoded></item><item><title><![CDATA[Polyfills of JS Array Methods - Flat, Map, Filter & Reduce (Because Why Not?)]]></title><description><![CDATA[Alright, confession time, I’ve been writing JavaScript for a while now, but there’s something oddly satisfying about going back to the basics. You know, like trying to bake your bread from scratch even though you live next to a bakery. That’s exactly...]]></description><link>https://blog.nandan.dev/polyfills-of-js-array-methods-flat-map-filter-and-reduce-because-why-not</link><guid isPermaLink="true">https://blog.nandan.dev/polyfills-of-js-array-methods-flat-map-filter-and-reduce-because-why-not</guid><category><![CDATA[polyfills]]></category><category><![CDATA[array methods]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Tue, 13 May 2025 14:03:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747144915746/c275448b-a46c-4dad-8d0f-7d63dd154f7e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Alright, confession time, I’ve been writing JavaScript for a while now, but there’s something oddly satisfying about going back to the basics. You know, like trying to bake your bread from scratch even though you live next to a bakery. That’s exactly what this post is about: baking our versions of some classic array methods.</p>
<p>Want to understand what polyfills are? <a target="_blank" href="https://blog.nandan.dev/polyfills-for-call-apply-and-bind-lets-build-them-from-scratch">I have covered them in my blog Polyfills for Call, Apply &amp; Bind.</a></p>
<p>Why? Because it's fun. Because it's educational. And because the next time someone throws a technical interview curveball at you, like "Can you write your own <code>reduce</code>" and you’ll casually say, "Sure, do you want it with comments or without?"</p>
<p>Today, we’re building polyfills for:</p>
<ul>
<li><p><code>flat()</code></p>
</li>
<li><p><code>map()</code></p>
</li>
<li><p><code>filter()</code></p>
</li>
<li><p><code>reduce()</code></p>
</li>
</ul>
<p>Let’s roll.</p>
<h2 id="heading-flat-a-pancake-stack-for-arrays"><code>flat()</code> — A Pancake Stack for Arrays</h2>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span>(!<span class="hljs-built_in">Array</span>.prototype.myFlat){
    <span class="hljs-built_in">Array</span>.prototype.myFlat = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">depth</span>)</span>{
        <span class="hljs-keyword">if</span>(depth &lt; <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;

        <span class="hljs-keyword">const</span> flatten = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">arr, depth</span>)</span>{
            <span class="hljs-keyword">return</span> arr.reduce(<span class="hljs-function">(<span class="hljs-params">acc,ele</span>)=&gt;</span>{
                <span class="hljs-keyword">if</span>(<span class="hljs-built_in">Array</span>.isArray(ele) &amp;&amp; depth &gt; <span class="hljs-number">0</span>){
                    acc = acc.concat(flatten(ele,depth <span class="hljs-number">-1</span>));
                }<span class="hljs-keyword">else</span>{
                    acc.push(ele);
                }
                <span class="hljs-keyword">return</span> acc;
            },[])
        }

        <span class="hljs-keyword">return</span> flatten(<span class="hljs-built_in">this</span>,depth);
    }
}

<span class="hljs-keyword">const</span> nestedArray = [<span class="hljs-number">1</span>, [<span class="hljs-number">2</span>, [<span class="hljs-number">3</span>, [<span class="hljs-number">4</span>, <span class="hljs-number">5</span>]]], <span class="hljs-number">6</span>];

<span class="hljs-built_in">console</span>.log(nestedArray.myFlat()); <span class="hljs-comment">// Default depth = 1 =&gt; [1, 2, [3, [4, 5]], 6]</span>
<span class="hljs-built_in">console</span>.log(nestedArray.myFlat(<span class="hljs-number">2</span>)); <span class="hljs-comment">// Depth = 2 =&gt; [1, 2, 3, [4, 5], 6]</span>
<span class="hljs-built_in">console</span>.log(nestedArray.myFlat(<span class="hljs-number">3</span>)); <span class="hljs-comment">// Depth = 3 =&gt; [1, 2, 3, 4, 5, 6]</span>
<span class="hljs-built_in">console</span>.log(nestedArray.myFlat(<span class="hljs-number">0</span>)); <span class="hljs-comment">// Depth = 0 =&gt; [1, [2, [3, [4, 5]]], 6]</span>
</code></pre>
<p>Just a basic flattening function. Will it replace your backend job? No. But will it make you feel cool? Absolutely.</p>
<h2 id="heading-map-because-sometimes-arrays-need-a-makeover"><code>map()</code> — Because Sometimes Arrays Need a Makeover</h2>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span>(!<span class="hljs-built_in">Array</span>.prototype.myMap){
    <span class="hljs-built_in">Array</span>.prototype.myMap = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">fn, thisArg</span>)</span>{
       <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span> == <span class="hljs-literal">null</span>)  { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not an array"</span>)};

       <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span>(fn) != <span class="hljs-string">'function'</span>) {<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not an array"</span>)}

       <span class="hljs-keyword">let</span> result = [];

       <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>; i&lt; <span class="hljs-built_in">this</span>.length; i++){
           <span class="hljs-keyword">if</span>(i <span class="hljs-keyword">in</span> <span class="hljs-built_in">this</span>){
               result.push(fn.call(thisArg,<span class="hljs-built_in">this</span>[i], i, <span class="hljs-built_in">this</span>));
           }
       }
       <span class="hljs-keyword">return</span> result;
    }
}

a = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>].myMap(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">mem</span>) </span>{
    <span class="hljs-keyword">return</span> mem*mem;
})

<span class="hljs-built_in">console</span>.log(a); <span class="hljs-comment">// [ 1, 4, 9, 16, 25 ]</span>
</code></pre>
<p>Simple and clean. No magical spells involved.</p>
<h2 id="heading-filter-only-the-worthy-shall-pass"><code>filter()</code> — Only the Worthy Shall Pass</h2>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span>(!<span class="hljs-built_in">Array</span>.prototype.myFilter){
    <span class="hljs-built_in">Array</span>.prototype.myFilter = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">fn, thisArg</span>)</span>{
       <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span> == <span class="hljs-literal">null</span>)  { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not an array"</span>)};

       <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span>(fn) != <span class="hljs-string">'function'</span>) {<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not an array"</span>)}

       <span class="hljs-keyword">let</span> result = [];

       <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>; i&lt; <span class="hljs-built_in">this</span>.length; i++){
           <span class="hljs-keyword">if</span>(i <span class="hljs-keyword">in</span> <span class="hljs-built_in">this</span> &amp;&amp; fn.call(thisArg,<span class="hljs-built_in">this</span>[i], i, <span class="hljs-built_in">this</span>)){
               result.push(<span class="hljs-built_in">this</span>[i]);
           }
       }
       <span class="hljs-keyword">return</span> result;
    }
}

b = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>].myFilter(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">mem</span>) </span>{
    <span class="hljs-keyword">return</span> mem &gt; <span class="hljs-number">1</span>;
})

<span class="hljs-built_in">console</span>.log(b) <span class="hljs-comment">//[ 2, 3, 4, 5 ]</span>
</code></pre>
<p>Give it a test run with your own weird conditions. Even filter out people who don't like coffee; your call.</p>
<h2 id="heading-reduce-the-boss-level"><code>reduce()</code> — The Boss Level</h2>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span>(!<span class="hljs-built_in">Array</span>.prototype.myReduce){
    <span class="hljs-built_in">Array</span>.prototype.myReduce = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">callback,initialValue</span>)</span>{
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span> == <span class="hljs-literal">null</span>) {<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not an Array"</span>);}

        <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span>(callback) != <span class="hljs-string">'function'</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not a function"</span>);
        }

        <span class="hljs-keyword">let</span> arr = <span class="hljs-built_in">this</span>;
        <span class="hljs-keyword">let</span> len = arr.length;

        <span class="hljs-keyword">let</span> hasInitial = <span class="hljs-built_in">arguments</span>.length &gt; <span class="hljs-number">1</span>;
        <span class="hljs-keyword">let</span> accumulator = hasInitial ? initialValue : arr[<span class="hljs-number">0</span>];
        <span class="hljs-keyword">let</span> startIndex = hasInitial ? <span class="hljs-number">0</span> : <span class="hljs-number">1</span>;

        <span class="hljs-keyword">if</span> (len === <span class="hljs-number">0</span> &amp;&amp; !hasInitial) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Reduce of empty array with no initial value"</span>);
        }


        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = startIndex; i &lt; len; i++) {
          <span class="hljs-keyword">if</span> (i <span class="hljs-keyword">in</span> arr) {
            accumulator = callback(accumulator, arr[i], i, arr);
          }
        }

        <span class="hljs-keyword">return</span> accumulator;
    }
}

<span class="hljs-keyword">let</span> a = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].myReduce(<span class="hljs-function">(<span class="hljs-params">acc, val</span>) =&gt;</span> acc + val, <span class="hljs-number">0</span>);
<span class="hljs-built_in">console</span>.log(a);
</code></pre>
<p>Reduce is like that friend who seems intimidating at first, but once you get to know them, they carry the whole squad.</p>
<h3 id="heading-why-do-this">Why Do This?</h3>
<p>You may never need to use these in a production app. But knowing how they work helps you:</p>
<ul>
<li><p>Understand JavaScript fundamentals better.</p>
</li>
<li><p>Appreciate the built-in methods more.</p>
</li>
<li><p>Have something cool to say in interviews or while pretending to debug in coffee shops.</p>
</li>
</ul>
<p>So go ahead, mess around with these, add your own flavour, and maybe even extend a couple more methods just for fun.</p>
<p>That’s all, folks! I hope you found this helpful. If you enjoyed this, check out more articles on my Blog, <a target="_blank" href="https://blog.nandan.dev/">https://blog.nandan.dev/</a></p>
<p>Feel free to comment, email me at <a target="_blank" href="http://mailto:connect@nandan.dev/"><strong>connect@nandan.dev</strong></a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.instagram.com/nandandotdev"><strong>Instagram</strong></a> | <a target="_blank" href="https://github.com/sirius93"><strong>Github</strong></a> | <a target="_blank" href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Polyfills for Call, Apply, and Bind — Let’s Build Them From Scratch]]></title><description><![CDATA[https://store.nandan.dev/l/javascript-interview-prep-handbook
 
Okay, so if you have ever deep-dived into JavaScript interviews or just casually scrolled through dev Twitter (yes, that’s a thing), you would have stumbled upon someone throwing around ...]]></description><link>https://blog.nandan.dev/polyfills-for-call-apply-and-bind-lets-build-them-from-scratch</link><guid isPermaLink="true">https://blog.nandan.dev/polyfills-for-call-apply-and-bind-lets-build-them-from-scratch</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[polyfills]]></category><category><![CDATA[call apply and bind methods]]></category><category><![CDATA[interview questions]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Mon, 28 Apr 2025 16:51:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745858731907/fc43c7fc-0191-42ff-bdd1-19518625c8b7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://store.nandan.dev/l/javascript-interview-prep-handbook">https://store.nandan.dev/l/javascript-interview-prep-handbook</a></div>
<p> </p>
<p>Okay, so if you have ever deep-dived into JavaScript interviews or just casually scrolled through dev Twitter (yes, that’s a thing), you would have stumbled upon someone throwing around the words <strong>polyfill</strong> like they were born building web browsers.</p>
<p>And if you’re like me, you probably nodded along, opened a tab to Google it later, and… procrastinated.</p>
<p><img src="https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExd2NhODEycGhiZjJmZGsweG4xaHc5ODAzbnB0eGk4dWZjM241Yjd4YSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3o6gbenQcUjEGTaNfW/giphy.gif" alt class="image--center mx-auto" /></p>
<p>But no worries. You’re here now, and we are going to figure this out <em>together.</em></p>
<h2 id="heading-first-things-first-what-the-heck-is-a-polyfill"><strong>First things first — What the heck is a polyfill?</strong></h2>
<p>A <strong>polyfill</strong> is just a piece of code (usually JavaScript) that <strong>adds functionality to older browsers</strong> that do not natively support a certain feature.</p>
<p>Think of it like you bringing a power bank to an event where there are no charging points. Your phone is old? No problem. You got the backup.</p>
<h2 id="heading-now-why-call-apply-and-bind"><strong>Now… Why Call, Apply, and Bind?</strong></h2>
<p>Because they’re like the Avengers of function manipulation in JavaScript.</p>
<p>And also because they confuse almost everyone at first.</p>
<ul>
<li><p><code>call</code> lets you call a function with a given this value and arguments.</p>
</li>
<li><p><code>apply</code> is like <code>call</code>, but arguments are passed as an array.</p>
</li>
<li><p><code>bind</code> doesn’t immediately invoke the function. It <em>returns a new function</em> with a given this.</p>
</li>
</ul>
<p><a target="_blank" href="https://blog.nandan.dev/yet-another-blog-on-call-apply-bind">Here is a detailed write-up on call, apply &amp; bind</a>.</p>
<p>Simple enough, right? Now let’s make our versions</p>
<h2 id="heading-polyfill-for-call"><strong>Polyfill for call</strong></h2>
<p>Here’s how we can build a <strong>polyfill for call</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">Function</span>.prototype.myCall = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context,...args</span>)</span>{ 
    context = context || globalThis;
    <span class="hljs-keyword">let</span> Symbolfn = <span class="hljs-built_in">Symbol</span>(<span class="hljs-string">"fn"</span>); <span class="hljs-comment">// Unique key</span>
    context[Symbolfn] = <span class="hljs-built_in">this</span>; <span class="hljs-comment">// Assign 'this' function to the context</span>
    <span class="hljs-keyword">let</span> result = context[Symbolfn](...args); <span class="hljs-comment">// Call the function</span>
    <span class="hljs-keyword">delete</span> context[Symbolfn];  <span class="hljs-comment">// Clean up</span>
    <span class="hljs-keyword">return</span> result;
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sayHello</span>(<span class="hljs-params">greeting</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`<span class="hljs-subst">${greeting}</span>, <span class="hljs-subst">${<span class="hljs-built_in">this</span>.name}</span>`</span>);
}

<span class="hljs-keyword">const</span> user = { <span class="hljs-attr">name</span>: <span class="hljs-string">"Nandan"</span> };
sayHello.myCall(user, <span class="hljs-string">"Hello"</span>);
</code></pre>
<p>Cool, right? Not rocket science.</p>
<h2 id="heading-polyfill-for-apply"><strong>Polyfill for apply</strong></h2>
<p>Now, let’s tweak it a bit for <strong>apply</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">Function</span>.prototype.myApply = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context,args</span>)</span>{
    context = context || globalThis;
    <span class="hljs-keyword">let</span> Symbolfn = <span class="hljs-built_in">Symbol</span>(<span class="hljs-string">"fn"</span>);
    context[Symbolfn] = <span class="hljs-built_in">this</span>;
    <span class="hljs-keyword">let</span> result = args ? context[Symbolfn](...args) : context[Symbolfn]();
    <span class="hljs-keyword">delete</span> context[Symbolfn];
    <span class="hljs-keyword">return</span> result;
}

<span class="hljs-comment">// Usage</span>
sayHello.myApply(user, [<span class="hljs-string">"Hi"</span>]);
</code></pre>
<h2 id="heading-polyfill-for-bind"><strong>Polyfill for bind</strong></h2>
<p>Now comes the big boss <strong>bind</strong>.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">Function</span>.prototype.myBind = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context,...initialArgs</span>)</span>{
    <span class="hljs-keyword">let</span> fn = <span class="hljs-built_in">this</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">...functArgs</span>)</span>{
        <span class="hljs-keyword">return</span> fn.apply(context, [...initialArgs,...functArgs])
    }
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">const</span> boundSayHello = sayHello.myBind(user, <span class="hljs-string">"Hey"</span>);
boundSayHello(); <span class="hljs-comment">// Outputs: Hey, Nandan</span>
</code></pre>
<ul>
<li><p>bind doesn’t call the function right away.</p>
</li>
<li><p>It returns a <em>new function</em> that you can call later.</p>
</li>
<li><p>We merge the arguments provided at bind-time and call-time.</p>
</li>
</ul>
<h2 id="heading-thats-it-you-just-built-call-apply-and-bind-polyfills"><strong>That’s it — You just built call, apply, and bind polyfills!</strong></h2>
<p>Honestly, when I first read about polyfills, I thought it was some deep internal browser magic. Turns out, it’s just about creatively manipulating JavaScript’s behavior.</p>
<p>If you take one thing from this post, let it be this:</p>
<blockquote>
<p>“The cowards never started and the weak died along the way. That leaves us, ladies and gentlemen. Us.”</p>
</blockquote>
<p>(Yeah, that’s a quote from <em>Shoe Dog</em>. Still fits perfectly here, doesn’t it?)</p>
<p><a target="_blank" href="https://kitaaben.com/p/just-do-it-the-real-story-behind-nike-s-rise">Here’s one if you want to read a review of the book Shoe Dog, written by me.</a></p>
<p>That’s all, folks! I hope you found this short note on Polyfills of Call, Apply &amp; Bind helpful. If you enjoyed this, check out more articles on my Blog, <a target="_blank" href="https://blog.nandan.dev/">https://blog.nandan.dev/</a></p>
<p>Feel free to comment, email me at <a target="_blank" href="http://mailto:connect@nandan.dev/"><strong>connect@nandan.dev</strong></a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.instagram.com/nandandotdev"><strong>Instagram</strong></a> | <a target="_blank" href="https://github.com/sirius93"><strong>Github</strong></a> | <a target="_blank" href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[I built an AI Book Recommendation App and was DDOS-ed for it.]]></title><description><![CDATA[Last week, Google introduced its AI copilot Firebase Studio, and I decided to give it a try. It got me thinking about what I want to do next.
If you may be following me on socials, I just started a newsletter for book reviews & suggestions. Check htt...]]></description><link>https://blog.nandan.dev/i-built-an-ai-book-recommendation-app-and-was-ddos-ed-for-it</link><guid isPermaLink="true">https://blog.nandan.dev/i-built-an-ai-book-recommendation-app-and-was-ddos-ed-for-it</guid><category><![CDATA[AI]]></category><category><![CDATA[vibe coding]]></category><category><![CDATA[ddos]]></category><category><![CDATA[DDoS Protection ]]></category><category><![CDATA[cloudflare]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Tue, 15 Apr 2025 10:37:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744713215395/d734f5ba-48fe-43da-a994-8232425c0fac.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last week, Google introduced its AI copilot Firebase Studio, and I decided to give it a try. It got me thinking about what I want to do next.</p>
<p>If you may be following me on socials, I just started a newsletter for book reviews &amp; suggestions. Check <a target="_blank" href="https://kitaaben.com">https://kitaaben.com</a>. That got me thinking: What if I built a small book recommendation application that can help users get book recommendations based on their interests?</p>
<p>I started building it on Firebase studio. Started with a simple prompt to build an AI-based book recommendation application that shows a good variety of genres by default and an interest field for additional context. The Number of books you want to be recommended is either 1/3/6 or 12.</p>
<p>The initial version came with these fields. But after some iterations, I decided to add the email field and email integration to help users get the recommendation directly to their email.</p>
<p>The tech stack looks something like this:</p>
<ul>
<li><p>Nextjs + Reactjs ( For the application)</p>
</li>
<li><p>Gemini API (For AI)</p>
</li>
<li><p>Firebase (For Hosting)</p>
</li>
<li><p>Resend (For Email - Free Tier)</p>
</li>
</ul>
<p>The final version looked like this:</p>
<p><a target="_blank" href="https://bookwise.kitaaben.com/"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744711016863/dd4268f4-7f57-4399-9fc3-83b724e4b07f.png" alt class="image--center mx-auto" /></a></p>
<p>After some testing and a lot of dopamine, I went ahead and made it public. Mapped it to a subdomain and made it public. Here is the URL if you want to give it a try: <a target="_blank" href="https://bookwise.kitaaben.com/">https://bookwise.kitaaben.com</a></p>
<p>I shared it in my friend’s WhatsApp group, Instagram, Twitter( now X), Linkedin almost everywhere I could.</p>
<p>But seems like someone didn’t like the spamming and decided to do something about it. By launching a DDOS attack on my website.Targeting my email functionality, trying to exhaust my email limit and increasing my cloud hosting + AI usage cost.</p>
<p>See the sudden spike in the requests?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744711671403/431a9532-9260-4fd6-9a0b-e89038891b24.png" alt class="image--center mx-auto" /></p>
<p>Panic set in; I didn’t know what to do. Should I bring down the site and end this or something else? I decided not to give up and just think and act quickly</p>
<p>I did and quickly signed up for Cloudflare, added the Cloudflare protection layer to my app and observed the behaviour for some time. By this time, my email limit of 200 emails by resend had already been exhausted, so I waited for the next day and added a caching layer to store the emails and rate limit the emails to 3 emails per hour.</p>
<p>Things seem to be stable for now. The traffic has dropped as well. However, I would not mind some humans trying the app, benefiting from it and providing some real feedback.</p>
<p>If you are a human reading this, give the app a try. Tell me what you like/dislike. I am always just an email away at <a target="_blank" href="mailto:connect@nandan.dev">connect@nandan.dev</a></p>
<hr />
<p>That’s all, folks! I hope you enjoyed this. Check out more articles on my website, <a target="_blank" href="https://nandan.dev/"><strong>https://nandan.dev/</strong></a></p>
<p>Feel free to comment, email me at <a target="_blank" href="http://mailto:connect@nandan.dev/"><strong>connect@nandan.dev</strong></a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.instagram.com/nandandotdev"><strong>Instagram</strong></a> | <a target="_blank" href="https://github.com/sirius93"><strong>Github</strong></a> | <a target="_blank" href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Yet another blog on Call, Apply & Bind..!!]]></title><description><![CDATA[Bla Bla Bla..
Okay, Hear me out..!!
Hundreds of blogs and tutorials explain call, bind, and apply. Heck, even ChatGPT and Copilot can simplify them for you!
So why another post? Well, this one’s not for you—it’s my self-note as I revisit JavaScript f...]]></description><link>https://blog.nandan.dev/yet-another-blog-on-call-apply-bind</link><guid isPermaLink="true">https://blog.nandan.dev/yet-another-blog-on-call-apply-bind</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[learning]]></category><category><![CDATA[this keyword]]></category><category><![CDATA[Objects]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Fri, 22 Nov 2024 11:05:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732273207021/92fa831e-80f5-415e-b1ab-9e46cb5b6998.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-bla-bla-bla">Bla Bla Bla..</h2>
<p>Okay, Hear me out..!!</p>
<p>Hundreds of blogs and tutorials explain call, bind, and apply. Heck, even ChatGPT and Copilot can simplify them for you!</p>
<p>So why another post? Well, this one’s not for <em>you</em>—it’s my self-note as I revisit JavaScript fundamentals.</p>
<p>Even after 8 years in front-end development, I sometimes mix up these methods. Turns out, the confusion often stems from how they’re grouped in interview questions. Let me break it down for you—this time, in a way that sticks.</p>
<h2 id="heading-can-we-skip-to-the-good-part">Can we skip to the good part.</h2>
<p>Why is it always call, bind, and apply in interviews? Functionally, call and apply are closer—they invoke the function immediately—while bind returns a new function. That’s why I’ve grouped them this way in the title.</p>
<p>Let's look at their usage to understand it better.</p>
<h3 id="heading-what-is-common-among-call-bind-amp-apply">What is common among Call, Bind &amp; Apply?</h3>
<p><strong>call</strong>, <strong>bind</strong>, and <strong>apply</strong> are methods available on functions that allow you to explicitly set the value of <strong>this</strong> and pass arguments to the function.</p>
<h3 id="heading-what-is-the-difference-between-call-bind-amp-apply">What is the difference between Call, Bind &amp; Apply?</h3>
<p>While Call and Apply Immediately invoke the function, the Bind method does not immediately invoke the function. instead, it returns a new function with <strong>this</strong> set to a specified value, and this returned function can be invoked/executed later.</p>
<h3 id="heading-what-is-the-difference-between-call-and-apply-then">What is the difference between Call and Apply then?</h3>
<p>While both Call and Apply immediately invoke the function with the custom this, it’s how they take the additional argument that differentiates them.</p>
<p>While Call takes the individual arguments, Apply takes an array of arguments.</p>
<h3 id="heading-examples">Examples</h3>
<p>Let’s look at the below example to understand the use of call, apply and bind.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Greetings</span>(<span class="hljs-params">greeting,punctuation</span>)</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${greeting}</span> <span class="hljs-subst">${<span class="hljs-built_in">this</span>.name}</span> <span class="hljs-subst">${punctuation}</span>`</span>
}

<span class="hljs-keyword">var</span> person = {
    <span class="hljs-attr">name</span> : <span class="hljs-string">"Nandan"</span>
}

<span class="hljs-comment">// Call: Immediately invokes the function with individual arguments</span>
<span class="hljs-built_in">console</span>.log(Greetings.call(person,<span class="hljs-string">"Hello"</span>,<span class="hljs-string">"!"</span>)); <span class="hljs-comment">// Output : Hello Nandan !</span>

<span class="hljs-comment">// Apply: Immediately invokes the function, but takes arguments as an array</span>
<span class="hljs-built_in">console</span>.log(Greetings.apply(person,[<span class="hljs-string">"Hello"</span>,<span class="hljs-string">"!"</span>])); <span class="hljs-comment">// Output : Hello Nandan !</span>

<span class="hljs-comment">// Bind: Returns a new function with `this` bound to the specified value</span>
<span class="hljs-keyword">let</span> greet = Greetings.bind(person,<span class="hljs-string">"Hello"</span>,<span class="hljs-string">"!"</span>);
<span class="hljs-built_in">console</span>.log(greet()); <span class="hljs-comment">// Output : Hello Nandan !</span>

<span class="hljs-comment">//Note:</span>
<span class="hljs-built_in">console</span>.log(Greetings.bind(person,<span class="hljs-string">"Hello"</span>,<span class="hljs-string">"!"</span>)); <span class="hljs-comment">// Output : It will return a function</span>
</code></pre>
<h3 id="heading-some-additional-examples">Some Additional Examples</h3>
<p>• <strong>Using</strong> call<strong>:</strong> Borrowing methods from another object:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> person1 = { <span class="hljs-attr">name</span>: <span class="hljs-string">"Nandan"</span> };
<span class="hljs-keyword">let</span> person2 = { <span class="hljs-attr">name</span>: <span class="hljs-string">"Kumar"</span> };

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">introduce</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Hi, my name is <span class="hljs-subst">${<span class="hljs-built_in">this</span>.name}</span>`</span>);
}

introduce.call(person1); <span class="hljs-comment">// Hi, my name is Nandan</span>
introduce.call(person2); <span class="hljs-comment">// Hi, my name is Kumar</span>
</code></pre>
<p>• <strong>Using</strong> apply<strong>:</strong> Finding the max value in an array:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];
<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Math</span>.max.apply(<span class="hljs-literal">null</span>, numbers)); <span class="hljs-comment">// 5</span>
</code></pre>
<p>• <strong>Using</strong> bind<strong>:</strong> Event handling with custom this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> button = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"myButton"</span>);
<span class="hljs-keyword">let</span> user = {
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Nandan"</span>,
    greet() {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Hello, <span class="hljs-subst">${<span class="hljs-built_in">this</span>.name}</span>`</span>);
    }
};

button.addEventListener(<span class="hljs-string">"click"</span>, user.greet.bind(user));
</code></pre>
<p>Additionally, I hope this table will help you understand it better.</p>
<table><tbody><tr><td><p><strong>Feature</strong></p></td><td><p><strong>Execution</strong></p></td><td><p><strong>Arguments</strong></p></td><td><p><strong>Use Case</strong></p></td></tr><tr><td><p><strong>call</strong></p></td><td><p>Executes the function immediately</p></td><td><p>Passed individually</p></td><td><p>When you know arguments at call time</p></td></tr><tr><td><p><strong>apply</strong></p></td><td><p>Executes the function immediately</p></td><td><p>Passed as an array</p></td><td><p>When arguments are in an array</p></td></tr><tr><td><p><strong>bind</strong></p></td><td><p>Returns a new function (does not execute)</p></td><td><p>Optionally pre-filled for the new function</p></td><td><p>When you need a reusable or pre-configured function</p></td></tr></tbody></table>

<p>That’s all, folks! I hope you found this short note on Call, Apply &amp; Bind helpful. If you enjoyed this, check out more articles on my website, <a target="_blank" href="https://nandan.dev/"><strong>https://nandan.dev/</strong></a></p>
<p>Feel free to comment, email me at <a target="_blank" href="http://mailto:connect@nandan.dev/"><strong>connect@nandan.dev</strong></a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.instagram.com/nandandotdev"><strong>Instagram</strong></a> | <a target="_blank" href="https://github.com/sirius93"><strong>Github</strong></a> | <a target="_blank" href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[What are Javascript Closures?]]></title><description><![CDATA[Closures are a fundamental concept in JavaScript that enables powerful features like data privacy, state persistence, and functional programming. This blog will demystify closures with examples and practical use cases.
When are closures created?
Clos...]]></description><link>https://blog.nandan.dev/what-are-javascript-closures</link><guid isPermaLink="true">https://blog.nandan.dev/what-are-javascript-closures</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[closures in javascript]]></category><category><![CDATA[lexical scope]]></category><category><![CDATA[interview questions]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Mon, 18 Nov 2024 17:04:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731949387166/3ba3dbdd-5437-4a59-92a0-633e5d59405d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Closures are a fundamental concept in JavaScript that enables powerful features like data privacy, state persistence, and functional programming. This blog will demystify closures with examples and practical use cases.</p>
<h2 id="heading-when-are-closures-created">When are closures created?</h2>
<p>Closures are created when a function <strong>(inner function)</strong> is defined inside another function <strong>(outer function).</strong> The inner function has access to the outer function's lexical scope <strong>(variables)</strong> even after the outer function is executed.</p>
<h3 id="heading-lexical-scope-in-javascript">Lexical Scope in Javascript</h3>
<p>Lexical scope means a function’s scope is determined by where it is written in the code, not where it is executed. This allows inner functions to access variables from their outer functions even if the outer function has already finished executing.</p>
<h3 id="heading-usage">Usage</h3>
<p>Closures are used for creating Private variables and persistent States (Cache, Memoisation etc.).</p>
<h3 id="heading-closures-with-examples">Closures with Examples</h3>
<p><strong>A Simple counter</strong></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Outer</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>; <span class="hljs-comment">// count is a private variable</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inner</span>(<span class="hljs-params"></span>) </span>{
        count++; <span class="hljs-comment">// count is updated every time inner is called</span>
        <span class="hljs-keyword">return</span> count; <span class="hljs-comment">// the updated value of count is returned</span>
    };
}

<span class="hljs-keyword">let</span> Counter = Outer(); 
Counter(); <span class="hljs-comment">// 1 (count starts at 0, incremented to 1)</span>
Counter(); <span class="hljs-comment">// 2 (incremented again)</span>
Counter(); <span class="hljs-comment">// 3</span>
</code></pre>
<p><strong>Building a cache leveraging closure</strong></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ObjectCache</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> Obj = {}; <span class="hljs-comment">// Obj acts as a private cache</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ObjectUpdate</span>(<span class="hljs-params">key, value</span>) </span>{
        <span class="hljs-keyword">if</span> (!Obj[key]) {
            Obj[key] = value; <span class="hljs-comment">// Add key-value to cache if not already present</span>
            <span class="hljs-keyword">return</span> Obj; <span class="hljs-comment">// Return updated cache</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Duplicate Key"</span>); <span class="hljs-comment">// Prevent overwriting</span>
        }
    };
}

<span class="hljs-keyword">let</span> cache = ObjectCache();
cache(<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>); <span class="hljs-comment">// {a: 'b'}</span>
cache(<span class="hljs-string">"a"</span>, <span class="hljs-string">"c"</span>); <span class="hljs-comment">// Error: Duplicate Key</span>
cache(<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"romeo"</span>); <span class="hljs-comment">// {a: 'b', alpha: 'romeo'}</span>
</code></pre>
<h3 id="heading-real-world-examples">Real World Examples</h3>
<p><strong>Event Listeners - Click Counter</strong></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">attachListener</span>(<span class="hljs-params">element</span>) </span>{
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    element.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> {
        count++;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Clicked <span class="hljs-subst">${count}</span> times`</span>);
    });
}
</code></pre>
<p><strong>A Better Counter</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> CounterModule = (<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">increment</span>: <span class="hljs-function">() =&gt;</span> ++count,
        <span class="hljs-attr">reset</span>: <span class="hljs-function">() =&gt;</span> (count = <span class="hljs-number">0</span>),
        <span class="hljs-attr">decrement</span>: <span class="hljs-function">() =&gt;</span> --count
    };
})();

CounterModule.increment(); <span class="hljs-comment">// 1</span>
CounterModule.increment(); <span class="hljs-comment">// 2</span>
CounterModule.decrement(); <span class="hljs-comment">// 1</span>
CounterModule.reset(); <span class="hljs-comment">// 0</span>
</code></pre>
<p>That’s all, folks! I hope you found this short blog on closures helpful. If you enjoyed this, check out more articles on my website, <a target="_blank" href="https://nandan.dev/">https://nandan.dev/</a></p>
<p>Feel free to comment, email me at <a target="_blank" href="http://mailto:connect@nandan.dev">connect@nandan.dev</a>, or connect with me on Twitter, Instagram, or GitHub. Don’t forget to subscribe to my newsletter for regular updates on JavaScript topics!</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_">Twitter</a> | <a target="_blank" href="https://www.instagram.com/nandandotdev">Instagram</a> | <a target="_blank" href="https://github.com/sirius93">Github</a> | <a target="_blank" href="https://nandan.dev">Website</a></p>
]]></content:encoded></item><item><title><![CDATA[System Design : SQL vs NoSQL databases]]></title><description><![CDATA[In the realm of databases, two primary types of solutions exist, SQL (relational) and NoSQL (non-relational) databases. These two categories differ significantly in their construction, the nature of the data they store, and their storage methods. Rel...]]></description><link>https://blog.nandan.dev/system-design-sql-vs-nosql-databases</link><guid isPermaLink="true">https://blog.nandan.dev/system-design-sql-vs-nosql-databases</guid><category><![CDATA[System Design]]></category><category><![CDATA[Databases]]></category><category><![CDATA[SQL]]></category><category><![CDATA[NoSQL]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Tue, 13 Aug 2024 17:01:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723568306196/0a61975f-c413-49e2-9356-d45dc2078bcf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the realm of databases, two primary types of solutions exist, SQL (relational) and NoSQL (non-relational) databases. These two categories differ significantly in their construction, the nature of the data they store, and their storage methods. Relational databases are structured with predefined schemas, while non-relational databases are unstructured, distributed, and feature dynamic schemas.</p>
<h2 id="heading-high-level-differences">High-level differences</h2>
<p>Here are some high-level differences between SQL and NoSQL:</p>
<h3 id="heading-storage">Storage</h3>
<p>SQL stores data in tables where each row represents an entity, and each column represents a data point related to that entity.</p>
<p>NoSQL databases utilize various data storage models, including key-value, graph, and document-oriented approaches.</p>
<h3 id="heading-schema">Schema</h3>
<p>In SQL, each record follows a fixed schema, which means that the columns must be determined and chosen before data entry, and each row must contain data for each column. The schema can be modified later, but this involves modifying the database using migrations.</p>
<p>NoSQL schemas are dynamic. Fields can be added on the fly, and each <em>record</em> doesn’t have to contain data for each <em>field</em>.</p>
<h3 id="heading-querying">Querying</h3>
<p>SQL databases use Structured Query Language (SQL) to define and manipulate data, which is highly powerful.</p>
<p>In a NoSQL database, queries focus on a collection of documents, with different databases having different syntax for querying.</p>
<h3 id="heading-scalability"><strong>Scalability</strong></h3>
<p>In most cases, SQL databases can be scaled vertically, which can become very costly. While it's feasible to scale a relational database across multiple servers, it's a difficult and time-consuming process.</p>
<p>NoSQL databases are horizontally scalable, which means it's easy to add more servers to handle large traffic. They can be hosted on inexpensive hardware or cloud instances, making them cost-effective compared to vertical scaling. Many NoSQL technologies also distribute data across servers automatically.</p>
<h3 id="heading-reliability">Reliability</h3>
<p>The majority of relational databases are ACID compliant. Therefore, SQL databases are still the best choice for data reliability and transaction safety.</p>
<p>Many NoSQL solutions prioritize performance and scalability over ACID compliance.</p>
<h2 id="heading-reasons">Reasons</h2>
<p>As always we should always pick the technology that fits the requirements better. So, let’s look at some reasons for picking SQL or NoSQL based database:</p>
<p><strong>For SQL</strong></p>
<ul>
<li><p>Structured data with strict schema</p>
</li>
<li><p>Relational data</p>
</li>
<li><p>Need for complex joins</p>
</li>
<li><p>Transactions</p>
</li>
<li><p>Lookups by index are very fast</p>
</li>
</ul>
<p><strong>For NoSQL</strong></p>
<ul>
<li><p>Dynamic or flexible schema</p>
</li>
<li><p>Non-relational data</p>
</li>
<li><p>No need for complex joins</p>
</li>
<li><p>Very data-intensive workload</p>
</li>
<li><p>Very high throughput for IOPS</p>
</li>
</ul>
<p>That's all folks.</p>
<p>Feel free to comment on how you like my blog on the system design series or shoot me an email at <a target="_blank" href="http://mailto:connect@nandan.dev">connect@nandan.dev</a> If you have any queries I will try to answer them.</p>
<p>You can also visit my website to read some of the articles at <a target="_blank" href="https://nandan.dev/">https://nandan.dev/</a></p>
<p>Stay tuned &amp; connect with me on my social media channels. Subscribe to my newsletter to get regular updates on my upcoming posts.</p>
<p>If you're interested in learning about system design, you can join Design Guru's course on <a target="_blank" href="https://www.designgurus.io/course/grokking-system-design-fundamentals?aff=y0pphf">System Design Fundamentals</a>.</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_">Twitter</a> | <a target="_blank" href="https://www.instagram.com/nandandotdev">Instagram</a> | <a target="_blank" href="https://github.com/sirius93">Github</a> | <a target="_blank" href="https://nandan.dev">Website</a></p>
]]></content:encoded></item><item><title><![CDATA[System Design: Databases and DBMS]]></title><description><![CDATA[What is a Database?
A database is a structured collection of information or data that is typically stored electronically in a computer system. The management of the database is usually done by a Database Management System (DBMS). The combination of t...]]></description><link>https://blog.nandan.dev/system-design-databases-and-dbms</link><guid isPermaLink="true">https://blog.nandan.dev/system-design-databases-and-dbms</guid><category><![CDATA[Databases]]></category><category><![CDATA[System Design]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software architecture]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Wed, 01 May 2024 14:46:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714574316849/b1e9a1fc-40ed-4676-ac2c-2459738c6a53.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-a-database">What is a Database?</h2>
<p>A database is a structured collection of information or data that is typically stored electronically in a computer system. The management of the database is usually done by a Database Management System (DBMS). The combination of the data and the DBMS, along with the related applications, is commonly referred to as a database system, which is often shortened to just database.</p>
<h2 id="heading-what-is-dbms">What is DBMS?</h2>
<p>A database is a collection of data that requires a comprehensive database software program, known as a Database Management System (DBMS), to function. Essentially, a DBMS serves as an interface between the database and its end-users or programs, allowing users to retrieve, update, and manage how the information is organized and optimized. Additionally, a DBMS enables oversight and control of databases, enabling a variety of administrative operations such as performance monitoring, tuning, backup, and recovery.</p>
<h2 id="heading-components">Components</h2>
<p>Here are some commonly found components that are present in different databases:</p>
<h3 id="heading-schema">Schema</h3>
<p>A schema is responsible for defining the structure of a data storage system and specifying the types of data that can be stored in it. Schemas can be strictly enforced throughout the entire database, enforced loosely in some areas, or not enforced at all.</p>
<h3 id="heading-table">Table</h3>
<p>Each table can have anywhere from two to over a hundred columns, depending on the type of information it contains, similar to a spreadsheet.</p>
<h3 id="heading-column">Column</h3>
<p>A database column contains a single data value of a particular type for each row. The data value can be text, number, enum, timestamp, etc.</p>
<h3 id="heading-row">Row</h3>
<p>Data in a table is recorded in rows. There can be thousands or millions of rows in a table having any particular information.</p>
<h2 id="heading-types">Types</h2>
<p><img src="https://nandan.dev/system-design/images/database-types.png" alt="database-types" /></p>
<p>Below are different types of databases:</p>
<ul>
<li><p><a target="_blank" href="https://nandan.dev/pages/courses/system-design/sql-databases"><strong>SQL</strong></a></p>
</li>
<li><p><a target="_blank" href="https://nandan.dev/pages/courses/system-design/nosql-databases"><strong>NoSQL</strong></a></p>
<ul>
<li><p>Document</p>
</li>
<li><p>Key-value</p>
</li>
<li><p>Graph</p>
</li>
<li><p>Timeseries</p>
</li>
<li><p>Wide column</p>
</li>
<li><p>Multi-model</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-challenges">Challenges</h2>
<p>Here's a revised version of the text to make it clearer and free of spelling, grammar, and punctuation errors:</p>
<p>Running databases at scale can pose some common challenges that need to be addressed.</p>
<ul>
<li><p><strong>Absorbing significant increases in data volume</strong>: The explosion of data from sensors, connected machines, and other sources is increasing rapidly.</p>
</li>
<li><p><strong>Ensuring data security</strong>: Data breaches are becoming increasingly common, making it more important than ever to prioritize both data security and user accessibility.</p>
</li>
<li><p><strong>Keeping up with demand</strong>: For timely decision-making and seizing new opportunities, companies require real-time access to their data.</p>
</li>
<li><p><strong>Managing and maintaining the database and infrastructure</strong>: As data volumes increase and databases become more complex, companies face the expense of hiring additional talent to manage them.</p>
</li>
<li><p><strong>Removing scalability limits</strong>: A business needs to grow to survive, and its data management must grow with it. However, predicting the required capacity of on-premises databases can be challenging.</p>
</li>
<li><p><strong>Ensuring data residency, data sovereignty, or latency requirements</strong>: Some organizations have use cases that are better suited to run on-premises. In those cases, engineered systems that are pre-configured and pre-optimized for running the database are ideal..</p>
</li>
</ul>
<h1 id="heading-sql-databases">SQL databases</h1>
<p>A SQL or relational database is a collection of items that are related to each other and organized into tables consisting of columns and rows. These tables hold the information about the objects to be represented in the database. Each column in a table holds a specific type of data, and each field stores the actual value of an attribute. The rows in the table represent a collection of related values of one object or entity.</p>
<p>Each row in a table can be identified uniquely with a primary key, and the rows among multiple tables can be related to each other using foreign keys. This data can be accessed in many different ways without the need to reorganize the database tables themselves. SQL databases usually follow the <a target="_blank" href="https://nandan.dev/pages/courses/system-design/acid-and-base-consistency-models#base#acid">ACID consistency model</a>.</p>
<h2 id="heading-materialized-views">Materialized views</h2>
<p>A materialized view is a pre-computed dataset that is derived from a query specification and stored for later use. The data is pre-computed, which makes querying a materialized view faster than executing a query against the base table of the view. This performance difference is significant when a query is run frequently or is complex.</p>
<p>Materialized views enable data subsetting, which improves the performance of complex queries that run on large datasets and reduces network loads. Though there are other uses of materialized views, they are primarily used for performance and replication purposes.</p>
<h2 id="heading-n1-query-problem">N+1 query problem</h2>
<p>The N+1 query problem occurs when the data access layer executes additional SQL statements (N) to retrieve the same data that could have been obtained when executing the primary SQL query. The greater the value of N, the more queries will be executed, leading to a performance impact.</p>
<p>This problem is commonly observed in GraphQL and ORM (Object-Relational Mapping) tools. It can be solved by optimizing the SQL query or by using a dataloader that batches consecutive requests and makes a single data request under the hood.</p>
<h2 id="heading-advantages">Advantages</h2>
<p>Let’s look at some advantages of using relational databases:</p>
<ul>
<li><p>Simple and accurate</p>
</li>
<li><p>Accessibility</p>
</li>
<li><p>Data consistency</p>
</li>
<li><p>Flexibility</p>
</li>
</ul>
<h2 id="heading-disadvantages">Disadvantages</h2>
<p>Below are the disadvantages of relational databases:</p>
<ul>
<li><p>Expensive to maintain</p>
</li>
<li><p>Difficult schema evolution</p>
</li>
<li><p>Performance hits (join, denormalization, etc.)</p>
</li>
<li><p>Difficult to scale due to poor horizontal scalability</p>
</li>
</ul>
<h2 id="heading-examples">Examples</h2>
<p>Here are some commonly used relational databases:</p>
<ul>
<li><p><a target="_blank" href="https://www.postgresql.org/">PostgreSQL</a></p>
</li>
<li><p><a target="_blank" href="https://www.mysql.com/">MySQL</a></p>
</li>
<li><p><a target="_blank" href="https://mariadb.org/">MariaDB</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/rds/aurora">Amazon Aurora</a></p>
</li>
</ul>
<h1 id="heading-nosql-databases">NoSQL databases</h1>
<p>NoSQL refers to a category of databases that do not primarily use SQL as their data access language. These databases are also known as non-relational databases. Unlike relational databases, NoSQL databases do not require data to follow a predefined schema. NoSQL databases follow <a target="_blank" href="https://nandan.dev/pages/courses/system-design/acid-and-base-consistency-models#base">BASE consistency model</a>.</p>
<p>Below are different types of NoSQL databases:</p>
<h3 id="heading-document">Document</h3>
<p>A document database, also known as a document-oriented database or a document store, is a versatile database that stores information in the form of documents. These databases can be used for transactional and analytical applications.</p>
<p><strong>Advantages</strong></p>
<ul>
<li><p>Intuitive and flexible</p>
</li>
<li><p>Easy horizontal scaling</p>
</li>
<li><p>Schemaless</p>
</li>
</ul>
<p><strong>Disadvantages</strong></p>
<ul>
<li><p>Schemaless</p>
</li>
<li><p>Non-relational</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<ul>
<li><p><a target="_blank" href="https://www.mongodb.com/">MongoDB</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/documentdb">Amazon DocumentDB</a></p>
</li>
<li><p><a target="_blank" href="https://couchdb.apache.org/">CouchDB</a></p>
</li>
</ul>
<h3 id="heading-key-value">Key-value</h3>
<p>One of the simplest types of NoSQL databases, key-value databases save data as a group of key-value pairs made up of two data items each. They’re also sometimes referred to as a key-value store.</p>
<p><strong>Advantages</strong></p>
<ul>
<li><p>Simple and performant</p>
</li>
<li><p>Highly scalable for high volumes of traffic</p>
</li>
<li><p>Session management</p>
</li>
<li><p>Optimized lookups</p>
</li>
</ul>
<p><strong>Disadvantages</strong></p>
<ul>
<li><p>Basic CRUD</p>
</li>
<li><p>Values can’t be filtered</p>
</li>
<li><p>Lacks indexing and scanning capabilities</p>
</li>
<li><p>Not optimized for complex queries</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<ul>
<li><p><a target="_blank" href="https://redis.io/">Redis</a></p>
</li>
<li><p><a target="_blank" href="https://memcached.org/">Memcached</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/dynamodb">Amazon DynamoDB</a></p>
</li>
<li><p><a target="_blank" href="https://aerospike.com/">Aerospike</a></p>
</li>
</ul>
<h3 id="heading-graph">Graph</h3>
<p>A graph database is a type of NoSQL database that uses graph structures to store and represent data. Instead of tables or documents, it uses nodes, edges, and properties to create relationships between data items.</p>
<p>The nodes represent the data items, while the edges represent the relationships between them. The relationships allow data in the store to be linked together directly and can be retrieved with a single operation in many cases.</p>
<p><strong>Advantages</strong></p>
<ul>
<li><p>Query speed</p>
</li>
<li><p>Agile and flexible</p>
</li>
<li><p>Explicit data representation</p>
</li>
</ul>
<p><strong>Disadvantages</strong></p>
<ul>
<li><p>Complex</p>
</li>
<li><p>No standardized query language</p>
</li>
</ul>
<p><strong>Use cases</strong></p>
<ul>
<li><p>Fraud detection</p>
</li>
<li><p>Recommendation engines</p>
</li>
<li><p>Social networks</p>
</li>
<li><p>Network mapping</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<ul>
<li><p><a target="_blank" href="https://neo4j.com/">Neo4j</a></p>
</li>
<li><p><a target="_blank" href="https://www.arangodb.com/">ArangoDB</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/neptune">Amazon Neptune</a></p>
</li>
<li><p><a target="_blank" href="https://janusgraph.org/">JanusGraph</a></p>
</li>
</ul>
<h3 id="heading-time-series">Time series</h3>
<p>A time-series database is a database optimized for time-stamped, or time series, data.</p>
<p><strong>Advantages</strong></p>
<ul>
<li><p>Fast insertion and retrieval</p>
</li>
<li><p>Efficient data storage</p>
</li>
</ul>
<p><strong>Use cases</strong></p>
<ul>
<li><p>IoT data</p>
</li>
<li><p>Metrics analysis</p>
</li>
<li><p>Application monitoring</p>
</li>
<li><p>Understand financial trends</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<ul>
<li><p><a target="_blank" href="https://www.influxdata.com/">InfluxDB</a></p>
</li>
<li><p><a target="_blank" href="https://druid.apache.org/">Apache Druid</a></p>
</li>
</ul>
<h3 id="heading-wide-column">Wide column</h3>
<p>Wide column databases, also known as wide column stores, are schema-agnostic. Data is stored in column families, rather than in rows and columns.</p>
<p><strong>Advantages</strong></p>
<ul>
<li><p>Highly scalable, can handle petabytes of data</p>
</li>
<li><p>Ideal for real-time big data applications</p>
</li>
</ul>
<p><strong>Disadvantages</strong></p>
<ul>
<li><p>Expensive</p>
</li>
<li><p>Increased write time</p>
</li>
</ul>
<p><strong>Use cases</strong></p>
<ul>
<li><p>Business analytics</p>
</li>
<li><p>Attribute-based data storage</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<ul>
<li><p><a target="_blank" href="https://cloud.google.com/bigtable">BigTable</a></p>
</li>
<li><p><a target="_blank" href="https://cassandra.apache.org/">Apache Cassandra</a></p>
</li>
<li><p><a target="_blank" href="https://www.scylladb.com/">ScyllaDB</a></p>
</li>
</ul>
<h3 id="heading-multi-model">Multi-model</h3>
<p>Multi-model databases combine different database models (i.e. relational, graph, key-value, document, etc.) into a single, integrated backend. This means they can accommodate various data types, indexes, queries, and store data in more than one model.</p>
<p><strong>Advantages</strong></p>
<ul>
<li><p>Flexibility</p>
</li>
<li><p>Suitable for complex projects</p>
</li>
<li><p>Data consistent</p>
</li>
</ul>
<p><strong>Disadvantages</strong></p>
<ul>
<li><p>Complex</p>
</li>
<li><p>Less mature</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<ul>
<li><p><a target="_blank" href="https://www.arangodb.com/">ArangoDB</a></p>
</li>
<li><p><a target="_blank" href="https://azure.microsoft.com/en-in/services/cosmos-db">Azure Cosmos DB</a></p>
</li>
<li><p><a target="_blank" href="https://www.couchbase.com/">Couchbase</a></p>
</li>
</ul>
<p>That's all folks.</p>
<p>In the next blog, I will try to cover more about SQL V/s NoSQL Databases.</p>
<p>Feel free to comment on how you like my blog on the system design series or shoot me an email at <a target="_blank" href="http://mailto:connect@nandan.dev">connect@nandan.dev</a> If you have any queries I will try to answer them.</p>
<p>You can also visit my website to read some of the articles at <a target="_blank" href="https://nandan.dev/">https://nandan.dev/</a></p>
<p>Stay tuned &amp; connect with me on my social media channels. Subscribe to my newsletter to get regular updates on my upcoming posts.</p>
<p>If you're interested in learning about system design, you can join Design Guru's course on <a target="_blank" href="https://www.designgurus.io/course/grokking-system-design-fundamentals?aff=y0pphf">System Design Fundamentals</a>.</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_">Twitter</a> | <a target="_blank" href="https://www.instagram.com/nandandotdev">Instagram</a> | <a target="_blank" href="https://github.com/sirius93">Github</a> | <a target="_blank" href="https://nandan.dev">Website</a></p>
]]></content:encoded></item><item><title><![CDATA[System Design: Availablity, Scalability, and Types Of Storage.]]></title><description><![CDATA[Availability
Availability refers to the duration for which a system performs its intended function within a given timeframe. It measures the percentage of time a system or machine remains operational under normal circumstances, serving as a simple me...]]></description><link>https://blog.nandan.dev/system-design-availablity-scalability-and-types-of-storage</link><guid isPermaLink="true">https://blog.nandan.dev/system-design-availablity-scalability-and-types-of-storage</guid><category><![CDATA[System Architecture]]></category><category><![CDATA[System Design]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[technology]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Wed, 13 Dec 2023 03:58:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1702439760323/bf3f9a76-f0e4-4a56-a0c6-1e0ed4fc55c4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-availability">Availability</h2>
<p>Availability refers to the duration for which a system performs its intended function within a given timeframe. It measures the percentage of time a system or machine remains operational under normal circumstances, serving as a simple metric of operational continuity.</p>
<h3 id="heading-the-nines-of-availability">The Nine’s of availability</h3>
<p>The "Nine's of availability" is a way to measure the reliability and uptime of a system. It is expressed as a percentage or number of nines, where each "nine" represents an additional decimal place of uptime.</p>
<p>$$Availability = \frac{Uptime}{(Uptime + Downtime)}$$</p><p>If availability is 99.00% available, it is said to have “2 nines” of availability, and if it is 99.9%, it is called “3 nines”, and so on.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Availability (Percent)</strong></td><td><strong>Downtime (Year)</strong></td><td><strong>Downtime (Month)</strong></td><td><strong>Downtime (Week)</strong></td></tr>
</thead>
<tbody>
<tr>
<td>90% (one nine)</td><td>36.53 days</td><td>72 hours</td><td>16.8 hours</td></tr>
<tr>
<td>99% (two nines)</td><td>3.65 days</td><td>7.20 hours</td><td>1.68 hours</td></tr>
<tr>
<td>99.9% (three nines)</td><td>8.77 hours</td><td>43.8 minutes</td><td>10.1 minutes</td></tr>
<tr>
<td>99.99% (four nines)</td><td>52.6 minutes</td><td>4.32 minutes</td><td>1.01 minutes</td></tr>
<tr>
<td>99.999% (five nines)</td><td>5.25 minutes</td><td>25.9 seconds</td><td>6.05 seconds</td></tr>
<tr>
<td>99.9999% (six nines)</td><td>31.56 seconds</td><td>2.59 seconds</td><td>604.8 milliseconds</td></tr>
<tr>
<td>99.99999% (seven nines)</td><td>3.15 seconds</td><td>263 milliseconds</td><td>60.5 milliseconds</td></tr>
<tr>
<td>99.999999% (eight nines)</td><td>315.6 milliseconds</td><td>26.3 milliseconds</td><td>6 milliseconds</td></tr>
<tr>
<td>99.9999999% (nine nines)</td><td>31.6 milliseconds</td><td>2.6 milliseconds</td><td>0.6 milliseconds</td></tr>
</tbody>
</table>
</div><h2 id="heading-availability-in-sequence-vs-parallel">Availability in Sequence vs Parallel</h2>
<p>If a service consists of multiple components prone to failure, the service’s overall availability depends on whether the components are in sequence or in parallel.</p>
<h3 id="heading-sequence">Sequence</h3>
<p>Overall availability decreases when two components are in sequence.</p>
<p>$$Availability \space (Total) = Availability \space (Foo) * Availability \space (Bar)$$</p><p>For example, if both <code>Foo</code> and <code>Bar</code> each had 99.9% availability, their total availability in sequence would be 99.8%.</p>
<h3 id="heading-parallel">Parallel</h3>
<p>Overall availability increases when two components are in parallel.</p>
<p>$$Availability \space (Total) = 1 - (1 - Availability \space (Foo)) * (1 - Availability \space (Bar))$$</p><p>For example, if both <code>Foo</code> and <code>Bar</code> each had 99.9% availability, their total availability in parallel would be 99.9999%.</p>
<h2 id="heading-availability-vs-reliability">Availability vs Reliability</h2>
<p>If a system is reliable, it means it is available. However, if a system is available, it does not necessarily mean it is reliable. This means that a system with high reliability will also have high availability, but a system can still achieve high availability without being reliable.</p>
<h2 id="heading-high-availability-vs-fault-tolerance">High Availability vs Fault Tolerance</h2>
<p>Both high availability and fault tolerance are methods for achieving high uptime levels, but they work differently.</p>
<p>A fault-tolerant system ensures that there is no service interruption, but it comes at a higher cost. On the other hand, a highly available system minimizes service interruption. To achieve fault tolerance, hardware redundancy is essential. In case the main system fails, another system takes over without any loss in uptime.</p>
<h1 id="heading-scalability">Scalability</h1>
<p>Scalability is the measure of how well a system responds to changes by adding or removing resources to meet demands.</p>
<p><img src="https://nandan.dev/system-design/images/scalability.png" alt="scalability" /></p>
<p>Let’s discuss different types of scaling:</p>
<h2 id="heading-vertical-scaling">Vertical scaling</h2>
<p>Vertical scaling (also known as scaling up) expands a system’s scalability by adding more power to an existing machine. In other words, vertical scaling refers to improving an application’s capability via increasing hardware capacity.</p>
<h3 id="heading-advantages">Advantages</h3>
<ul>
<li><p>Simple to implement</p>
</li>
<li><p>Easier to manage</p>
</li>
<li><p>Data consistent</p>
</li>
</ul>
<h3 id="heading-disadvantages">Disadvantages</h3>
<ul>
<li><p>Risk of high downtime</p>
</li>
<li><p>Harder to upgrade</p>
</li>
<li><p>Can be a single point of failure</p>
</li>
</ul>
<h2 id="heading-horizontal-scaling">Horizontal scaling</h2>
<p>Horizontal scaling (also known as scaling out) expands a system’s scale by adding more machines. It improves the performance of the server by adding more instances to the existing pool of servers, allowing the load to be distributed more evenly.</p>
<h3 id="heading-advantages-1">Advantages</h3>
<ul>
<li><p>Increased redundancy</p>
</li>
<li><p>Better fault tolerance</p>
</li>
<li><p>Flexible and efficient</p>
</li>
<li><p>Easier to upgrade</p>
</li>
</ul>
<h3 id="heading-disadvantages-1">Disadvantages</h3>
<ul>
<li><p>Increased complexity</p>
</li>
<li><p>Data inconsistency</p>
</li>
<li><p>Increased load on downstream services</p>
</li>
</ul>
<h1 id="heading-storage">Storage</h1>
<p>Storage is a mechanism that enables a system to retain data, either temporarily or permanently. This topic is mostly skipped over in the context of system design, however, it is important to have a basic understanding of some common types of storage techniques that can help us fine-tune our storage components. Let’s discuss some important storage concepts:</p>
<h2 id="heading-raid">RAID</h2>
<p>RAID (Redundant Array of Independent Disks) is a way of storing the same data on multiple hard disks or solid-state drives (SSDs) to protect data in the case of a drive failure.</p>
<p>There are different RAID levels, however, and not all have the goal of providing redundancy. Let’s discuss some commonly used RAID levels:</p>
<ul>
<li><p><strong>RAID 0</strong>: Also known as striping, data is split evenly across all the drives in the array.</p>
</li>
<li><p><strong>RAID 1</strong>: Also known as mirroring, at least two drives contains the exact copy of a set of data. If a drive fails, others will still work.</p>
</li>
<li><p><strong>RAID 5</strong>: Striping with parity. Requires the use of at least 3 drives, striping the data across multiple drives like RAID 0, but also has a parity distributed across the drives.</p>
</li>
<li><p><strong>RAID 6</strong>: Striping with double parity. RAID 6 is like RAID 5, but the parity data are written to two drives.</p>
</li>
<li><p><strong>RAID 10</strong>: Combines striping plus mirroring from RAID 0 and RAID 1. It provides security by mirroring all data on secondary drives while using striping across each set of drives to speed up data transfers.</p>
</li>
</ul>
<h3 id="heading-comparison">Comparison</h3>
<p>Let’s compare all the features of different RAID levels:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Features</strong></td><td><strong>RAID 0</strong></td><td><strong>RAID 1</strong></td><td><strong>RAID 5</strong></td><td><strong>RAID 6</strong></td><td><strong>RAID 10</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Description</td><td>Striping</td><td>Mirroring</td><td>Striping with Parity</td><td>Striping with double parity</td><td>Striping and Mirroring</td></tr>
<tr>
<td>Minimum Disks</td><td>2</td><td>2</td><td>3</td><td>4</td><td>4</td></tr>
<tr>
<td>Read Performance</td><td>High</td><td>High</td><td>High</td><td>High</td><td>High</td></tr>
<tr>
<td>Write Performance</td><td>High</td><td>Medium</td><td>High</td><td>High</td><td>Medium</td></tr>
<tr>
<td>Cost</td><td>Low</td><td>High</td><td>Low</td><td>Low</td><td>High</td></tr>
<tr>
<td>Fault Tolerance</td><td>None</td><td>Single-drive failure</td><td>Single-drive failure</td><td>Two-drive failure</td><td>Up to one disk failure in each sub-array</td></tr>
<tr>
<td>Capacity Utilization</td><td>100%</td><td>50%</td><td>67%-94%</td><td>50%-80%</td><td>50%</td></tr>
</tbody>
</table>
</div><h2 id="heading-volumes">Volumes</h2>
<p>"Volume" refers to a fixed amount of storage space on a disk or tape. Although the term "volume" is often used interchangeably with "storage," it is important to note that a single disk can contain multiple volumes, or a volume can span across multiple disks.</p>
<h2 id="heading-file-storage">File storage</h2>
<p>File storage is a method used to store data in a hierarchical directory structure and present it to users. It is a user-friendly solution that provides easy storage and retrieval of files. In order to locate a file in file storage, one must know the complete path of the file. It is an economical and well-structured method that is commonly used on hard drives. This means that files appear exactly the same for the user and on the hard drive.</p>
<p>Example: <a target="_blank" href="https://aws.amazon.com/efs">Amazon EFS</a>, <a target="_blank" href="https://azure.microsoft.com/en-in/services/storage/files">Azure files</a>, <a target="_blank" href="https://cloud.google.com/filestore">Google Cloud Filestore</a>, etc.</p>
<h2 id="heading-block-storage">Block storage</h2>
<p>Block storage is a method of storing data by dividing it into blocks or chunks, which are then stored as separate entities. Each block is given a unique identifier, which enables the system to store the smaller pieces of data wherever it is most convenient.</p>
<p>Block storage separates data from user environments, making it possible for data to be spread across multiple environments. This creates multiple paths to the data and enables users to retrieve it quickly. When a user or application requests data from the block storage system, the system reassembles the data blocks and presents the data to the user or application.</p>
<p>Example: <a target="_blank" href="https://aws.amazon.com/ebs">Amazon EBS</a>.</p>
<h2 id="heading-object-storage">Object Storage</h2>
<p>Object storage, also known as object-based storage, divides data files into smaller units called objects. These objects are then stored in a single repository that can be distributed across multiple networked systems.</p>
<p>Example: <a target="_blank" href="https://aws.amazon.com/s3">Amazon S3</a>, <a target="_blank" href="https://azure.microsoft.com/en-in/services/storage/blobs">Azure Blob Storage</a>, <a target="_blank" href="https://cloud.google.com/storage">Google Cloud Storage</a>, etc.</p>
<h2 id="heading-nas">NAS</h2>
<p>A Network Attached Storage (NAS) is a device that is connected to a network and allows authorized users to store and retrieve data from a central location. The NAS device is flexible, which means that we can easily add more storage capacity as needed. It is faster and less expensive than using a public cloud service, and it provides all the benefits of cloud storage while giving us complete control over our data.</p>
<h2 id="heading-hdfs">HDFS</h2>
<p>The Hadoop Distributed File System (HDFS) is a file system that can be distributed across multiple machines in a cluster. It's designed to run on inexpensive hardware and is highly fault-tolerant. HDFS offers high-throughput access to large datasets, making it ideal for applications that require handling of massive data.</p>
<p>HDFS uses a block-based approach for storing files. Each file is divided into a sequence of blocks, with all blocks in a file (except the last one) being of the same size. For reliability, the blocks of a file are replicated across different machines in the cluster.</p>
<p>That's all folks.</p>
<p>In the next blog, I will try to cover more about Databases.</p>
<p>Feel free to comment on how you like my blog on the system design series or shoot me a mail at <a target="_blank" href="http://mailto:connect@nandan.dev">connect@nandan.dev</a> If you have any queries I will try to answer.</p>
<p>You can also visit my website to read some of the articles at <a target="_blank" href="https://nandan.dev/">https://nandan.dev/</a></p>
<p>Stay tuned &amp; connect with me on my social media channels. Subscribe to my newsletter to get regular updates on my upcoming posts.</p>
<p>If you're interested in learning about system design, you can join Design Guru's course on <a target="_blank" href="https://www.designgurus.io/course/grokking-system-design-fundamentals?aff=y0pphf">System Design Fundamentals</a>.</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_">Twitter</a> | <a target="_blank" href="https://www.instagram.com/nandandotdev">Instagram</a> | <a target="_blank" href="https://github.com/sirius93">Github</a> | <a target="_blank" href="https://nandan.dev">Website</a></p>
]]></content:encoded></item><item><title><![CDATA[The Power of ChatGPT Prompts: Unleashing Creativity and Utility..!!]]></title><description><![CDATA[In the world of artificial intelligence, ChatGPT is a breakthrough that has captured the attention of developers, writers, and businesses alike. It's an AI model developed by OpenAI, powered by GPT-3 technology, and it's revolutionizing the way we in...]]></description><link>https://blog.nandan.dev/the-power-of-chatgpt-prompts-unleashing-creativity-and-utility</link><guid isPermaLink="true">https://blog.nandan.dev/the-power-of-chatgpt-prompts-unleashing-creativity-and-utility</guid><category><![CDATA[chatgpt]]></category><category><![CDATA[#PromptEngineering]]></category><category><![CDATA[prompts]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Tue, 03 Oct 2023 02:30:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1696299897177/8c8cc38c-6bd7-4faa-8d43-94e985cd108e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the world of artificial intelligence, ChatGPT is a breakthrough that has captured the attention of developers, writers, and businesses alike. It's an AI model developed by OpenAI, powered by GPT-3 technology, and it's revolutionizing the way we interact with and leverage artificial intelligence. At the heart of this innovation are prompts, a simple yet remarkably powerful concept that drives ChatGPT's capabilities.</p>
<h2 id="heading-the-basics-of-prompts"><strong>The Basics of Prompts</strong></h2>
<p>To understand the magic behind ChatGPT, let's start with the basics. A prompt is essentially a piece of text or code that you provide to the AI model to initiate a conversation or request a specific task. It's like giving instructions to an intelligent assistant, and ChatGPT responds based on the input you provide.</p>
<p>For example, if you want to ask ChatGPT to generate a creative story about a space adventure, your prompt might look like this:</p>
<pre><code class="lang-xml">"Once upon a time, in a galaxy far, far away, there was a brave astronaut named Buzz Lightyear. Write a story about Buzz's thrilling journey through the cosmos."
</code></pre>
<p>The prompt instructs ChatGPT to generate a story about an astronaut named Buzz Lightyear in a space adventure setting. What makes this fascinating is that prompts can be as simple or as complex as you need them to be.</p>
<h2 id="heading-creative-writing"><strong>Creative Writing</strong></h2>
<p>Prompts have opened up exciting possibilities for writers and content creators. ChatGPT can assist with brainstorming ideas, generating story outlines, and even producing entire articles or pieces of fiction. By providing a well-crafted prompt, you can tap into the AI's creative abilities to jumpstart your writing process.</p>
<p>For instance, you could use ChatGPT to:</p>
<ul>
<li><p>Generate creative story plots or twists.</p>
</li>
<li><p>Compose catchy headlines for blog posts or articles.</p>
</li>
<li><p>Draft engaging dialogue for characters in a novel.</p>
</li>
<li><p>Create captivating product descriptions for e-commerce websites.</p>
</li>
</ul>
<p>The flexibility of prompts makes ChatGPT an invaluable tool for writers seeking inspiration or assistance with their projects.</p>
<h2 id="heading-problem-solving-and-information-retrieval"><strong>Problem Solving and Information Retrieval</strong></h2>
<p>Prompts are not limited to creative writing; they are equally adept at solving problems and retrieving information. ChatGPT can be your virtual assistant for tasks such as:</p>
<ul>
<li><p>Answering questions on a wide range of topics.</p>
</li>
<li><p>Providing code snippets for programming challenges.</p>
</li>
<li><p>Translating text from one language to another.</p>
</li>
<li><p>Summarizing lengthy documents or articles.</p>
</li>
<li><p>Assisting with research by generating ideas and insights.</p>
</li>
</ul>
<p>By crafting precise prompts, you can harness the AI's knowledge and problem-solving abilities to tackle a variety of tasks efficiently.</p>
<h2 id="heading-conversation-and-chatbots"><strong>Conversation and Chatbots</strong></h2>
<p>ChatGPT is also well-suited for creating chatbots and simulating human-like conversations. You can use prompts to initiate dialogue with the AI model and build chat-based applications. Whether it's for customer support, virtual assistants, or interactive storytelling, ChatGPT's conversation capabilities are impressive and adaptable.</p>
<h2 id="heading-tips-for-crafting-effective-prompts"><strong>Tips for Crafting Effective Prompts</strong></h2>
<p>While prompts are incredibly versatile, there are a few tips to keep in mind when crafting them:</p>
<ol>
<li><p><strong>Be Clear and Specific</strong>: Clearly define your request or question in the prompt to get the desired response.</p>
</li>
<li><p><strong>Experiment</strong>: Don't be afraid to experiment with different prompts to see what works best for your needs.</p>
</li>
<li><p><strong>Use Context</strong>: You can provide context in your prompts, reference previous messages in a conversation, or set the persona of the AI to guide the responses.</p>
</li>
<li><p><strong>Review Output</strong>: Always review and edit the output generated by ChatGPT to ensure accuracy and coherence.</p>
</li>
</ol>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Prompts are the gateway to unlocking the potential of ChatGPT. Whether you're a writer seeking creative inspiration, a developer building applications, or someone looking for quick answers and solutions, ChatGPT's capabilities are at your fingertips. With a well-crafted prompt, you can harness the power of artificial intelligence to enhance your productivity and creativity.</p>
<p>As technology continues to advance, the possibilities for ChatGPT prompts are only limited by our imagination. So, go ahead, experiment, and see how ChatGPT can assist you in achieving your goals and bringing your ideas to life. The future of AI-powered interactions is here, and it all starts with a prompt.</p>
<p>Feel free to comment on how you like my blog or shoot me an mail at <a target="_blank" href="mailto:connect@nandan.dev"><strong>connect@nandan.dev</strong></a> If you have any queries and I will try to answer.</p>
<p>You can also visit my website to read some of the articles at <a target="_blank" href="http://nandan.dev"><strong>nandan.dev</strong></a></p>
<p>Stay tuned &amp; connect with me on my social media channels. Make sure to subscribe to my newsletter to get regular updates on my upcoming posts.</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.instagram.com/_sirius93_"><strong>Instagram</strong></a> | <a target="_blank" href="https://github.com/sirius93"><strong>Github</strong></a> | <a target="_blank" href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Design Patterns: Module Design Pattern in JavaScript]]></title><description><![CDATA[Module Design Pattern is a way to encapsulate and organize code in a self-containing module that can expose certain functionalities while keeping the rest of the code private.
This helps prevent variable and function name clashes, improves code maint...]]></description><link>https://blog.nandan.dev/design-patterns-module-design-pattern-in-javascript</link><guid isPermaLink="true">https://blog.nandan.dev/design-patterns-module-design-pattern-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[design patterns]]></category><category><![CDATA[module-pattern]]></category><category><![CDATA[learn coding]]></category><dc:creator><![CDATA[Nandan Kumar]]></dc:creator><pubDate>Fri, 18 Aug 2023 16:41:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692376676555/d6d19109-6e9f-4605-864b-83682ab0f4b7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Module Design Pattern is a way to encapsulate and organize code in a self-containing module that can expose certain functionalities while keeping the rest of the code private.</p>
<p>This helps prevent variable and function name clashes, improves code maintainability, and promotes the concept of separation of concerns.</p>
<p>Here's an example of how you can implement the Module Design Pattern in JavaScript:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Module using the Module Design Pattern</span>
<span class="hljs-keyword">var</span> Module = (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Private variable</span>
  <span class="hljs-keyword">var</span> privateVariable = <span class="hljs-string">"I am a private variable"</span>;

  <span class="hljs-comment">// Private function</span>
  <span class="hljs-keyword">var</span> privateFunction = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">privateFunction</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"This is a private function"</span>);
  }

  <span class="hljs-comment">// Public function</span>
  <span class="hljs-keyword">var</span> publicFunction = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"This is a public function"</span>);
  }
  <span class="hljs-comment">// Public interface</span>
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">publicFunction</span>:publicFunction,
    <span class="hljs-comment">// Public variable accessing private variable</span>
    <span class="hljs-attr">publicVariable</span>: privateVariable
  };
})();

<span class="hljs-comment">// Usage of the module</span>
<span class="hljs-built_in">console</span>.log(Module.publicVariable); <span class="hljs-comment">// Output: "I am a private variable"</span>
Module.publicFunction(); <span class="hljs-comment">// Output: "This is a public function"</span>

<span class="hljs-comment">// Trying to access private members directly (will result in an error)</span>
<span class="hljs-built_in">console</span>.log(Module.privateVariable); <span class="hljs-comment">// Output: undefined</span>
Module.privateFunction(); <span class="hljs-comment">// Output: Uncaught TypeError: MyModule.privateFunction is not a function</span>
</code></pre>
<p>In the above example, we've created a module called <code>Module</code> using an <strong>Immediately Invoked Function Expression (IIFE)</strong>. Inside the IIFE, we define private variables and functions that are not accessible from outside the module. We then return an object containing the public members (functions and variables) that we want to expose.</p>
<p>Benefits of the Module Design Pattern:</p>
<ol>
<li><p><strong>Encapsulation:</strong> Private members are not accessible from outside the module, which helps prevent unintended modifications or conflicts with other parts of the codebase.</p>
</li>
<li><p><strong>Organization:</strong> Code is organized into logical modules, making it easier to manage and understand.</p>
</li>
<li><p><strong>Namespacing:</strong> The module acts as a namespace, reducing the chances of naming collisions in a global scope.</p>
</li>
<li><p><strong>Reusability:</strong> Modules can be easily reused in different parts of your application or different projects.</p>
</li>
<li><p><strong>Maintenance:</strong> Separation of concerns and encapsulation make it easier to maintain and refactor code.</p>
</li>
</ol>
<p>That's a concise explanation of the module design pattern in Javascript.</p>
<p>Feel free to comment on how you like my blog or shoot me a mail at <a target="_blank" href="mailto:connect@nandan.dev"><strong>connect@nandan.dev</strong></a> If you have any queries and I will try to answer.</p>
<p>You can also visit my website to read some of the articles at <a target="_blank" href="http://nandan.dev"><strong>nandan.dev</strong></a></p>
<p>Stay tuned &amp; connect with me on my social media channels. Make sure to subscribe to my newsletter to get regular updates on my upcoming posts.</p>
<p><a target="_blank" href="https://twitter.com/_sirius93_"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.instagram.com/_sirius93_"><strong>Instagram</strong></a> | <a target="_blank" href="https://github.com/sirius93"><strong>Github</strong></a> | <a target="_blank" href="https://nandan.dev/"><strong>Website</strong></a></p>
]]></content:encoded></item></channel></rss>