<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ateeb Hussain</title>
    <description>The latest articles on DEV Community by Ateeb Hussain (@mateebhussain).</description>
    <link>https://dev.to/mateebhussain</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3732276%2Ff32e59ca-ad9e-4cd9-9584-cbe8a8a13957.png</url>
      <title>DEV Community: Ateeb Hussain</title>
      <link>https://dev.to/mateebhussain</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mateebhussain"/>
    <language>en</language>
    <item>
      <title>Why is your Database Index actually fast?</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Mon, 20 Apr 2026 06:55:11 +0000</pubDate>
      <link>https://dev.to/mateebhussain/why-is-your-database-index-actually-fast-74h</link>
      <guid>https://dev.to/mateebhussain/why-is-your-database-index-actually-fast-74h</guid>
      <description>&lt;p&gt;We’ve spent all week talking about Redis Hashes, Bloom Filters, and Tries. &lt;/p&gt;

&lt;p&gt;But let’s be real: 90% of the time, you’re just hitting a &lt;strong&gt;PostgreSQL Index&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Ever wondered what’s actually happening when you add &lt;code&gt;@unique&lt;/code&gt; or &lt;code&gt;CREATE INDEX&lt;/code&gt;? It’s not just a "magic list." It’s almost always a &lt;strong&gt;B+ Tree&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a B+ Tree?
&lt;/h3&gt;

&lt;p&gt;Think of it as a perfectly balanced, multi-level library. &lt;/p&gt;

&lt;p&gt;In a normal Binary Tree, you only have two branches. In a &lt;strong&gt;B+ Tree&lt;/strong&gt;, each "node" can have hundreds of branches. This means even if you have &lt;strong&gt;BILLIONS&lt;/strong&gt; of records, the database only needs to look at 3 or 4 levels to find your specific data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why B+ Trees are the GOAT:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Range Queries:&lt;/strong&gt; Unlike Redis Hashes (which are bad at "give me users with ID 10 to 50"), B+ Trees keep all their data at the "leaves" in a linked list. You can slide across them instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disk Friendly:&lt;/strong&gt; Databases don't live in RAM (usually). B+ Trees are designed to be read from your SSD in "blocks," making them incredibly efficient for physical storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Balanced Performance:&lt;/strong&gt; Whether you’re searching for the 1st record or the 1,000,000th, the time taken ($O(\log n)$) is virtually the same.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Reality Check:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redis Hash:&lt;/strong&gt; Instant, but expensive (RAM).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B+ Tree (SQL Index):&lt;/strong&gt; Very fast, cheaper (Disk), and handles ranges like a pro.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This is why I keep saying:&lt;/strong&gt; For my 10k user projects, a well-indexed Postgres table is more than enough. You don't need to over-engineer until you actually have the traffic to justify it.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series Wrap-up:&lt;/strong&gt;&lt;br&gt;
We’ve covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis Hashes (Storage efficiency)&lt;/li&gt;
&lt;li&gt;Bloom Filters (The Bouncer)&lt;/li&gt;
&lt;li&gt;Trie Structures (Prefix/Search)&lt;/li&gt;
&lt;li&gt;B+ Trees (The Foundation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The most important skill isn't knowing how to build these—it's knowing WHICH one to pick for your specific problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s a wrap on this System Design series! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which of these four surprised you the most? Or is there another structure I missed? Let’s nerd out in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>devops</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How do "Suggested Usernames" actually work?</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Sun, 19 Apr 2026 11:47:00 +0000</pubDate>
      <link>https://dev.to/mateebhussain/how-do-suggested-usernames-actually-work-45hd</link>
      <guid>https://dev.to/mateebhussain/how-do-suggested-usernames-actually-work-45hd</guid>
      <description>&lt;p&gt;We’ve talked about Redis Hashes for storage and Bloom Filters for the "Bouncer" at the door. &lt;/p&gt;

&lt;p&gt;But what happens when &lt;code&gt;ateebHussain&lt;/code&gt; is taken and you need to suggest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ateebHussain_1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ateebHussain_pro&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ateeb_h&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try to do this with &lt;code&gt;LIKE %ateeb%&lt;/code&gt; in a SQL database with 10 million rows... &lt;strong&gt;RIP your latency.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter: The Trie Structure (Prefix Tree)
&lt;/h3&gt;

&lt;p&gt;A Trie is a specialized tree used to store associative arrays. Instead of storing whole strings, it stores characters as nodes. &lt;/p&gt;

&lt;p&gt;Imagine it like a path:&lt;br&gt;
&lt;code&gt;A&lt;/code&gt; -&amp;gt; &lt;code&gt;T&lt;/code&gt; -&amp;gt; &lt;code&gt;E&lt;/code&gt; -&amp;gt; &lt;code&gt;E&lt;/code&gt; -&amp;gt; &lt;code&gt;B&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this is a Game Changer:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Autocomplete in $O(L)$ Time:&lt;/strong&gt; Where $L$ is the length of the word. It doesn't matter if your DB has 1 billion names; finding "ateeb" takes the same amount of time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefix Matching:&lt;/strong&gt; It’s built for "starts with" queries. You can find every username starting with "ateeb" in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Sharing:&lt;/strong&gt; Usernames like &lt;code&gt;ateeb1&lt;/code&gt;, &lt;code&gt;ateeb2&lt;/code&gt;, and &lt;code&gt;ateeb_dev&lt;/code&gt; all share the same "ateeb" prefix nodes. It’s incredibly efficient for overlapping data.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Where to use it?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Search Bars:&lt;/strong&gt; That "Search as you type" feature? Probably a Trie.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username Suggestions:&lt;/strong&gt; Instantly finding the next available variation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP Routing:&lt;/strong&gt; High-speed networking uses this to route your data packets!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Building a Trie from scratch in production can be overkill for a small LMS or a simple store. But understanding how it works separates the "I just use frameworks" devs from the "I design systems" engineers.&lt;/p&gt;

&lt;p&gt;If you’re using &lt;strong&gt;Next.js&lt;/strong&gt;, you can actually implement a simple version of this in an Edge Function for insane performance. &lt;/p&gt;

&lt;p&gt;Next post: &lt;strong&gt;B+ Trees.&lt;/strong&gt; (The reason your PostgreSQL indexes actually work).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you ever tried implementing a Trie, or do you just let your frontend handle the filtering? Let’s talk architecture!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>javascript</category>
      <category>devops</category>
    </item>
    <item>
      <title>How bloom filters work, irl</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Thu, 16 Apr 2026 16:35:33 +0000</pubDate>
      <link>https://dev.to/mateebhussain/how-bloom-filters-work-irl-52j0</link>
      <guid>https://dev.to/mateebhussain/how-bloom-filters-work-irl-52j0</guid>
      <description>&lt;p&gt;A single &lt;code&gt;SELECT&lt;/code&gt; for every "Username Taken" check? &lt;br&gt;
Seriously??&lt;/p&gt;

&lt;p&gt;Imagine you're building a SaaS and 10,000 bots try to sign up with random usernames in one minute.&lt;/p&gt;

&lt;p&gt;Are you really gonna let your Postgres/MySQL handle 10,000 unnecessary hits just to say "Nope, doesn't exist"? &lt;br&gt;
&lt;strong&gt;That’s how you kill your DB performance and blow your AWS budget on RDS IOPS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tech Giants don't even &lt;em&gt;touch&lt;/em&gt; the database to check if a username is free. They use a &lt;strong&gt;Bloom Filter&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Bouncer" at the door
&lt;/h3&gt;

&lt;p&gt;Think of a Bloom Filter as a high-speed bouncer. He doesn't have a list of names; he just has a row of 1,000 switches (bits).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a user signs up, you run their name through a &lt;strong&gt;Hash Function&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It flips a few switches to &lt;strong&gt;ON&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When the next person checks a name, the bouncer just looks at the switches. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Result?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the switches are &lt;strong&gt;OFF&lt;/strong&gt;: The username 100% DOES NOT exist. (Database: 0 hits. Speed: Lightning). &lt;/li&gt;
&lt;li&gt;If the switches are &lt;strong&gt;ON&lt;/strong&gt;: It &lt;em&gt;might&lt;/em&gt; exist. &lt;em&gt;Now&lt;/em&gt; you go check the DB.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why this is a Flex:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory is Cheap:&lt;/strong&gt; You can track &lt;strong&gt;10 MILLION&lt;/strong&gt; usernames in about 10MB of RAM. Try doing that with a SQL Index. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Disk I/O:&lt;/strong&gt; Your database stays chill while the Bloom Filter handles the noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; Even if someone hacks your Redis, they can't see the usernames. It’s just bits, baby.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Real Talk:&lt;/strong&gt;&lt;br&gt;
I know, I know... "Ateeb, I only have 500 users, I'll just use &lt;code&gt;@unique&lt;/code&gt; in Prisma." &lt;br&gt;
Fine. Use your sledgehammer for a nut.&lt;/p&gt;

&lt;p&gt;But if you’re planning to scale to the moon, you need to stop being lazy with your architecture. &lt;/p&gt;

&lt;p&gt;Next up: &lt;strong&gt;Trie Structures.&lt;/strong&gt; (How to do those "Suggested Usernames" without making your server sweat).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you still "Select-ing" your way to a massive AWS bill, or are you actually optimizing? Fight me in the comments! 👇&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>devops</category>
    </item>
    <item>
      <title>Claude Code's architecture is pretty insane, isn't?</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Wed, 15 Apr 2026 10:06:15 +0000</pubDate>
      <link>https://dev.to/mateebhussain/claude-codes-architecture-is-pretty-insane-isnt-5dep</link>
      <guid>https://dev.to/mateebhussain/claude-codes-architecture-is-pretty-insane-isnt-5dep</guid>
      <description>&lt;p&gt;Let's talk about it...&lt;/p&gt;

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

</description>
    </item>
    <item>
      <title>Wow, now we can have filtered feeds.

Discover/Following/Latest --- Nice</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Tue, 14 Apr 2026 09:58:33 +0000</pubDate>
      <link>https://dev.to/mateebhussain/wow-now-we-can-have-filtered-feedsdiscoverfollowinglatest-nice-2m3c</link>
      <guid>https://dev.to/mateebhussain/wow-now-we-can-have-filtered-feedsdiscoverfollowinglatest-nice-2m3c</guid>
      <description></description>
    </item>
    <item>
      <title>How Redis Hashes actually work?</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Tue, 14 Apr 2026 09:56:03 +0000</pubDate>
      <link>https://dev.to/mateebhussain/how-redis-hashes-actually-work-4pij</link>
      <guid>https://dev.to/mateebhussain/how-redis-hashes-actually-work-4pij</guid>
      <description>&lt;p&gt;If you’re just using Redis to &lt;code&gt;SET&lt;/code&gt; and &lt;code&gt;GET&lt;/code&gt; basic strings, you’re basically using a Ferrari to drive to the grocery store. You’re leaving money on the table.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Dictionary inside a Dictionary"
&lt;/h3&gt;

&lt;p&gt;Stop thinking of Redis as just one giant list of keys. A &lt;strong&gt;Redis Hash&lt;/strong&gt; is basically a container. &lt;/p&gt;

&lt;p&gt;Instead of doing this:&lt;br&gt;
&lt;code&gt;user:username:ateeb&lt;/code&gt; -&amp;gt; &lt;code&gt;"active"&lt;/code&gt;&lt;br&gt;
&lt;code&gt;user:email:ateeb&lt;/code&gt; -&amp;gt; &lt;code&gt;"ateeb@example.com"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You do this:&lt;br&gt;
&lt;code&gt;user:ateeb&lt;/code&gt; -&amp;gt; { &lt;code&gt;status&lt;/code&gt;: &lt;code&gt;"active"&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;: &lt;code&gt;"ateeb@example.com"&lt;/code&gt; }&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do Tech Giants love this? (The nerdy bit)
&lt;/h3&gt;

&lt;p&gt;It’s all about &lt;strong&gt;Memory Optimization.&lt;/strong&gt; Redis is smart. If your Hash is small, it uses something called a &lt;strong&gt;ZipList&lt;/strong&gt;. It literally squashes your data together to save space. &lt;/p&gt;

&lt;p&gt;When that Hash gets huge, it automatically switches to a &lt;strong&gt;HashTable&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$O(1)$ Lookup:&lt;/strong&gt; It doesn't matter if you have 100 users or 100 million. It finds that username instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheaper Bills:&lt;/strong&gt; You’re storing way more data in less RAM. Your AWS bill will thank you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Trade-offs (Because nothing is perfect)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Good:&lt;/strong&gt; Insanely fast. Perfect for "Hot Data" (users logging in constantly).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Bad:&lt;/strong&gt; You can’t set an expiry time (TTL) on just &lt;em&gt;one&lt;/em&gt; field. It’s all or nothing for the whole hash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Ugly:&lt;/strong&gt; If you need to search for "usernames starting with A," a Hash is NOT your friend. Use a &lt;strong&gt;Trie&lt;/strong&gt; for that (covering that next!).&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Real Talk:&lt;/strong&gt;&lt;br&gt;
I’m still rocking PostgreSQL with &lt;code&gt;@unique&lt;/code&gt; for my 10k user projects. You don't need a sledgehammer to crack a nut. But when the nut is the size of a planet? You use Redis.&lt;/p&gt;

&lt;p&gt;Next post: &lt;strong&gt;Bloom Filters.&lt;/strong&gt; (How to tell if a username is taken WITHOUT even checking the DB).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s your go-to? Do you dump everything into one Hash, or do you prefer flat strings? Let's talk (keep it chill this time! 😅) 👇&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>redis</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Using Next.js with Redis</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Mon, 13 Apr 2026 16:33:38 +0000</pubDate>
      <link>https://dev.to/mateebhussain/using-nextjs-with-redis-4693</link>
      <guid>https://dev.to/mateebhussain/using-nextjs-with-redis-4693</guid>
      <description>&lt;p&gt;Is Redis (Upstash Redis) with Next.js a good matchup?&lt;/p&gt;

&lt;p&gt;Another week of developing ECourses, an LMS SaaS (also opensource)&lt;/p&gt;

&lt;p&gt;I implemented caching, or more precisely redis cache for the first time.&lt;/p&gt;

&lt;p&gt;So... the previous was sending 10 database read queries while creating a single course. &lt;br&gt;
What is that? Probably because I was fetching instructors to assign to a course and then for filtering too.&lt;/p&gt;

&lt;p&gt;At first, I was using TanStack React Query for caching until it hit me that the cache is session based and temporary.&lt;/p&gt;

&lt;p&gt;SSR or ISR are a bit headache because form is on client-side code.&lt;/p&gt;

&lt;p&gt;So, it was time add some caching layers.&lt;br&gt;
I already had React Query cache implemented, what need was redis to store recent data from database and next.js cache to store requested data for a short period of time – or until invalidated&lt;/p&gt;

&lt;p&gt;The current caching flow looks like this:&lt;br&gt;
 User --&amp;gt; React Query --&amp;gt; NextJS Cache --&amp;gt; Redis Cache --&amp;gt; Database (PostgreSQL)&lt;/p&gt;

&lt;p&gt;So, what are your thoughts about this flow...? I don't have a large group of users but I want to stay under the free stack, and hashtag#prisma postgres provides 10 concurrent connections with 100,000 operations per month.&lt;/p&gt;

&lt;p&gt;P.S. What was your first caching flow you created?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Why let over var in JavaScript?</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Sun, 12 Apr 2026 10:40:38 +0000</pubDate>
      <link>https://dev.to/mateebhussain/why-let-over-var-in-javascript-2o07</link>
      <guid>https://dev.to/mateebhussain/why-let-over-var-in-javascript-2o07</guid>
      <description>&lt;p&gt;I didn't get it...&lt;/p&gt;

&lt;p&gt;This is ECourses Progress Report 2, and you're witnessing AI taking over.&lt;/p&gt;

&lt;p&gt;When I was having Claude writing three separate forms, routes and actions based on my previous code...&lt;/p&gt;

&lt;p&gt;I stumbled upon something outrageous,&lt;br&gt;
&lt;code&gt;let retries = 0;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What is it? – I don't know, truly...&lt;/p&gt;

&lt;p&gt;Basically, there are two ways to store data in JavaScript, in-fact everywhere&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Constants and&lt;/li&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While declaring constants is pretty straight forward,&lt;br&gt;
variables can be declared in two ways,&lt;br&gt;
using &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;var&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For years, var has dominated the JavaScript files until let stepped in.&lt;br&gt;
Why?&lt;/p&gt;

&lt;p&gt;var has a major risk. It is Function-based Scoped. (maybe I spelled it wrong)&lt;br&gt;
BTW, the meaning is that once a variable is declared with var:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;var retries = 0;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can access the &lt;code&gt;retries&lt;/code&gt; variable anywhere inside the function, this poses a threat in some cases.&lt;br&gt;
While, using let for the same thing would restrict its scope to the parent code block i.e. curly braces &lt;code&gt;{ &amp;amp; }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There is more to it.&lt;/p&gt;

&lt;p&gt;HOISTING !!!!!&lt;/p&gt;

&lt;p&gt;The legendary NIGHTMARE for interviewee (Hope it is correct...)&lt;/p&gt;

&lt;p&gt;So, var initializes the variable with null or undefined, while let stores it inside a Dead Zone the variable does not get initialized unless you do so...&lt;/p&gt;

&lt;p&gt;The era of &lt;code&gt;var&lt;/code&gt; came to an end,&lt;br&gt;
And now, most files are dominated with &lt;code&gt;let&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Summary: ECourses is progressing butterly smooth...&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to lookup for usernames with a BILLION users?</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Fri, 10 Apr 2026 05:30:14 +0000</pubDate>
      <link>https://dev.to/mateebhussain/how-to-lookup-for-usernames-with-a-billion-users-32ef</link>
      <guid>https://dev.to/mateebhussain/how-to-lookup-for-usernames-with-a-billion-users-32ef</guid>
      <description>&lt;p&gt;It ain't this simple !!&lt;br&gt;
A single damn SELECT query is not enough for your application !&lt;/p&gt;

&lt;p&gt;Imagine that you're building a SaaS that requires username lookups every time a user signs-in.&lt;/p&gt;

&lt;p&gt;Are you really gonna use&lt;br&gt;
&lt;code&gt;SELECT username FROM user WHERE username IS "ateebHussain";&lt;/code&gt;&lt;br&gt;
Seriously??&lt;/p&gt;

&lt;p&gt;That's not how tech giants do this, you are gonna rack up AWS bills just for one user when you have BILLIONS of records sitting in one DB TABLE.&lt;/p&gt;

&lt;p&gt;Tech Giants, and other applications user a number of techniques/algorithms to handle username lookups efficiently with as much less time as possible.&lt;/p&gt;

&lt;p&gt;These Involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis Hashmaps&lt;/li&gt;
&lt;li&gt;Trie Structures&lt;/li&gt;
&lt;li&gt;B+ Trees&lt;/li&gt;
&lt;li&gt;Bloom Filters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many a companies use a combination of theses for more efficient lookups and less false positives.&lt;/p&gt;

&lt;p&gt;In the next post, I am gonna cover detailed implementation plan and PROS and CONS for each algorithm depending on your edge-case.&lt;/p&gt;

&lt;p&gt;P.S. I'm still using &lt;code&gt;username @unique&lt;/code&gt; with PostgreSQL for a 10k users System Design 😋&lt;/p&gt;

&lt;p&gt;What was your first, "This ain't right" moment with usernames?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What Even Is The Purpose of Learning?</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Wed, 08 Apr 2026 08:30:23 +0000</pubDate>
      <link>https://dev.to/mateebhussain/what-even-is-the-purpose-of-learning-4c2e</link>
      <guid>https://dev.to/mateebhussain/what-even-is-the-purpose-of-learning-4c2e</guid>
      <description>&lt;p&gt;In this era of rapid AI hype, learning often feels… obsolete.&lt;br&gt;&lt;br&gt;
Especially when it comes to “learning how to code.”&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Learning to code has become optional” — but learning fundamentals and how things work under the hood is more important than ever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine you’re building a 100k MMR SaaS&lt;br&gt;&lt;br&gt;
(…of course, with zero bugs 😂)&lt;br&gt;&lt;br&gt;
and you don’t know which database to choose.&lt;/p&gt;

&lt;p&gt;There are &lt;em&gt;tons&lt;/em&gt; out there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL – flexible queries, great for read‑heavy workloads
&lt;/li&gt;
&lt;li&gt;Cassandra – built for write‑heavy, no‑relational data
&lt;/li&gt;
&lt;li&gt;MongoDB – schema‑flexible, good for raw or vector‑like data
&lt;/li&gt;
&lt;li&gt;Redis – in‑memory, volatile, perfect for caching
&lt;/li&gt;
&lt;li&gt;Elasticsearch – search‑first, as the name suggests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the catch:&lt;br&gt;&lt;br&gt;
You don’t need &lt;strong&gt;all&lt;/strong&gt; of them.&lt;br&gt;&lt;br&gt;
You don’t even need &lt;strong&gt;one&lt;/strong&gt; “perfect” database.&lt;br&gt;&lt;br&gt;
It’s &lt;em&gt;always&lt;/em&gt; about your use case.  &lt;/p&gt;

&lt;p&gt;How do you know which one to pick?&lt;br&gt;&lt;br&gt;
How do you choose the right architecture?&lt;/p&gt;




&lt;h3&gt;
  
  
  A Real‑World Mistake
&lt;/h3&gt;

&lt;p&gt;While building an e‑commerce store, I decided to use &lt;strong&gt;all five&lt;/strong&gt; of these.&lt;br&gt;&lt;br&gt;
AI was all in.&lt;br&gt;&lt;br&gt;
The idea sounded “scalable.”  &lt;/p&gt;

&lt;p&gt;The harsh reality?&lt;br&gt;&lt;br&gt;
At &lt;strong&gt;10K users&lt;/strong&gt;, a single PostgreSQL instance alone could’ve handled everything.  &lt;/p&gt;

&lt;p&gt;I learned it the hard way.&lt;br&gt;&lt;br&gt;
You can learn it the smart way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Comment &lt;strong&gt;“Databases”&lt;/strong&gt; and I’ll send you a short list of resources (articles, docs, and patterns) that helped me clean up that mess.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  So, How &lt;em&gt;Do&lt;/em&gt; You Decide on a Database?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Start with &lt;strong&gt;reads vs writes&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Ask: “Do I need relations or not?”
&lt;/li&gt;
&lt;li&gt;Ask: “Is this data transient, cached, or mission‑critical?”
&lt;/li&gt;
&lt;li&gt;Then, and only then, pick the tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point isn’t to master every database.&lt;br&gt;&lt;br&gt;
It’s to &lt;strong&gt;understand enough fundamentals&lt;/strong&gt; that you can stop over‑engineering and start shipping.&lt;/p&gt;

&lt;p&gt;What’s your “I over‑engineered this and learned the hard way” story with databases or architecture? Drop it in the comments 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Project Structure of eCourses: How I Organized My LMS SaaS Codebase</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Mon, 06 Apr 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/mateebhussain/project-structure-of-ecourses-how-i-organized-my-lms-saas-codebase-1642</link>
      <guid>https://dev.to/mateebhussain/project-structure-of-ecourses-how-i-organized-my-lms-saas-codebase-1642</guid>
      <description>&lt;p&gt;In the previous post we looked at the overall architecture and tech stack of &lt;strong&gt;eCourses&lt;/strong&gt;, but a lot of the “real” experience of shipping a product lives inside the &lt;strong&gt;project structure&lt;/strong&gt;.&lt;br&gt;
In this post, I’ll walk through how I’ve organized the code so far, the patterns that make the app easier to scale, and why this structure fits so well for a &lt;strong&gt;community‑first LMS SaaS&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  What eCourses Is (Quick Recap)
&lt;/h2&gt;

&lt;p&gt;eCourses is a &lt;strong&gt;Learning Management System SaaS&lt;/strong&gt; built for small communities and independent educators.&lt;br&gt;&lt;br&gt;
Right now the codebase is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 15 (App Router) + TypeScript + Prisma (PostgreSQL).
&lt;/li&gt;
&lt;li&gt;Authentication with Clerk.
&lt;/li&gt;
&lt;li&gt;Caching with Next.js fetch cache + Upstash Redis.
&lt;/li&gt;
&lt;li&gt;UI built with Tailwind CSS, shadcn/ui, and dnd‑kit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this is glued together by a project structure that tries to stay &lt;strong&gt;predictable, scoped, and easy to grow&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Folder Layout at a Glance
&lt;/h2&gt;

&lt;p&gt;Here’s the core layout (slightly trimmed for readability):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/
├── app/
│   ├── &lt;span class="o"&gt;[&lt;/span&gt;communitySlug]/
│   │   ├── admin/
│   │   │   ├── courses/
│   │   │   │   ├── page.tsx          &lt;span class="c"&gt;# courses list with filters&lt;/span&gt;
│   │   │   │   ├── add/page.tsx      &lt;span class="c"&gt;# create course&lt;/span&gt;
│   │   │   │   ├── edit/page.tsx     &lt;span class="c"&gt;# edit course&lt;/span&gt;
│   │   │   │   └── manage/page.tsx   &lt;span class="c"&gt;# manage modules &amp;amp; lessons&lt;/span&gt;
│   │   │   ├── analytics/
│   │   │   └── ...
│   │   ├── courses/                  &lt;span class="c"&gt;# public course listing&lt;/span&gt;
│   │   ├── home/
│   │   └── sessions/
│   └── api/
│       ├── courses/
│       │   ├── route.ts             &lt;span class="c"&gt;# GET, POST&lt;/span&gt;
│       │   └── &lt;span class="o"&gt;[&lt;/span&gt;courseId]/route.ts  &lt;span class="c"&gt;# GET, PATCH, DELETE&lt;/span&gt;
│       ├── modules/
│       │   ├── route.ts             &lt;span class="c"&gt;# GET, POST&lt;/span&gt;
│       │   ├── reorder/route.ts     &lt;span class="c"&gt;# PATCH&lt;/span&gt;
│       │   └── &lt;span class="o"&gt;[&lt;/span&gt;moduleId]/route.ts  &lt;span class="c"&gt;# DELETE&lt;/span&gt;
│       ├── lessons/
│       │   ├── route.ts             &lt;span class="c"&gt;# POST&lt;/span&gt;
│       │   ├── reorder/route.ts     &lt;span class="c"&gt;# PATCH&lt;/span&gt;
│       │   └── &lt;span class="o"&gt;[&lt;/span&gt;lessonId]/route.ts  &lt;span class="c"&gt;# DELETE&lt;/span&gt;
│       ├── members/
│       │   └── instructors/
│       │       ├── route.ts         &lt;span class="c"&gt;# GET non‑student members&lt;/span&gt;
│       │       └── &lt;span class="o"&gt;[&lt;/span&gt;userId]/route.ts
│       └── upload-auth/route.ts     &lt;span class="c"&gt;# ImageKit auth&lt;/span&gt;
├── actions/
│   ├── courses.ts
│   ├── modules.ts
│   ├── lessons.ts
│   └── members.ts
├── components/
│   ├── courses/
│   ├── modules/
│   ├── lessons/
│   └── inputs/
├── hooks/
│   ├── use-course-form.ts
│   ├── use-course-filters.ts
│   ├── use-courses.ts
│   ├── use-instructor.ts
│   └── use-modules.ts
├── lib/
│   ├── api.ts           &lt;span class="c"&gt;# verifyApiRequest, generateSlug, validateWithRegex&lt;/span&gt;
│   ├── cache.ts         &lt;span class="c"&gt;# withCache, bustCache&lt;/span&gt;
│   ├── redis.ts         &lt;span class="c"&gt;# Upstash Redis client&lt;/span&gt;
│   └── prisma.ts
└── types/
    ├── course.ts        &lt;span class="c"&gt;# Prisma.CourseGetPayload types&lt;/span&gt;
    └── module.ts        &lt;span class="c"&gt;# Prisma.ModuleGetPayload types&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why This Structure Works for an LMS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Community‑Scoped Routing
&lt;/h3&gt;

&lt;p&gt;The key design choice is routing by &lt;code&gt;communitySlug&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;communitySlug&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;communitySlug&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Every route is &lt;strong&gt;scoped to a community&lt;/strong&gt;, which maps naturally to the &lt;code&gt;Community&lt;/code&gt; → &lt;code&gt;Course&lt;/code&gt; → &lt;code&gt;Module&lt;/code&gt; → &lt;code&gt;Lesson&lt;/code&gt; model.
&lt;/li&gt;
&lt;li&gt;The same code can run multiple communities without needing separate deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is common in SaaS‑style LMS platforms, where you want to isolate data but reuse the same frontend logic.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Clean Separation: app / api / actions
&lt;/h3&gt;

&lt;p&gt;I keep three layers for server‑side logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;app/api/&lt;/code&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pure internal API routes.
&lt;/li&gt;
&lt;li&gt;Each ends with &lt;code&gt;route.ts&lt;/code&gt; and follows REST‑ish patterns (e.g., &lt;code&gt;GET /courses&lt;/code&gt;, &lt;code&gt;GET /courses/[courseId]&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;All routes are protected via &lt;code&gt;x-api-secret&lt;/code&gt; and &lt;code&gt;verifyApiRequest()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;actions/&lt;/code&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server Actions that act as &lt;strong&gt;proxies&lt;/strong&gt; to those API routes.
&lt;/li&gt;
&lt;li&gt;They attach the secret server‑side so the API can be reused by both the frontend and a future mobile app.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;lib/&lt;/code&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shared utilities:&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api.ts&lt;/code&gt; → &lt;code&gt;verifyApiRequest&lt;/code&gt;, &lt;code&gt;generateSlug&lt;/code&gt;, validators.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cache.ts&lt;/code&gt; → &lt;code&gt;withCache&lt;/code&gt;, &lt;code&gt;bustCache&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prisma.ts&lt;/code&gt; → a single Prisma client instance.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This keeps the business logic out of the route handlers and makes it easier to test, refactor, and reuse.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Domain‑Driven Components
&lt;/h3&gt;

&lt;p&gt;Inside &lt;code&gt;components/&lt;/code&gt;, I group by &lt;strong&gt;domain&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;components/
├── courses/
├── modules/
├── lessons/
└── inputs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Anything that touches courses (cards, filters, forms) lives under &lt;code&gt;courses/&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Video cards, drag‑and‑drop lists, accordions, and lesson forms live in &lt;code&gt;lessons/&lt;/code&gt; and &lt;code&gt;modules/&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Shared form inputs live under &lt;code&gt;inputs/&lt;/code&gt; so they can be reused across multiple pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern scales well as the LMS grows — adding new features like “assignments” or “quizzes” becomes a matter of adding a new domain folder instead of scattering logic everywhere.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Hooks: Collocating Logic with UI
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;hooks/&lt;/code&gt; folder is where I keep reusable logic that’s closely tied to the LMS model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hooks/
├── use-course-form.ts          &lt;span class="c"&gt;# manage course form state + validation&lt;/span&gt;
├── use-course-filters.ts       &lt;span class="c"&gt;# sync URL query params with filter UI&lt;/span&gt;
├── use-courses.ts              &lt;span class="c"&gt;# fetch and cache course lists&lt;/span&gt;
├── use-instructor.ts           &lt;span class="c"&gt;# instructor‑related queries&lt;/span&gt;
└── use-modules.ts              &lt;span class="c"&gt;# module list + reordering helpers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping these hooks together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes it easy to see &lt;strong&gt;what kinds of things the app can do&lt;/strong&gt; just by scanning the folder.
&lt;/li&gt;
&lt;li&gt;Lets components stay thin and focused on UI instead of business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is common in modern LMS‑style apps, where you end up with many similar “list + filter + form” flows.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Types and the Data Model
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;types/&lt;/code&gt; folder currently holds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;course.ts&lt;/code&gt; → types derived from &lt;code&gt;Prisma.CourseGetPayload&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;module.ts&lt;/code&gt; → types for modules and their nested relationships.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I keep these thin, auto‑generated, and strongly aligned with the database schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Community&lt;/span&gt;
 &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nc"&gt;Course &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;communityId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instructorId&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;
       &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nc"&gt;Module &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;courseId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nc"&gt;Lesson &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VIDEO&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;SESSION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes refactoring safer and keeps the TypeScript type system consistent with the underlying LMS model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Things I Optimized for
&lt;/h2&gt;

&lt;p&gt;When I was setting up this structure, I consciously optimized for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Predictability&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;New routes, components, and hooks follow a clear pattern so you can guess where to look.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Scalability&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Community‑scoped routing and modular components make it easy to add new features without breaking existing ones.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Separation of concerns&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;API routes, server actions, UI components, and shared utilities each have their own place.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Developer experience&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript + Prisma + Zod + React Hook Form make the whole stack feel cohesive and type‑safe from top to bottom.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These are the same patterns I see in other production‑grade LMS‑style SaaS apps, where the complexity comes from the &lt;strong&gt;data model and workflows&lt;/strong&gt;, not from a messy codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  How This Fits Into the Series
&lt;/h2&gt;

&lt;p&gt;This post is the &lt;strong&gt;first deep dive into the codebase&lt;/strong&gt; of eCourses. In future posts in this series, I’ll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;data model&lt;/strong&gt; and why &lt;code&gt;Float index&lt;/code&gt; + soft delete works so well for drag‑and‑drop.
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;caching strategy&lt;/strong&gt; (three‑layer cache, tag‑based invalidation).
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;admin‑panel UX patterns&lt;/strong&gt; (optimistic UI, reordering, filters with URL state).
&lt;/li&gt;
&lt;li&gt;How &lt;strong&gt;server actions as API proxies&lt;/strong&gt; will support a future mobile app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you found this structure helpful, drop a comment or a reaction — I’ll keep using this series to walk through real‑world decisions as eCourses grows. And if you’re building your own LMS or SaaS, feel free to borrow or adapt any of these patterns (and tweak them to fit your own style).&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>buildinpublic</category>
      <category>lms</category>
    </item>
    <item>
      <title>Building eCourses: A Community‑First LMS SaaS (and Why You Should Build in Public)</title>
      <dc:creator>Ateeb Hussain</dc:creator>
      <pubDate>Sun, 05 Apr 2026 07:54:22 +0000</pubDate>
      <link>https://dev.to/mateebhussain/building-ecourses-a-community-first-lms-saas-and-why-you-should-build-in-public-3g56</link>
      <guid>https://dev.to/mateebhussain/building-ecourses-a-community-first-lms-saas-and-why-you-should-build-in-public-3g56</guid>
      <description>&lt;p&gt;I’m building a &lt;strong&gt;Learning Management System SaaS&lt;/strong&gt; called &lt;strong&gt;eCourses&lt;/strong&gt;, designed specifically for small communities and independent educators who feel priced out or over‑engineered by existing platforms.&lt;/p&gt;

&lt;p&gt;This post is the first in a series where I’ll walk through the architecture, decisions, and “lessons learned” from shipping an LMS from scratch — in public, open source, and on a tight budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built eCourses
&lt;/h2&gt;

&lt;p&gt;Most LMS platforms are either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too expensive for solo creators and small communities.
&lt;/li&gt;
&lt;li&gt;Too complex for simple “course + modules + lessons + live sessions” workflows.
&lt;/li&gt;
&lt;li&gt;Too rigid to let instructors experiment with their own teaching style.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted something that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feels native to &lt;strong&gt;communities&lt;/strong&gt; (not just single instructors).
&lt;/li&gt;
&lt;li&gt;Scales technically and financially under &lt;strong&gt;$10/month&lt;/strong&gt; at reasonable load.
&lt;/li&gt;
&lt;li&gt;Lets me experiment with &lt;strong&gt;API design, caching, and complex state&lt;/strong&gt; in a real product context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s how &lt;strong&gt;eCourses&lt;/strong&gt; started — as a personal itch, and now as a real product and portfolio project.&lt;/p&gt;




&lt;h2&gt;
  
  
  What eCourses Is (Right Now)
&lt;/h2&gt;

&lt;p&gt;At its core, eCourses is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;community‑scoped LMS SaaS&lt;/strong&gt; with &lt;code&gt;communitySlug&lt;/code&gt;‑based routing.
&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;admin panel&lt;/strong&gt; where instructors can:

&lt;ul&gt;
&lt;li&gt;Create, edit, and soft‑delete courses (with image upload via ImageKit).
&lt;/li&gt;
&lt;li&gt;Reorder modules and lessons with drag‑and‑drop (optimistic UI, two‑pass &lt;code&gt;index&lt;/code&gt; transactions).
&lt;/li&gt;
&lt;li&gt;Assign non‑student members as instructors.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;A structure that prepares the ground for:

&lt;ul&gt;
&lt;li&gt;Public course pages.
&lt;/li&gt;
&lt;li&gt;Video upload and playback.
&lt;/li&gt;
&lt;li&gt;Live sessions and recordings.
&lt;/li&gt;
&lt;li&gt;Student progress tracking and analytics.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The live version is currently deployed at &lt;strong&gt;&lt;a href="https://ecourses-sigma.vercel.app" rel="noopener noreferrer"&gt;https://ecourses-sigma.vercel.app&lt;/a&gt;&lt;/strong&gt;, and the whole stack is open source under the MIT license.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Big Picture: Tech Stack
&lt;/h2&gt;

&lt;p&gt;Right now the stack is optimized for &lt;strong&gt;type‑safe, fast iteration&lt;/strong&gt; rather than maximal feature‑bloat:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tech / Pattern&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Next.js 15 (App Router + Turbopack)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;Clerk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ORM&lt;/td&gt;
&lt;td&gt;Prisma v7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache&lt;/td&gt;
&lt;td&gt;Upstash Redis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File Storage&lt;/td&gt;
&lt;td&gt;ImageKit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend UI&lt;/td&gt;
&lt;td&gt;Tailwind CSS v4 + shadcn/ui + dnd‑kit, nuqs, React Hook Form + Zod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State (HTTP)&lt;/td&gt;
&lt;td&gt;TanStack Query v5 (React Query v4 in browser)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This gives me strong typing, rapid iteration, and a clear path to a mobile‑ready backend later.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You Want to Follow Along
&lt;/h2&gt;

&lt;p&gt;Right now &lt;strong&gt;eCourses&lt;/strong&gt; is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live at: &lt;strong&gt;&lt;a href="https://ecourses-sigma.vercel.app" rel="noopener noreferrer"&gt;https://ecourses-sigma.vercel.app&lt;/a&gt;&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Open source under MIT.
&lt;/li&gt;
&lt;li&gt;Actively under development, with plans to ship to real communities within the next few months.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Star / fork the repo and open issues or PRs.
&lt;/li&gt;
&lt;li&gt;Comment here with questions or suggestions (especially around LMS‑specific UX, API patterns, or caching).
&lt;/li&gt;
&lt;li&gt;Follow as I keep shipping and documenting — if you’ve ever wondered what a “real‑sized” LMS architecture looks like at SaaS‑scale, this is a good place to watch it evolve.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>buildinpublic</category>
      <category>lms</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
