<?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: M TOQEER ZIA</title>
    <description>The latest articles on DEV Community by M TOQEER ZIA (@m_toqeer).</description>
    <link>https://dev.to/m_toqeer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3529850%2Fe40fd658-6577-41f6-b787-c57b8c4d9530.jpg</url>
      <title>DEV Community: M TOQEER ZIA</title>
      <link>https://dev.to/m_toqeer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/m_toqeer"/>
    <language>en</language>
    <item>
      <title>NATS and NATS Streaming: From Basics to Concurrency</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Mon, 27 Jul 2026 18:04:23 +0000</pubDate>
      <link>https://dev.to/m_toqeer/nats-and-nats-streaming-from-basics-to-concurrency-205k</link>
      <guid>https://dev.to/m_toqeer/nats-and-nats-streaming-from-basics-to-concurrency-205k</guid>
      <description>&lt;h2&gt;
  
  
  What is NATS?
&lt;/h2&gt;

&lt;p&gt;NATS is a lightweight messaging system. Services send messages to named subjects, and other services listen on those subjects. The sender never needs to know who listens. The listener never needs to know who sends. This pattern is called publish and subscribe.&lt;/p&gt;

&lt;p&gt;Core NATS works in a fire-and-forget way. A publisher sends a message to a subject. NATS delivers the message to every listener connected at that moment. No listener means no delivery. The message is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use NATS?
&lt;/h2&gt;

&lt;p&gt;In a monolith, one module calls another module through a direct function call. In microservices, each service runs as a separate process, often on a separate machine. Direct calls no longer fit. Services need a shared way to talk without tight links to each other.&lt;/p&gt;

&lt;p&gt;NATS gives you three benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decoupling. The publisher sends an event and moves on. The publisher never tracks who reacts.&lt;/li&gt;
&lt;li&gt;Scalability. A new service subscribes to existing events and reacts on its own. You change no publisher code.&lt;/li&gt;
&lt;li&gt;Speed. NATS routes small messages fast, which suits high-traffic systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is NATS Streaming?
&lt;/h2&gt;

&lt;p&gt;Core NATS loses a message when no one listens. NATS Streaming, also called STAN, fixes this gap. NATS Streaming runs as a layer on top of core NATS and adds storage and delivery guarantees.&lt;/p&gt;

&lt;p&gt;NATS Streaming adds four features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Persistence. Messages get stored in memory, in a file, or in a SQL store.&lt;/li&gt;
&lt;li&gt;Replay. A new subscriber requests messages from the start, from a sequence number, or from a point in time.&lt;/li&gt;
&lt;li&gt;At-least-once delivery. Subscribers acknowledge each message, so a crashed service does not lose data.&lt;/li&gt;
&lt;li&gt;Durable subscriptions. A service disconnects, reconnects, and resumes from the last processed message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the difference side by side:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Core NATS&lt;/th&gt;
&lt;th&gt;NATS Streaming&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delivery&lt;/td&gt;
&lt;td&gt;At most once&lt;/td&gt;
&lt;td&gt;At least once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Memory, file, or SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replay&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Durable subscriptions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;Maintained&lt;/td&gt;
&lt;td&gt;Deprecated, end of life June 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One warning before you build on this. Synadia ended support for NATS Streaming in June 2023 and points teams to JetStream, the persistence layer built into the NATS server. Learn the concepts here and apply them in JetStream for production work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main purpose of NATS
&lt;/h2&gt;

&lt;p&gt;The main purpose is to act as an event bus between microservices. A service publishes an event such as OrderCreated or TicketUpdated. Other services subscribe and react. The publisher stays decoupled from every consumer.&lt;/p&gt;

&lt;p&gt;NATS Streaming works well as an event bus for four reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No lost events on crash. Plain pub and sub drops events when a subscriber goes down. NATS Streaming stores them for later delivery.&lt;/li&gt;
&lt;li&gt;Ordering support. Events on a subject arrive in order, which matters for state changes like created, reserved, paid, expired.&lt;/li&gt;
&lt;li&gt;Replay for new services. A new service replays the full history from the start.&lt;/li&gt;
&lt;li&gt;Retry through acknowledgements. Unacknowledged messages get redelivered, giving you built-in retry logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Publisher example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;nats&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-nats-streaming&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ticketing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4222&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// connect(clusterId, clientId, config)&lt;/span&gt;
&lt;span class="c1"&gt;//  - clusterId ('ticketing'): matches the server cluster ID&lt;/span&gt;
&lt;span class="c1"&gt;//  - clientId ('abc'): unique per client; a duplicate kicks off the first&lt;/span&gt;
&lt;span class="c1"&gt;//  - config.url: address of the NATS Streaming server&lt;/span&gt;

&lt;span class="nx"&gt;stan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Publisher connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;concert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// NATS Streaming accepts strings or binary, so objects need JSON.stringify&lt;/span&gt;

  &lt;span class="nx"&gt;stan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ticket:created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Event published&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client ID sits hardcoded as 'abc' in this example. In a real deployment with many pod replicas of one service, every pod tries the same client ID and kicks the others off. Generate the ID at runtime from a random value or the pod name.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The duplicate processing problem
&lt;/h2&gt;

&lt;p&gt;You scale a service by running more copies. Picture three replicas of orders-service in Kubernetes. Each replica opens its own subscription to the ticket:created channel. With no grouping, every replica receives every event. Three replicas save the same comment three times. You get duplicate work and corrupted data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Queue groups fix duplicates
&lt;/h2&gt;

&lt;p&gt;A queue group is a named group inside a channel. When several instances join one group, NATS delivers each event to a single member of the group. Your three orders-service replicas then process each event once.&lt;/p&gt;

&lt;p&gt;Different services keep their own queue groups on the same channel. A payments service and an orders service each receive a copy of every event. Inside each group, one member does the work.&lt;/p&gt;

&lt;p&gt;Think of a pizza shop with three chefs on one team. One chef makes each pizza. No duplicates. A waiter works on a separate team and hears every order alone.&lt;/p&gt;

&lt;p&gt;In node-nats-streaming, you pass the queue group name as the second argument to subscribe(). Every replica of the same service uses the exact same name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queueGroupName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders-service-queue-group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ordering is the harder problem
&lt;/h2&gt;

&lt;p&gt;Queue groups stop duplicate work. Ordering across different events is a separate challenge. A banking app shows why. The rule is simple. The balance never drops below zero. Four failures break this rule.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An event fails to process. When a listener never acknowledges an event, NATS waits about 30 seconds and resends the event to another instance. Later events run before the failed one, and the balance goes negative.&lt;/li&gt;
&lt;li&gt;One instance runs slower than another. Instance A holds a backlog. Instance B sits idle and fast. A withdrawal reaches Instance B and runs before an earlier deposit still waiting in Instance A. Wrong order again.&lt;/li&gt;
&lt;li&gt;A service crashes. NATS needs time to detect a dead instance through heartbeats. Events sent to the dead instance wait until NATS times out and reassigns them. During the wait, other events run ahead.&lt;/li&gt;
&lt;li&gt;A race near the timeout. A service takes close to 30 seconds. NATS marks the event as failed and resends the event to another instance. The original instance finishes right after. Two instances process the same event.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These failures are not NATS bugs. Any system with parallel work faces them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same problem lives everywhere
&lt;/h2&gt;

&lt;p&gt;A common reaction is to drop events and use synchronous requests, or to return to a monolith. Neither move helps. Synchronous calls carry the same concurrency risks. A monolith behind a load balancer runs many instances and hits the same out-of-order failures. Microservices and events make the problem more visible through network hops, retries, and delivery delays. The root problem exists anywhere processes run in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three solutions and why each falls short
&lt;/h2&gt;

&lt;p&gt;Global shared sequence tracking. All instances share one store of the last processed sequence number. Before processing event N, a service checks for N minus 1. You get exact order and zero duplicates. The cost is severe. The whole system now processes one event at a time. Jim's stuck deposit blocks Mary's unrelated deposit. Throughput drops to the floor.&lt;/p&gt;

&lt;p&gt;Per-user sequence numbers. Give each user an independent sequence, so Jim and Mary never block each other. Concurrency improves. NATS Streaming breaks the plan. Sequence numbers reset only inside a channel, so each user needs a separate channel. NATS Streaming handles a limited number of channels, with a default near 1,000 and real overhead per channel. Millions of users need millions of channels. No scale.&lt;/p&gt;

&lt;p&gt;Publisher tracks the previous sequence. The publisher remembers each user's last sequence number and attaches the expected previous number to every new event. Consumers process an event only when the stored number matches the expected previous number. Clever, and broken. NATS Streaming never returns the assigned sequence number to the publisher. Publishing runs one way. The publisher never learns the number to track.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real fix: your own version numbers
&lt;/h2&gt;

&lt;p&gt;The lesson lands hard. The problem is your design, not NATS. Stop asking how NATS orders events. Ask how your services make ordering simple.&lt;/p&gt;

&lt;p&gt;Look at a blog app. The Post Service owns Posts. No other service creates or edits a Post. This is a single source of truth. Apply the same rule to money. A Transaction Service owns transactions. The service receives requests, saves each transaction, and publishes events.&lt;/p&gt;

&lt;p&gt;Then the service assigns its own numbers, one sequence per user.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deposit 70 becomes transaction 1&lt;/li&gt;
&lt;li&gt;Deposit 40 becomes transaction 2&lt;/li&gt;
&lt;li&gt;Withdraw 100 becomes transaction 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These numbers belong to your business, not to NATS. Every event carries userId, amount, and transactionNumber.&lt;/p&gt;

&lt;p&gt;The Account Service listens and stores a balance plus the last transaction number. The rule is one line. Process an event only when the incoming number equals the stored number plus one.&lt;/p&gt;

&lt;p&gt;Walk the failure. Event 1 crashes. Event 2 arrives with number 2. The stored last transaction is None. Expected previous is 1. No match. The Account Service skips the event and sends no acknowledgement. NATS resends Event 1 after 30 seconds. The service processes Event 1, sets the balance to 70, and stores 1. Event 2 arrives again. Expected 1 matches stored 1. Process. Balance 110. Event 3 arrives. Expected 2 matches stored 2. Process. Balance 10. Correct order, enforced by your logic, not by NATS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning in the ticketing app
&lt;/h2&gt;

&lt;p&gt;The ticketing project applies the same idea with a version number. Only the Ticket Service changes a version.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TicketCreated ships version 1&lt;/li&gt;
&lt;li&gt;TicketUpdated ships version 2&lt;/li&gt;
&lt;li&gt;TicketUpdated ships version 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Order Service also needs ticket prices, so the Order Service listens. The rule stays the same. Accept an event only when the incoming version equals the stored version plus one. Version 3 arriving before Version 2 fails the check. The Order Service sends no acknowledgement and waits. NATS retries later. Version 2 lands, then Version 3 lands, and the final price reaches 100, the correct value.&lt;/p&gt;

&lt;p&gt;The consumer stays behind or level with the producer, never ahead. Mongoose helps here with a built-in version field. You configure each update to increment the version and each event to carry the number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery and replay: three options together
&lt;/h2&gt;

&lt;p&gt;A crashed service misses events while offline. NATS Streaming stores every published event, so recovery works. Three subscription options solve recovery together.&lt;/p&gt;

&lt;p&gt;setDeliverAllAvailable() replays every stored event on startup. A brand-new service uses this once to build local state. The downside shows fast. Every restart replays the full history. Five events feel fine. Five million events waste hours of CPU and network on each restart.&lt;/p&gt;

&lt;p&gt;setDurableName('order-service') gives the subscription a stable identity. NATS remembers the last event this subscription processed. On restart, NATS sends only the missed events, not the whole history.&lt;/p&gt;

&lt;p&gt;Queue Group keeps one member processing each event and holds the durable state alive across brief disconnects. Without a queue group, a Ctrl+C looks like a permanent disconnect, so NATS drops the durable state and replays everything again.&lt;/p&gt;

&lt;p&gt;Used together the behavior stays clean. First startup replays all history and records progress. A restart with no new events delivers nothing. A restart after missing Events 4 and 5 delivers only 4 and 5. Three Order Service pods share one durable subscription and split the load, one event per pod.&lt;/p&gt;

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

&lt;p&gt;Every quick fix trades one problem for another. Global sequencing gives correct order and terrible speed. Per-user sequencing gives speed and hits channel limits. Publisher-tracked sequencing reads well and dies on a NATS limitation. The working answer moves ordering into your own design through business-level version numbers, then pairs setDeliverAllAvailable, setDurableName, and a queue group for reliable replay, fault tolerance, and horizontal scale.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understanding the Event Bus: Publishers, Subscribers, and How Events Get Heard</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Thu, 23 Jul 2026 11:24:28 +0000</pubDate>
      <link>https://dev.to/m_toqeer/understanding-the-event-bus-publishers-subscribers-and-how-events-get-heard-bj6</link>
      <guid>https://dev.to/m_toqeer/understanding-the-event-bus-publishers-subscribers-and-how-events-get-heard-bj6</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv1c28zapuxvznrrs4ak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv1c28zapuxvznrrs4ak.png" alt=" " width="799" height="416"&gt;&lt;/a&gt;&lt;br&gt;
Sometimes a picture saves you 500 words of explanation. This post is the visual companion to the event bus / pub-sub concept — four diagrams, each unpacked so you can actually &lt;em&gt;see&lt;/em&gt; how publishers and subscribers behave before you touch a single line of code.&lt;/p&gt;

&lt;p&gt;If you already read the full write-up on event buses, think of this as the cheat-sheet version. If you haven't, this still stands on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Direct calls vs. event bus
&lt;/h2&gt;

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

&lt;p&gt;On the left is the classic mistake: &lt;code&gt;OrderService&lt;/code&gt; reaches out and calls &lt;code&gt;Email&lt;/code&gt;, &lt;code&gt;Inventory&lt;/code&gt;, &lt;code&gt;Payment&lt;/code&gt;, and &lt;code&gt;Shipping&lt;/code&gt; directly. It has to know all four addresses, handle all four failure modes, and if one of them is slow, the whole checkout slows down with it.&lt;/p&gt;

&lt;p&gt;On the right, &lt;code&gt;OrderService&lt;/code&gt; does one thing: it publishes &lt;code&gt;order.created&lt;/code&gt; to the event bus and moves on. It never finds out who picked it up, how many services are listening, or whether a new one gets added next sprint.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Takeaway:&lt;/strong&gt; the arrow count doesn't change — what changes is &lt;em&gt;who owns the connection&lt;/em&gt;. With direct calls, the publisher owns every relationship. With a bus, the bus owns them, and the publisher owns none.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. What's actually happening inside the bus
&lt;/h2&gt;

&lt;p&gt;This is the part people skip past too quickly. The "bus" isn't one big pipe — it's a broker holding named &lt;strong&gt;topics&lt;/strong&gt; (&lt;code&gt;order.created&lt;/code&gt;, &lt;code&gt;order.cancelled&lt;/code&gt;, &lt;code&gt;payment.failed&lt;/code&gt;), and each topic can have multiple independent subscribers.&lt;/p&gt;

&lt;p&gt;Notice &lt;code&gt;Email Service&lt;/code&gt;, &lt;code&gt;Analytics Svc&lt;/code&gt;, and &lt;code&gt;Inventory Svc&lt;/code&gt; are all subscribed to the &lt;em&gt;same&lt;/em&gt; topic here. Each one gets its &lt;strong&gt;own copy&lt;/strong&gt; of the event. If &lt;code&gt;Analytics Svc&lt;/code&gt; is slow to process it, &lt;code&gt;Email Service&lt;/code&gt; doesn't wait around — they're not in a line, they're in parallel lanes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Takeaway:&lt;/strong&gt; subscribing is a one-time registration. After that, delivery is automatic — the broker does the fan-out, not the publisher.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. A real checkout, end to end
&lt;/h2&gt;

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

&lt;p&gt;This is where the theory turns into an actual business flow. Read it top to bottom:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Checkout Service&lt;/code&gt; publishes &lt;code&gt;order.placed&lt;/code&gt; — its job ends here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Payment Service&lt;/code&gt; picks it up, charges the card, and publishes &lt;code&gt;payment.confirmed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Inventory Service&lt;/code&gt; reacts to that, reserves stock, and publishes &lt;code&gt;stock.reserved&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Notification Service&lt;/code&gt; reacts to &lt;em&gt;that&lt;/em&gt; and emails the customer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every step is a &lt;strong&gt;reaction to an event&lt;/strong&gt;, never a direct request. If &lt;code&gt;Notification Service&lt;/code&gt; is temporarily down, steps 1–6 still complete without a hiccup — the email just goes out a little later once it's back.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Common mistake:&lt;/strong&gt; assuming this chain always completes in order and instantly. It's asynchronous — build your system (and your customer-facing UI) to tolerate a short delay between "payment confirmed" and "email sent."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. The cover — zoomed out
&lt;/h2&gt;

&lt;p&gt;Zoom back out and this is the whole pattern in one picture: multiple publishers (&lt;code&gt;Orders&lt;/code&gt;, &lt;code&gt;Payments&lt;/code&gt;, &lt;code&gt;Inventory&lt;/code&gt;) on the left, a single event bus in the middle, and multiple subscribers (&lt;code&gt;Email&lt;/code&gt;, &lt;code&gt;Analytics&lt;/code&gt;, &lt;code&gt;Shipping&lt;/code&gt;, &lt;code&gt;Fraud Check&lt;/code&gt;) on the right. Nobody on the left knows or cares who's on the right — that's the entire point of the architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Diagram&lt;/th&gt;
&lt;th&gt;What it shows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Why direct service-to-service calls create fragile, tightly coupled systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;How topics and multiple subscribers work inside the broker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;A real, asynchronous e-commerce checkout flow, step by step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;The full pattern zoomed out — many publishers, one bus, many subscribers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Next time you're sketching a system design on a whiteboard, try drawing it this way first. If you find yourself drawing arrows from one service to five others, that's usually the signal to introduce a bus.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Got a diagram of your own event-driven system? Drop it in the comments — always curious to see how different teams structure their topics.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Sharding, Explained: How to Talk About It in a System Design Interview</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Tue, 21 Jul 2026 06:14:20 +0000</pubDate>
      <link>https://dev.to/m_toqeer/sharding-explained-how-to-talk-about-it-in-a-system-design-interview-1484</link>
      <guid>https://dev.to/m_toqeer/sharding-explained-how-to-talk-about-it-in-a-system-design-interview-1484</guid>
      <description>&lt;p&gt;Sharding is one of those topics that shows up in almost every system design interview once scale enters the conversation. Yet many candidates reach for it reflexively, without justifying whether it's actually needed, and without a clear framework for explaining &lt;em&gt;how&lt;/em&gt; they'd do it. This article breaks down why sharding exists, how to choose a shard key, the main distribution strategies, the challenges sharding introduces, and how to bring all of this together in an interview setting.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Why Sharding Exists
&lt;/h2&gt;

&lt;p&gt;Imagine you've just launched an app backed by a single large database — say an AWS RDS Postgres instance with around 70 terabytes of storage and the capacity to handle roughly 10,000 writes per second. In the early days, this is more than enough. Traffic grows, but the single database keeps up comfortably.&lt;/p&gt;

&lt;p&gt;Eventually, though, growth catches up with you. Maybe you now need 20,000 writes per second, or your storage is creeping toward that 70 TB ceiling. Queries slow down, backups take forever, and it's clear you've hit a limit.&lt;/p&gt;

&lt;p&gt;The first instinct is usually &lt;strong&gt;vertical scaling&lt;/strong&gt; — move to bigger hardware. This buys you time. AWS offers machines that can handle upwards of 140 terabytes of storage and around 50,000 writes per second, and most companies will never even approach that ceiling. But if your app keeps growing — more users, global traffic, sustained load — even the biggest single machine eventually saturates its CPU, storage, and I/O.&lt;/p&gt;

&lt;p&gt;At that point, no amount of vertical scaling solves the problem. This is when you reach for &lt;strong&gt;sharding&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sharding Actually Is
&lt;/h2&gt;

&lt;p&gt;Sharding means splitting your data across multiple independent databases so that no single machine holds the entire dataset. Each shard is a standalone database with its own CPU, memory, storage, and connection pool, holding just a portion of the overall data. Need more capacity? Add another shard.&lt;/p&gt;

&lt;p&gt;This solves the scaling problem, but it introduces a new set of questions: How do you decide how to split the data? How do you know which shard to query? What happens when one shard gets far more traffic than the others? What happens if a shard goes down and data needs to be rebalanced? Sharding trades a scaling problem for an operational complexity problem.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Choosing a Shard Key
&lt;/h2&gt;

&lt;p&gt;The first decision in any sharding strategy is the &lt;strong&gt;shard key&lt;/strong&gt; — the field used to determine how data is grouped and distributed. In an interview, this should be one of the first things you state explicitly, along with your reasoning.&lt;/p&gt;

&lt;p&gt;A strong shard key has three properties:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;High cardinality&lt;/strong&gt; — many unique values, so data spreads across shards rather than clustering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Even distribution&lt;/strong&gt; — values that naturally spread out, so no shard ends up disproportionately large.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query alignment&lt;/strong&gt; — the key should match how the data is actually queried.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, if the dominant access pattern is "fetch all posts for a given user," sharding by user ID means each user's data lives on one shard, so most queries only need to hit a single database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good shard key examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A social media app where the core operation is loading a user's profile and posts → shard by user ID.&lt;/li&gt;
&lt;li&gt;An e-commerce platform where the core operation is retrieving or updating a single order → shard by order ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Poor shard key examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A boolean field like "is premium user" — only two possible values, which caps you at two shards.&lt;/li&gt;
&lt;li&gt;Creation date, in an app where most queries target recent data — this concentrates nearly all traffic on the newest shard, creating a hotspot.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Distribute Data Across Shards
&lt;/h2&gt;

&lt;p&gt;Once you have a shard key, you need a strategy for mapping its values to specific shards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Range-based sharding&lt;/strong&gt; splits the key into contiguous ranges (e.g., user IDs 0–10M go to shard one, 10M–20M to shard two, and so on). It's simple and intuitive, but tends to produce uneven load — early shards may sit empty while the current range absorbs all new traffic, especially if IDs increase monotonically. It's a reasonable starting point but rarely used in production at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hash-based sharding&lt;/strong&gt; is the industry default. You hash the shard key and mod the result by the number of shards to decide placement, which produces solid, even distribution. The catch is rebalancing: adding or removing a shard changes the modulus, which forces nearly all existing data to move. &lt;strong&gt;Consistent hashing&lt;/strong&gt; solves this by placing both keys and shards on a virtual ring — you hash to a point on the ring and walk it to find the right shard, with refinements like virtual nodes to smooth out distribution further. This avoids the mass-reshuffling problem entirely. In interviews, especially at senior levels, hash-based sharding with consistent hashing is generally the expected default; at junior or mid-levels, you may need to explain the mechanics rather than simply naming it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Directory-based sharding&lt;/strong&gt; uses a lookup table that maps each record to a specific shard, rather than a formula. This gives you flexibility — you can move an overloaded user to their own dedicated shard just by updating the mapping. The tradeoff is an extra lookup on every request (added latency, two round-trips instead of one) and the directory itself becomes a single point of failure. It's rarely the default answer in interviews but is useful to mention as an option when flexibility matters more than raw throughput.&lt;/p&gt;

&lt;p&gt;The practical takeaway: default to hash-based sharding with consistent hashing, and bring up the alternatives only when the scenario calls for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Challenges Sharding Introduces
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hotspots and Load Imbalance
&lt;/h3&gt;

&lt;p&gt;Even a well-chosen shard key can produce uneven load in practice — the classic "celebrity problem." If you shard by user ID and one extremely popular user's data lands on a single shard, that shard can get flooded with traffic while others sit idle. Two common fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compound shard keys&lt;/strong&gt;, which append extra data (like a number or time bucket) to the key before hashing, spreading one entity's data across multiple shards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated shards for outliers&lt;/strong&gt;, where high-traffic entities are detected and routed via a directory lookup to a special, higher-capacity shard, while everyone else follows the standard hashing scheme.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most systems never need this, but platforms with extreme traffic outliers — large social networks, for instance — often do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cross-Shard Operations
&lt;/h3&gt;

&lt;p&gt;Some queries need data from more than one shard, requiring a fan-out to multiple databases and then aggregation of the results — far more expensive than a single-shard query. This usually happens when a query doesn't align with the shard key. Fetching one user's profile is cheap; fetching "the ten most popular posts across the entire platform" requires querying every shard and combining the results.&lt;/p&gt;

&lt;p&gt;You can't eliminate cross-shard queries completely, but you can minimize their cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache expensive cross-shard results&lt;/strong&gt; (in something like Redis) with an expiration, trading some staleness for speed — useful for feeds, leaderboards, and trending pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Denormalize data&lt;/strong&gt; so related information lives on the same shard, reducing the need for cross-shard reads at the cost of more complex writes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If cross-shard queries keep showing up for common use cases, that's usually a sign the shard key needs rethinking, or that caching and denormalization should be applied more aggressively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistency Across Shards
&lt;/h3&gt;

&lt;p&gt;On a single database, a transaction like transferring money between two accounts is atomic by default — both updates succeed, or neither does. Once those two accounts live on different shards, that guarantee breaks down, and a partial failure can leave the system in an inconsistent state.&lt;/p&gt;

&lt;p&gt;The textbook fix is &lt;strong&gt;two-phase commit (2PC)&lt;/strong&gt;, where a coordinator asks every involved shard if it's ready, waits for agreement, and then tells them all to commit. In practice, 2PC is slow and fragile — if a shard or the coordinator fails mid-process, the system can get stuck in a hard-to-resolve locked state, which is why most production systems avoid it.&lt;/p&gt;

&lt;p&gt;Better alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avoid cross-shard transactions where possible&lt;/strong&gt;, by keeping transactionally related data on the same shard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the saga pattern&lt;/strong&gt;, breaking a transaction into a sequence of smaller steps, each with a compensating action to undo it if a later step fails — rather than relying on an atomic rollback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bringing It Together in an Interview
&lt;/h2&gt;

&lt;p&gt;Sharding usually comes up during the deep-dive portion of a system design interview, when you're addressing a non-functional requirement around scale. Before proposing it, do the math on storage, write throughput, and read throughput to justify whether sharding is actually necessary.&lt;/p&gt;

&lt;p&gt;For example: 500 million users at 5 KB each is only about 2.5 TB — well within the range a single Postgres instance can handle comfortably. But 50,000 writes per second at peak would strain a single database, which is a legitimate reason to shard. High read volume from a large active user base, even with read replicas in place, can also justify distributing load.&lt;/p&gt;

&lt;p&gt;This distinction matters. Modern single-instance hardware can go a long way — well past 100 TB of storage and tens of thousands of writes per second. Many candidates jump straight to sharding without checking whether it's warranted; showing the math for why sharding &lt;em&gt;isn't&lt;/em&gt; necessary yet can be just as impressive as knowing how to implement it.&lt;/p&gt;

&lt;p&gt;When sharding is justified, structure your answer around four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Propose a shard key&lt;/strong&gt; based on the dominant access pattern (e.g., shard by user ID because most queries are user-centric).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose a distribution strategy&lt;/strong&gt; — typically hash-based sharding with consistent hashing, explained in more depth if you're earlier in your career.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Call out the tradeoffs&lt;/strong&gt;, such as cross-shard queries becoming more expensive, and how you'd mitigate that with caching or precomputation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Address future growth&lt;/strong&gt;, such as starting with enough shards for headroom and noting that consistent hashing makes adding more shards later far less disruptive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Delivered well, this shouldn't feel like a checklist — it should read as a natural progression from the numbers you've calculated to the design decisions those numbers demand.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Testing Microservices: Scope, Strategy, and the Auth Service Test Setup</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Thu, 16 Jul 2026 07:24:53 +0000</pubDate>
      <link>https://dev.to/m_toqeer/testing-microservices-scope-strategy-and-the-auth-service-test-setup-3bbf</link>
      <guid>https://dev.to/m_toqeer/testing-microservices-scope-strategy-and-the-auth-service-test-setup-3bbf</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9iaibw3cvzlh3iyrq6uq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9iaibw3cvzlh3iyrq6uq.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A practical breakdown of how to think about and implement automated testing in a microservices architecture — using an Auth service (Express + TypeScript + MongoDB) as the working example.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. Why This Matters (The Problem)
&lt;/h2&gt;

&lt;p&gt;Up to this point, the auth service (and the broader app) had &lt;strong&gt;no automated tests&lt;/strong&gt; — only manual testing done through Postman. That's fine for quick checks, but it doesn't scale, doesn't run in CI, and doesn't protect you when you refactor.&lt;/p&gt;

&lt;p&gt;So the goal here is to build a real automated testing setup — but before writing a single line of test code, you need to answer a more fundamental question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What exactly am I testing, and how much of the system am I trying to cover in one test?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;scope&lt;/strong&gt; problem, and it's the single biggest decision in microservices testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Four Levels of Test Scope
&lt;/h2&gt;

&lt;p&gt;In any microservices system, tests can span a spectrum from very narrow to very broad. This list isn't exhaustive, but it captures the main levels you'll encounter:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;What it tests&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unit test&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A single piece of code in isolation&lt;/td&gt;
&lt;td&gt;Testing one middleware function on its own&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Integration within a service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple pieces of code working together&lt;/td&gt;
&lt;td&gt;A request flowing through &lt;code&gt;requireAuth&lt;/code&gt; middleware → into a route handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Component interaction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How a service talks to an external system ("component" here means a &lt;em&gt;program&lt;/em&gt;, not a UI component)&lt;/td&gt;
&lt;td&gt;Testing how the service interacts with MongoDB, or with an event bus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cross-service test&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How two or more independent services work together&lt;/td&gt;
&lt;td&gt;Launching the Orders service and the Ticketing service together, having one emit an event, and checking the other processes it correctly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why not just always test at the biggest scope (#4)?
&lt;/h3&gt;

&lt;p&gt;It's tempting to think: &lt;em&gt;"Let's just test how all the services work together — that's closest to real life!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But in practice, this is &lt;strong&gt;extremely complex and expensive&lt;/strong&gt; to set up. Think about what it would actually require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spinning up a temporary Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Deploying multiple services into it just for the test&lt;/li&gt;
&lt;li&gt;Figuring out how to send requests into that environment&lt;/li&gt;
&lt;li&gt;Figuring out how to assert on results coming out of it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot of infrastructure just to run a test suite. It's slow, costly, and fragile.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Decision
&lt;/h3&gt;

&lt;p&gt;Because of that complexity, the strategy here is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Test each service in isolation.&lt;/strong&gt; Don't try to launch multiple services together to test their interaction directly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, cross-service behavior will be tested indirectly — through &lt;strong&gt;event emitting and receiving&lt;/strong&gt; (more on this below). This gets you most of the confidence of a full integration test without the operational overhead.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Three Kinds of Tests You'll Actually Write (Per Service)
&lt;/h2&gt;

&lt;p&gt;For each individual service (starting with Auth), there are three categories of tests to focus on:&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Goal #1 — Basic Request Handling
&lt;/h3&gt;

&lt;p&gt;Send a request into the service and assert on the outcome. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hit the sign-up endpoint → expect a response with a cookie containing a JWT&lt;/li&gt;
&lt;li&gt;Assert that the expected data was written into MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the &lt;strong&gt;first and primary focus&lt;/strong&gt; for the Auth service right now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Goal #2 — Model-Level Unit Tests
&lt;/h3&gt;

&lt;p&gt;Test the behavior of a specific data model in isolation (e.g., a method on the User model).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not very relevant for Auth right now, since the User model is simple. But later services will have more complex models that need this kind of testing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Test Goal #3 — Event Emitting &amp;amp; Receiving
&lt;/h3&gt;

&lt;p&gt;Test that a service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Correctly &lt;strong&gt;receives&lt;/strong&gt; an incoming event and processes it properly&lt;/li&gt;
&lt;li&gt;Correctly &lt;strong&gt;emits&lt;/strong&gt; an event to the outside world&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Not applicable yet — the app doesn't send or receive events at this stage, and event infrastructure hasn't been built.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why Test Goal #3 matters long-term
&lt;/h3&gt;

&lt;p&gt;This is the key insight: &lt;strong&gt;testing events is how cross-service behavior gets tested, without needing to launch multiple services together.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of running Orders + Ticketing simultaneously (scope level #4 — expensive and complex), you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test that the Orders service &lt;strong&gt;emits&lt;/strong&gt; the correct event&lt;/li&gt;
&lt;li&gt;Test that the Ticketing service &lt;strong&gt;receives and processes&lt;/strong&gt; that event correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these can be tested &lt;em&gt;within a single service in isolation&lt;/em&gt;, but together they give you confidence that the services will work correctly when connected in the real world.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. How Tests Will Actually Be Run
&lt;/h2&gt;

&lt;p&gt;The practical execution model is intentionally simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests run &lt;strong&gt;directly in the terminal&lt;/strong&gt; on your local machine&lt;/li&gt;
&lt;li&gt;No Docker, no Kubernetes involved in running the tests themselves&lt;/li&gt;
&lt;li&gt;Command: &lt;code&gt;npm run test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This spins up the service locally and runs tests against it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The underlying assumption
&lt;/h3&gt;

&lt;p&gt;This approach assumes your &lt;strong&gt;local machine can fully run the service&lt;/strong&gt; — meaning all its dependencies (Node.js, MongoDB, etc.) are available locally without a complex setup.&lt;/p&gt;

&lt;p&gt;For the Auth service right now, that's true — you only need Node.js and MongoDB.&lt;/p&gt;

&lt;h3&gt;
  
  
  A note on future complexity
&lt;/h3&gt;

&lt;p&gt;Not every future service will be this simple. Some may need a specific OS, or a complex, hard-to-install database. When that happens, this simple "just run it locally" approach won't hold up — a more advanced testing setup will be needed later. For now, though, local execution is the right call.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Setting Up the Test Pipeline
&lt;/h2&gt;

&lt;p&gt;The plan for how a single test run works, using &lt;strong&gt;Jest&lt;/strong&gt; as the test runner:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Spin up an in-memory copy of MongoDB&lt;/strong&gt; (no need to install MongoDB locally)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start the Express app&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use SuperTest&lt;/strong&gt; to send fake HTTP requests to the Express app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run assertions&lt;/strong&gt; — check the response and/or check what got written to MongoDB&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 3 (SuperTest) is where things get interesting, because using it properly requires a &lt;strong&gt;refactor&lt;/strong&gt; of the project structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Why a Refactor Is Needed: The &lt;code&gt;app.ts&lt;/code&gt; / &lt;code&gt;index.ts&lt;/code&gt; Split
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The problem
&lt;/h3&gt;

&lt;p&gt;SuperTest needs direct access to the Express &lt;code&gt;app&lt;/code&gt; object to send fake requests into it. But currently, the Express app is created &lt;em&gt;and&lt;/em&gt; started (&lt;code&gt;app.listen(3000)&lt;/code&gt;) inside &lt;code&gt;index.ts&lt;/code&gt;, along with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mongoose connecting to a hardcoded MongoDB URL&lt;/li&gt;
&lt;li&gt;The app listening on a hardcoded port (3000)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a test file imports the app from &lt;code&gt;index.ts&lt;/code&gt;, it inherits &lt;strong&gt;all&lt;/strong&gt; of that startup logic — including binding to port 3000.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why hardcoded port 3000 is a real problem
&lt;/h3&gt;

&lt;p&gt;You'll eventually want to run tests for &lt;strong&gt;multiple services at the same time&lt;/strong&gt; on the same machine (e.g., Auth service tests running alongside Orders service tests). If both services are hardcoded to listen on port 3000, running their tests concurrently will cause a port conflict and tests will fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix: SuperTest's ephemeral port behavior
&lt;/h3&gt;

&lt;p&gt;SuperTest has a built-in behavior: &lt;strong&gt;if the server passed to it isn't already listening on a port, SuperTest will automatically start it on a random available ("ephemeral") port.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This solves the port collision problem — but only if the Express app you hand to SuperTest &lt;strong&gt;isn't already bound to a fixed port.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Split into two files
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;app.ts&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Creates the Express app, wires up all middleware and route handlers, and &lt;strong&gt;exports&lt;/strong&gt; the app. Does &lt;strong&gt;not&lt;/strong&gt; call &lt;code&gt;.listen()&lt;/code&gt; anywhere.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;index.ts&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Imports the app from &lt;code&gt;app.ts&lt;/code&gt;, connects Mongoose to MongoDB, and calls &lt;code&gt;app.listen(3000)&lt;/code&gt; to actually start the server for real (dev/production use).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Production/dev&lt;/strong&gt;: you still run &lt;code&gt;index.ts&lt;/code&gt;, which starts everything normally, including listening on port 3000.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;: your test files import the app directly from &lt;code&gt;app.ts&lt;/code&gt; — no port binding, no Mongoose connection baked in — so SuperTest can safely assign it a random ephemeral port and multiple services' tests can run concurrently without colliding.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation Notes (from the refactor walkthrough)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create &lt;code&gt;app.ts&lt;/code&gt; inside the service's source directory.&lt;/li&gt;
&lt;li&gt;Cut all the middleware/route-wiring code out of &lt;code&gt;index.ts&lt;/code&gt; (everything above the &lt;code&gt;start&lt;/code&gt; function) and paste it into &lt;code&gt;app.ts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Watch out — the &lt;code&gt;mongoose&lt;/code&gt; import might get pulled over by mistake. It needs to &lt;strong&gt;stay in &lt;code&gt;index.ts&lt;/code&gt;&lt;/strong&gt;, since that's where the DB connection actually happens.&lt;/li&gt;
&lt;li&gt;At the bottom of &lt;code&gt;app.ts&lt;/code&gt;, add a &lt;strong&gt;named export&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: curly braces are required — this is a &lt;em&gt;named&lt;/em&gt; export, not a default export.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;index.ts&lt;/code&gt;, import the app at the top:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Save both files and verify the app still boots correctly (e.g., via Skaffold).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;End result:&lt;/strong&gt; &lt;code&gt;app.ts&lt;/code&gt; only &lt;em&gt;configures&lt;/em&gt; the Express app. &lt;code&gt;index.ts&lt;/code&gt; is responsible for actually &lt;em&gt;starting&lt;/em&gt; it (listening on a port + connecting to Mongo).&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Installing the Test Dependencies
&lt;/h2&gt;

&lt;p&gt;Three packages get installed, &lt;strong&gt;all as dev dependencies&lt;/strong&gt; (&lt;code&gt;--save-dev&lt;/code&gt; / &lt;code&gt;-D&lt;/code&gt; flag):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @types/supertest jest supertest mongodb-memory-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The test runner — executes your test suite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;supertest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lets you make fake/simulated HTTP requests against your Express app in tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@types/supertest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TypeScript type definitions for SuperTest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mongodb-memory-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spins up a temporary, in-memory MongoDB instance for tests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;mongodb-memory-server&lt;/code&gt; instead of a real/shared MongoDB?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It lets each service's test suite run its &lt;strong&gt;own isolated in-memory database&lt;/strong&gt;, rather than all services fighting over one shared test MongoDB instance.&lt;/li&gt;
&lt;li&gt;This means multiple services' test suites can run &lt;strong&gt;concurrently on the same machine&lt;/strong&gt; without interfering with each other's data.&lt;/li&gt;
&lt;li&gt;It downloads a real copy of MongoDB (roughly ~80MB) the first time it's used, and runs it purely in memory — no local MongoDB installation required.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why "dev dependency" matters here
&lt;/h3&gt;

&lt;p&gt;The production Docker image for the service should only ever run the actual Express app — &lt;strong&gt;never the test suite&lt;/strong&gt;. So none of these testing libraries need to exist inside the built Docker image.&lt;/p&gt;

&lt;p&gt;By installing them as dev dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They get skipped when the image is built using &lt;code&gt;npm install --only=production&lt;/code&gt; (or &lt;code&gt;--omit=dev&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;This avoids re-downloading the ~80MB MongoDB memory server binary &lt;strong&gt;every time the Docker image is rebuilt&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dockerfile change
&lt;/h3&gt;

&lt;p&gt;Inside the Auth service's &lt;code&gt;Dockerfile&lt;/code&gt;, the &lt;code&gt;npm install&lt;/code&gt; command gets updated to only install production dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps rebuilds fast and avoids unnecessary bloat/downloads in the image build process — which matters a lot once you're rebuilding the image repeatedly during active development.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Summary — The Mental Model
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Decide your test's scope first&lt;/strong&gt; — unit → integration-within-service → component interaction → cross-service. Don't default to the broadest scope; it's expensive and complex in a microservices setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test each service in isolation.&lt;/strong&gt; Simulate cross-service behavior through &lt;strong&gt;event emit/receive tests&lt;/strong&gt; rather than spinning up multiple real services together.&lt;/li&gt;
&lt;li&gt;Focus each service's test suite on three goals: &lt;strong&gt;(1) request handling, (2) model behavior, (3) event emit/receive.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run tests locally&lt;/strong&gt; via &lt;code&gt;npm run test&lt;/code&gt; — no Docker/Kubernetes needed for the test run itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate app creation (&lt;code&gt;app.ts&lt;/code&gt;) from app startup (&lt;code&gt;index.ts&lt;/code&gt;)&lt;/strong&gt; so SuperTest can bind the app to a random port and multiple services can be tested concurrently without port conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;mongodb-memory-server&lt;/code&gt;&lt;/strong&gt; for fast, isolated, in-memory database testing — installed as a dev dependency so it never bloats the production Docker image.&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Horizontal vs. Vertical Scaling: Which One Should You Actually Use?</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Sat, 11 Jul 2026 11:32:21 +0000</pubDate>
      <link>https://dev.to/m_toqeer/horizontal-vs-vertical-scaling-which-one-should-you-actually-use-2913</link>
      <guid>https://dev.to/m_toqeer/horizontal-vs-vertical-scaling-which-one-should-you-actually-use-2913</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fygll42ayramg674ega56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fygll42ayramg674ega56.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
Every growing application eventually hits the same wall: the current infrastructure isn't enough. More users, more data, more requests per second — and suddenly "just add more resources" becomes a real engineering decision, not a throwaway line in a system design interview.&lt;/p&gt;

&lt;p&gt;That decision usually comes down to two paths: &lt;strong&gt;scale up&lt;/strong&gt; (vertical) or &lt;strong&gt;scale out&lt;/strong&gt; (horizontal). They sound like a matter of preference, but they come with very different tradeoffs in cost, complexity, and failure modes. Let's break both down.&lt;/p&gt;
&lt;h2&gt;
  
  
  Vertical Scaling: Make the Machine Bigger
&lt;/h2&gt;

&lt;p&gt;Vertical scaling (scaling up) means adding more power to a single machine — more CPU, more RAM, faster disks (e.g., moving from SSD to NVMe).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: 4 vCPU, 16GB RAM
After:  16 vCPU, 64GB RAM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On most cloud providers, this is often as simple as changing an instance type and restarting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (AWS EC2):&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;&lt;span class="c"&gt;# Stop the instance&lt;/span&gt;
aws ec2 stop-instances &lt;span class="nt"&gt;--instance-ids&lt;/span&gt; i-0123456789abcdef0

&lt;span class="c"&gt;# Resize&lt;/span&gt;
aws ec2 modify-instance-attribute &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance-id&lt;/span&gt; i-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance-type&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Value&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;m5.2xlarge&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;

&lt;span class="c"&gt;# Start it back up&lt;/span&gt;
aws ec2 start-instances &lt;span class="nt"&gt;--instance-ids&lt;/span&gt; i-0123456789abcdef0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple.&lt;/strong&gt; No architecture changes, no distributed systems complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No data consistency issues.&lt;/strong&gt; One machine, one source of truth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great for stateful workloads&lt;/strong&gt; like traditional relational databases that aren't built for sharding.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hard ceiling.&lt;/strong&gt; Eventually you hit the biggest instance type available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single point of failure.&lt;/strong&gt; If that one machine goes down, everything goes down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downtime during resize&lt;/strong&gt;, in most setups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost grows non-linearly&lt;/strong&gt; — bigger machines get disproportionately more expensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Horizontal Scaling: Add More Machines
&lt;/h2&gt;

&lt;p&gt;Horizontal scaling (scaling out) means adding more instances of your application and distributing load across them, typically behind a load balancer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: 1 server handling all traffic
After:  5 servers behind a load balancer, each handling ~20%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example (Docker Compose, scaling a service):&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;docker compose up &lt;span class="nt"&gt;--scale&lt;/span&gt; &lt;span class="nv"&gt;web&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example (Kubernetes, via Horizontal Pod Autoscaler):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;autoscaling/v2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HorizontalPodAutoscaler&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-app-hpa&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-app&lt;/span&gt;
  &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Resource&lt;/span&gt;
      &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cpu&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Utilization&lt;/span&gt;
          &lt;span class="na"&gt;averageUtilization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No practical ceiling.&lt;/strong&gt; Add as many nodes as your budget and architecture allow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better fault tolerance.&lt;/strong&gt; One node dying doesn't take down the whole system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elastic cost.&lt;/strong&gt; Scale down during low traffic, scale up during peaks — pay for what you use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enables rolling deployments&lt;/strong&gt; with zero downtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Requires stateless design&lt;/strong&gt; (or careful state management via shared caches, sessions stores like Redis, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed systems problems appear&lt;/strong&gt;: data consistency, network latency, race conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More moving parts&lt;/strong&gt;: load balancers, service discovery, health checks, orchestration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging is harder&lt;/strong&gt; — logs and traces are spread across many nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Side-by-Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Vertical Scaling&lt;/th&gt;
&lt;th&gt;Horizontal Scaling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Complexity&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Higher (distributed systems)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upper limit&lt;/td&gt;
&lt;td&gt;Hardware ceiling&lt;/td&gt;
&lt;td&gt;Practically unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fault tolerance&lt;/td&gt;
&lt;td&gt;Single point of failure&lt;/td&gt;
&lt;td&gt;Resilient to node failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost curve&lt;/td&gt;
&lt;td&gt;Non-linear, gets expensive fast&lt;/td&gt;
&lt;td&gt;More linear, elastic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Downtime to scale&lt;/td&gt;
&lt;td&gt;Usually yes&lt;/td&gt;
&lt;td&gt;No (add/remove nodes live)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Traditional RDBMS, legacy monoliths&lt;/td&gt;
&lt;td&gt;Stateless services, microservices, web APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  So Which One Should You Pick?
&lt;/h2&gt;

&lt;p&gt;In practice, most real systems use &lt;strong&gt;both&lt;/strong&gt;, applied to different layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Application/API layer&lt;/strong&gt; → horizontal scaling. Stateless services behind a load balancer scale out cleanly and give you fault tolerance for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database layer&lt;/strong&gt; → often vertical scaling first (bigger instance, more RAM for caching, faster disks), because re-architecting for horizontal scaling (sharding, read replicas, distributed databases like CockroachDB or Cassandra) is a much bigger investment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache layer&lt;/strong&gt; (Redis, Memcached) → horizontal scaling via clustering once a single node's memory becomes the bottleneck.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common growth path looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start on a single small server (nothing to scale yet).&lt;/li&gt;
&lt;li&gt;Hit load limits → scale vertically (bigger box). Fastest fix, buys time.&lt;/li&gt;
&lt;li&gt;Vertical scaling gets expensive or hits a ceiling → move to horizontal scaling for the application tier.&lt;/li&gt;
&lt;li&gt;Database becomes the bottleneck → introduce read replicas, then sharding or a distributed database if needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Real Takeaway
&lt;/h2&gt;

&lt;p&gt;Vertical scaling buys you time with minimal complexity. Horizontal scaling buys you resilience and (near) unlimited growth at the cost of architectural complexity. Neither is "better" — the right call depends on where your current bottleneck actually is, how stateful your system is, and how much complexity your team can realistically operate.&lt;/p&gt;

&lt;p&gt;If you're not sure which one you need right now: profile first. Find out whether you're CPU-bound, memory-bound, I/O-bound, or connection-bound before reaching for either solution. Scaling the wrong dimension just gets you the same problem with a bigger bill.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you had to make this call in production? What tipped the decision one way or the other for you — cost, team size, or the nature of the workload? Drop it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Load Balancing Explained: The Complete Guide to Types and Algorithms</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Sat, 11 Jul 2026 11:27:34 +0000</pubDate>
      <link>https://dev.to/m_toqeer/load-balancing-explained-the-complete-guide-to-types-and-algorithms-4a61</link>
      <guid>https://dev.to/m_toqeer/load-balancing-explained-the-complete-guide-to-types-and-algorithms-4a61</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftfuj6r270edd9081n19p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftfuj6r270edd9081n19p.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;If you've ever wondered how sites like Amazon or Netflix handle millions of simultaneous users without crashing, the answer usually starts with one unsung hero of system design: &lt;strong&gt;the load balancer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we'll break down what load balancing is, why it matters, and the different types and algorithms you'll encounter in real-world architectures.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Load Balancing?
&lt;/h2&gt;

&lt;p&gt;Load balancing is the process of distributing incoming network traffic across multiple servers so that no single server becomes overwhelmed. Instead of one server handling every request, a load balancer sits in front of a group of servers (often called a &lt;strong&gt;server pool&lt;/strong&gt; or &lt;strong&gt;server farm&lt;/strong&gt;) and routes each incoming request to the server best equipped to handle it.&lt;/p&gt;

&lt;p&gt;Think of it like a checkout line at a grocery store. If everyone lines up at one register, that cashier gets overwhelmed while others sit idle. A good store manager (the load balancer) directs customers to open registers, keeping the whole line moving smoothly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Load Balancing Matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High availability&lt;/strong&gt; – if one server fails, traffic is automatically rerouted to healthy servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt; – you can add or remove servers based on demand without downtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; – requests are distributed efficiently, reducing latency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redundancy&lt;/strong&gt; – protects against a single point of failure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt; – enables maintenance and deployments without taking the whole system offline&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How a Load Balancer Works
&lt;/h2&gt;

&lt;p&gt;At a high level, a load balancer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives an incoming client request&lt;/li&gt;
&lt;li&gt;Checks the health/status of servers in the pool&lt;/li&gt;
&lt;li&gt;Applies a routing algorithm to pick a server&lt;/li&gt;
&lt;li&gt;Forwards the request to that server&lt;/li&gt;
&lt;li&gt;Returns the response back to the client&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most load balancers also perform continuous &lt;strong&gt;health checks&lt;/strong&gt;, pinging servers periodically to make sure they're still responsive before sending traffic their way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Types of Load Balancers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Hardware Load Balancers
&lt;/h3&gt;

&lt;p&gt;These are dedicated physical devices designed specifically to distribute traffic. They offer strong performance and reliability but come with a high upfront cost and limited flexibility. Common in large enterprises with strict compliance or performance requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; High throughput, dedicated resources, vendor support&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Expensive, less flexible, harder to scale dynamically&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Software Load Balancers
&lt;/h3&gt;

&lt;p&gt;These run as applications on standard servers or virtual machines (e.g., NGINX, HAProxy, Traefik). They're cheaper, easier to configure, and integrate well with cloud-native and containerized environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Cost-effective, flexible, easy to automate&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Shares resources with the host machine, may need tuning for extreme scale&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cloud-Based / Managed Load Balancers
&lt;/h3&gt;

&lt;p&gt;Services like AWS Elastic Load Balancer (ELB), Google Cloud Load Balancing, and Azure Load Balancer are fully managed offerings. They scale automatically and integrate tightly with other cloud services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Auto-scaling, minimal maintenance, pay-as-you-go&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Vendor lock-in, less low-level control&lt;/p&gt;




&lt;h2&gt;
  
  
  Load Balancing by OSI Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Layer 4 (Transport Layer) Load Balancing
&lt;/h3&gt;

&lt;p&gt;Operates at the transport layer, making routing decisions based on IP address and TCP/UDP port information — without inspecting the actual content of the packet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster&lt;/strong&gt; since it doesn't need to parse application data&lt;/li&gt;
&lt;li&gt;Doesn't understand HTTP headers, cookies, or URLs&lt;/li&gt;
&lt;li&gt;Good for simple, high-throughput scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Layer 7 (Application Layer) Load Balancing
&lt;/h3&gt;

&lt;p&gt;Operates at the application layer, meaning it can inspect the actual content of requests — HTTP headers, URLs, cookies, and even request bodies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enables &lt;strong&gt;smart routing&lt;/strong&gt;, like sending &lt;code&gt;/api/*&lt;/code&gt; requests to one set of servers and &lt;code&gt;/images/*&lt;/code&gt; to another&lt;/li&gt;
&lt;li&gt;Supports SSL termination, content-based routing, and session persistence&lt;/li&gt;
&lt;li&gt;Slightly slower than Layer 4 due to deeper packet inspection&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Load Balancing Algorithms
&lt;/h2&gt;

&lt;p&gt;The algorithm determines &lt;em&gt;how&lt;/em&gt; the load balancer chooses which server gets the next request.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Round Robin
&lt;/h3&gt;

&lt;p&gt;Requests are distributed sequentially across the server pool, one after another. Simple and effective when all servers have similar capacity.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Weighted Round Robin
&lt;/h3&gt;

&lt;p&gt;Similar to round robin, but servers are assigned weights based on their capacity. More powerful servers receive a proportionally larger share of traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Least Connections
&lt;/h3&gt;

&lt;p&gt;Routes traffic to the server with the fewest active connections. Ideal when requests vary significantly in processing time.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Weighted Least Connections
&lt;/h3&gt;

&lt;p&gt;Combines the "least connections" logic with server capacity weighting — factoring in both current load and server power.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. IP Hash
&lt;/h3&gt;

&lt;p&gt;Uses the client's IP address to consistently route them to the same server, useful for maintaining session persistence without needing a shared session store.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Least Response Time
&lt;/h3&gt;

&lt;p&gt;Sends traffic to the server with the fastest response time and fewest active connections, optimizing for speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Random / Random with Two Choices
&lt;/h3&gt;

&lt;p&gt;Requests are distributed randomly, sometimes with a "power of two choices" optimization where the balancer picks the less-loaded of two randomly selected servers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Global Server Load Balancing (GSLB)
&lt;/h2&gt;

&lt;p&gt;Beyond distributing traffic across servers in one data center, &lt;strong&gt;GSLB&lt;/strong&gt; distributes traffic across multiple data centers or geographic regions. This is essential for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reducing latency by routing users to the nearest region&lt;/li&gt;
&lt;li&gt;Disaster recovery if an entire region goes down&lt;/li&gt;
&lt;li&gt;Compliance with data residency requirements&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Load Balancing vs. Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;These terms are often confused. A &lt;strong&gt;reverse proxy&lt;/strong&gt; sits in front of servers and forwards client requests, but its primary job may be caching, SSL termination, or security — not necessarily distributing traffic across multiple backend servers. A &lt;strong&gt;load balancer&lt;/strong&gt; is specifically focused on distributing traffic to prevent overload. In practice, many tools (like NGINX and HAProxy) can act as both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Choosing the Right Load Balancer
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Consideration&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple traffic distribution&lt;/td&gt;
&lt;td&gt;Round Robin or Least Connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session persistence needed&lt;/td&gt;
&lt;td&gt;IP Hash or Layer 7 with sticky sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mixed server capacities&lt;/td&gt;
&lt;td&gt;Weighted Round Robin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content-based routing&lt;/td&gt;
&lt;td&gt;Layer 7 load balancer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global user base&lt;/td&gt;
&lt;td&gt;GSLB with regional data centers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud-native infrastructure&lt;/td&gt;
&lt;td&gt;Managed cloud load balancer (ELB, GCP LB, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Load balancing is a foundational concept in building scalable, resilient systems. Whether you're running a small side project or architecting infrastructure for millions of users, understanding these types and algorithms helps you make informed decisions about reliability and performance.&lt;/p&gt;

&lt;p&gt;The right choice ultimately depends on your traffic patterns, infrastructure, and business requirements — there's no one-size-fits-all solution, but now you have the vocabulary and mental model to reason through it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Feel free to share your own load balancing setups or war stories in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The New HTTP QUERY Method: Why It Exists and What Problem It Actually Solves</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Fri, 10 Jul 2026 19:36:50 +0000</pubDate>
      <link>https://dev.to/m_toqeer/the-new-http-query-method-why-it-exists-and-what-problem-it-actually-solves-1fkp</link>
      <guid>https://dev.to/m_toqeer/the-new-http-query-method-why-it-exists-and-what-problem-it-actually-solves-1fkp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ferbnpuk5uf1qhr2n8m47.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ferbnpuk5uf1qhr2n8m47.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've spent any time building backend systems, you know the HTTP verbs by heart: GET, POST, PUT, PATCH, DELETE. For decades, that list felt complete. Recently, though, a new method has entered the conversation — &lt;strong&gt;QUERY&lt;/strong&gt;. Before diving into what it does, it's worth understanding &lt;em&gt;why&lt;/em&gt; it was needed in the first place, because the reasoning tells you a lot about how the web actually works under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP Isn't a Network Protocol — It's an Agreement
&lt;/h2&gt;

&lt;p&gt;A common misconception is that HTTP is a network-level protocol like TCP or UDP. It isn't. HTTP sits at the application layer, built on top of TCP, which handles the actual transport of data between machines.&lt;/p&gt;

&lt;p&gt;Here's the thing about TCP: it doesn't care what you send. You could open a TCP connection between two machines and transmit the string "hello," and it would arrive just fine. But that string carries no meaning. Is it a greeting? A command? Data to be saved? TCP has no concept of intent — it just moves bytes from one point to another.&lt;/p&gt;

&lt;p&gt;This is the exact problem HTTP was designed to solve. Instead of every developer sending arbitrary, unstructured strings across the wire, HTTP introduced a standardized way to express &lt;em&gt;intent&lt;/em&gt;. That's really what an HTTP method is: a declaration of what you're trying to do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GET&lt;/strong&gt; → I intend to retrieve something&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST&lt;/strong&gt; → I intend to create something&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PUT&lt;/strong&gt; → I intend to replace something&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PATCH&lt;/strong&gt; → I intend to partially update something&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DELETE&lt;/strong&gt; → I intend to remove something&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the method comes the resource name (like &lt;code&gt;/users&lt;/code&gt;), and after that, optional extras — headers (key-value metadata) and a body (the actual payload). This simple structure is what allows every server and client in the world, regardless of who built them, to understand each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where GET Requests Start to Break Down
&lt;/h2&gt;

&lt;p&gt;GET requests carry their parameters in the URL itself, using query strings — anything after the &lt;code&gt;?&lt;/code&gt;. This works beautifully for simple filtering: &lt;code&gt;/users?name=piyush&lt;/code&gt; is clean, readable, and easy to reason about.&lt;/p&gt;

&lt;p&gt;The trouble starts when your filtering needs grow. Real-world applications often need to combine multiple conditions — filter by name, restrict by age, match an email domain, sort, paginate, and more, all in a single request. Try to cram that into a query string and you run into real constraints:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;URL length limits.&lt;/strong&gt; Most browsers and servers cap URLs around 8,000 characters. Complex filters can blow past that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encoding overhead.&lt;/strong&gt; Spaces and special characters aren't allowed raw in URLs — everything needs to go through &lt;code&gt;encodeURIComponent&lt;/code&gt; or similar, making complex queries messy and error-prone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exposure in logs.&lt;/strong&gt; Since query parameters live in the URL, they show up in server logs, browser history, and proxy logs — not ideal if any of that data is sensitive.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Workaround Everyone Used (and Why It Was Broken)
&lt;/h2&gt;

&lt;p&gt;Faced with these limits, developers found a workaround: if GET can't hold a complex query, just use POST instead. A POST request has a body, and a body can hold arbitrarily complex, structured JSON — filters, search terms, nested conditions, whatever you need, with no length restrictions and no awkward encoding.&lt;/p&gt;

&lt;p&gt;So teams started building endpoints like &lt;code&gt;POST /getUsers&lt;/code&gt;, reading the filter criteria from &lt;code&gt;request.body&lt;/code&gt;, and running the query on the backend. Functionally, it worked. But it broke the semantic contract of REST.&lt;/p&gt;

&lt;p&gt;The resource name says "get users." The method says "I intend to create something." That mismatch isn't just a style nitpick — it has real technical consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;POST requests are never cacheable.&lt;/strong&gt; Caching systems (whether at the CDN layer or elsewhere) assume that identical POST requests might produce different side effects each time, so they're excluded from caching by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST requests are not idempotent.&lt;/strong&gt; Idempotency means that calling an operation repeatedly with the same input produces the same result every time. GET is naturally idempotent — calling &lt;code&gt;GET /users/1&lt;/code&gt; a thousand times returns the same user a thousand times, with no side effects. POST is the opposite: call &lt;code&gt;POST /users&lt;/code&gt; five times with the same payload, and you'll likely end up with five different records, because POST's entire purpose is to create something new each time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the "use POST for complex reads" trick solved the encoding problem but created a caching and correctness problem. You couldn't cache these disguised read operations at all, even though they were, semantically, just reads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the QUERY Method
&lt;/h2&gt;

&lt;p&gt;This is precisely the gap the new HTTP QUERY method fills. Conceptually, it's simple: QUERY behaves like GET — safe, cacheable, and idempotent — but it allows a request body, just like POST.&lt;/p&gt;

&lt;p&gt;That combination is exactly what was missing. With QUERY:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your intent is unambiguous: you're reading data, not creating or mutating it.&lt;/li&gt;
&lt;li&gt;You can send arbitrarily complex filter logic in the request body — no URL length limits, no manual encoding gymnastics.&lt;/li&gt;
&lt;li&gt;Because the method itself signals "this is a safe, repeatable read," CDNs and caching layers can treat QUERY requests as cacheable, the same way they treat GET.&lt;/li&gt;
&lt;li&gt;Because it's idempotent, retry logic, deduplication, and other reliability patterns that depend on idempotency all work correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, QUERY resolves a long-standing architectural tension between GET and POST — the need for expressive, complex read requests without sacrificing cacheability or correctness.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should You Actually Use It?
&lt;/h2&gt;

&lt;p&gt;This doesn't mean GET is obsolete. For simple lookups and filters — a handful of query parameters — GET with query strings remains perfectly fine and arguably more transparent, since the full request is visible in the URL itself.&lt;/p&gt;

&lt;p&gt;QUERY becomes the better choice when your read operations get complex enough that expressing them in a URL is impractical: multi-field search, nested filters, large parameter sets, or anything that previously would have forced you into the POST workaround.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Lesson
&lt;/h2&gt;

&lt;p&gt;It's worth remembering that REST conventions — GET fetches, POST creates, PUT updates — aren't laws of physics. They're self-imposed rules the developer community agreed on to keep systems interoperable. Nothing stops your server from working if you send data with a method named "hello" instead of GET. The value of these conventions is entirely in the shared understanding they create between client, server, and every piece of infrastructure (caches, proxies, logging tools) sitting in between.&lt;/p&gt;

&lt;p&gt;The QUERY method is a good example of how these conventions evolve: not by breaking the system, but by formalizing a pattern developers were already reaching for through workarounds, and giving it the semantics it always should have had.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Microservices App with Kubernetes: What I Learned the Hard Way</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Tue, 07 Jul 2026 12:52:32 +0000</pubDate>
      <link>https://dev.to/m_toqeer/building-a-microservices-app-with-kubernetes-what-i-learned-the-hard-way-5h95</link>
      <guid>https://dev.to/m_toqeer/building-a-microservices-app-with-kubernetes-what-i-learned-the-hard-way-5h95</guid>
      <description>&lt;p&gt;From Docker to Minikube, Ingress, Services, and Skaffold in one real project&lt;/p&gt;

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

&lt;p&gt;I did not learn Kubernetes by reading theory alone. I learned it by building a real microservices app, breaking things, fixing them, and slowly seeing how all the parts fit together.&lt;/p&gt;

&lt;p&gt;The project was a simple blogging app with posts and comments, but the architecture behind it was the real lesson. Instead of putting everything into one big backend, I split the app into small services: Posts, Comments, Moderation, Query, and an Event Bus. The frontend was a React client. Each service had one clear job. That choice forced me to understand containers, service discovery, deployments, networking, ingress, and local Kubernetes with Minikube.&lt;/p&gt;

&lt;p&gt;This is my learning journey written in simple English. I am sharing what I built, why I built it this way, what confused me, and what finally made the whole picture click.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction to the Project&lt;/li&gt;
&lt;li&gt;Understanding Containers&lt;/li&gt;
&lt;li&gt;Kubernetes Basics&lt;/li&gt;
&lt;li&gt;Kubernetes Architecture&lt;/li&gt;
&lt;li&gt;Deployments&lt;/li&gt;
&lt;li&gt;Services&lt;/li&gt;
&lt;li&gt;Load Balancing&lt;/li&gt;
&lt;li&gt;Communication Between Microservices&lt;/li&gt;
&lt;li&gt;Ingress Controller&lt;/li&gt;
&lt;li&gt;Host File Configuration&lt;/li&gt;
&lt;li&gt;Minikube&lt;/li&gt;
&lt;li&gt;Minikube IP&lt;/li&gt;
&lt;li&gt;Docker Inside Minikube&lt;/li&gt;
&lt;li&gt;Skaffold&lt;/li&gt;
&lt;li&gt;Important Commands Explained&lt;/li&gt;
&lt;li&gt;Common Problems I Faced&lt;/li&gt;
&lt;li&gt;Pros and Cons&lt;/li&gt;
&lt;li&gt;Lessons I Learned&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;What’s Next?&lt;/li&gt;
&lt;li&gt;SEO Details&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Introduction to the Project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is this project?
&lt;/h3&gt;

&lt;p&gt;I built a small blogging app where users can create posts and add comments. The interesting part is not the app idea itself. The interesting part is how the app behaves behind the scenes.&lt;/p&gt;

&lt;p&gt;The system is split into multiple services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Posts Service creates posts&lt;/li&gt;
&lt;li&gt;Comments Service creates comments&lt;/li&gt;
&lt;li&gt;Moderation Service checks comments and approves or rejects them&lt;/li&gt;
&lt;li&gt;Query Service keeps a read-optimized view for the UI&lt;/li&gt;
&lt;li&gt;Event Bus forwards events between services&lt;/li&gt;
&lt;li&gt;React Client shows the user interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project helped me understand event-driven microservices, CQRS, and eventual consistency. In simple words, one service does not try to do everything. Instead, each service does one thing well and talks to others through events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why microservices?
&lt;/h3&gt;

&lt;p&gt;I asked myself a very practical question: why not just build one backend?&lt;/p&gt;

&lt;p&gt;The answer became clearer as the project grew. A monolithic app is easier at the beginning, but it can become heavy later. Every change affects the same codebase and one bug can slow down the whole system.&lt;/p&gt;

&lt;p&gt;Microservices solved a few real problems for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each service could be built and understood separately&lt;/li&gt;
&lt;li&gt;Each service could be deployed separately&lt;/li&gt;
&lt;li&gt;One slow service did not need to block the others&lt;/li&gt;
&lt;li&gt;It was easier to model real business flow as events&lt;/li&gt;
&lt;li&gt;I could practice service-to-service communication the way larger systems do it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why not a monolithic application?
&lt;/h3&gt;

&lt;p&gt;I did think about a monolith first. For a small demo, that would have been simpler. But my goal was learning, not just shipping the shortest possible code.&lt;/p&gt;

&lt;p&gt;If I had used one monolith, I would have missed the hard but useful parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How services discover each other&lt;/li&gt;
&lt;li&gt;How Kubernetes manages containers&lt;/li&gt;
&lt;li&gt;Why ingress matters&lt;/li&gt;
&lt;li&gt;Why internal services use ClusterIP&lt;/li&gt;
&lt;li&gt;How async events keep the system flexible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I chose the harder path because it taught me more.&lt;/p&gt;

&lt;h3&gt;
  
  
  What problems microservices solve
&lt;/h3&gt;

&lt;p&gt;Microservices help when different parts of an application need to move at different speeds.&lt;/p&gt;

&lt;p&gt;For example, in my project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Posts are created quickly&lt;/li&gt;
&lt;li&gt;Comments may need moderation&lt;/li&gt;
&lt;li&gt;The UI reads from a separate query model&lt;/li&gt;
&lt;li&gt;The system should not freeze just because one service is busy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the kind of problem microservices handle well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture overview
&lt;/h3&gt;

&lt;p&gt;Here is the high-level picture I worked with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React Client
    |
    v
Ingress
    |
    +------------------------+
    |                        |
    v                        v
Posts Service           Query Service
    |                        ^
    v                        |
Event Bus --------------&amp;gt; Moderation Service
    |
    v
Comments Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow is event-based. A request starts in the client, goes to a service, and then that service emits an event to the event bus. The event bus shares that event with the other services that care about it.&lt;/p&gt;

&lt;p&gt;That separation was one of the most useful lessons in the whole journey.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Understanding Containers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Docker?
&lt;/h3&gt;

&lt;p&gt;Docker was my entry point into this whole path. Before Docker, I used to think, “Why does an app work on my machine but fail somewhere else?” Docker made that question smaller.&lt;/p&gt;

&lt;p&gt;Docker lets me package an application with everything it needs to run: code, runtime, dependencies, and configuration. That package is called an image. When the image runs, it becomes a container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why containers exist
&lt;/h3&gt;

&lt;p&gt;Containers exist because software environments are messy.&lt;/p&gt;

&lt;p&gt;One machine may have Node.js 16. Another may have Node.js 20. One machine may have missing packages. Another may have a different operating system. Containers reduce that problem by giving the app a predictable environment.&lt;/p&gt;

&lt;p&gt;The easiest way I understood it was this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An image is the blueprint&lt;/li&gt;
&lt;li&gt;A container is the running house built from that blueprint&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Images vs containers
&lt;/h3&gt;

&lt;p&gt;This confused me at first, so I kept it simple.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image: the saved template&lt;/li&gt;
&lt;li&gt;Container: the live running instance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I build the same image ten times, I get ten containers. They all come from the same starting point, but each container runs separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Kubernetes needs Docker
&lt;/h3&gt;

&lt;p&gt;Strictly speaking, Kubernetes does not require Docker specifically anymore, but in my learning path Docker was the tool that built the container images Kubernetes used.&lt;/p&gt;

&lt;p&gt;Kubernetes does not care how the image was built. It cares that the image exists and can be pulled by a node. Docker helped me create those images in a familiar way.&lt;/p&gt;

&lt;h3&gt;
  
  
  How containers communicate
&lt;/h3&gt;

&lt;p&gt;In the project, containers communicate over the network. Each service listens on a port, and other services call that port using HTTP.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Posts Service listens on port 8000&lt;/li&gt;
&lt;li&gt;Comments Service listens on port 8001&lt;/li&gt;
&lt;li&gt;Query Service listens on port 8002&lt;/li&gt;
&lt;li&gt;Moderation Service listens on port 8003&lt;/li&gt;
&lt;li&gt;Event Bus listens on port 5000&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One service sends a request to another service using its network address and port. That is simple in theory, but in Kubernetes it becomes easier when we use Services and DNS names instead of raw IP addresses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple example
&lt;/h3&gt;

&lt;p&gt;If I want the Posts Service to send an event, it posts to the Event Bus endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Posts Service -&amp;gt; http://event-bus-srv:5000/events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That internal address is much better than hardcoding a Pod IP, because Pods can change.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Kubernetes Basics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Kubernetes?
&lt;/h3&gt;

&lt;p&gt;Kubernetes is a system that helps me run containers across one or more machines. It handles placement, restarts, scaling, and networking.&lt;/p&gt;

&lt;p&gt;The easiest way I can describe it is this: Docker runs containers, but Kubernetes organizes them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Kubernetes exists
&lt;/h3&gt;

&lt;p&gt;I used to think Kubernetes was just a fancy way to run Docker. It is not.&lt;/p&gt;

&lt;p&gt;It exists because running one container is easy, but running many containers reliably is hard. Once I had more than one service, I started asking real orchestration questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What if a container dies?&lt;/li&gt;
&lt;li&gt;What if traffic increases?&lt;/li&gt;
&lt;li&gt;Which machine should run which container?&lt;/li&gt;
&lt;li&gt;How do services find each other?&lt;/li&gt;
&lt;li&gt;How do I update a service without breaking everything?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kubernetes helps answer those questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What problems Kubernetes solves
&lt;/h3&gt;

&lt;p&gt;Kubernetes solves a few practical problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It restarts failed containers&lt;/li&gt;
&lt;li&gt;It spreads containers across nodes&lt;/li&gt;
&lt;li&gt;It scales applications up and down&lt;/li&gt;
&lt;li&gt;It gives containers stable network identities through Services&lt;/li&gt;
&lt;li&gt;It supports rolling updates&lt;/li&gt;
&lt;li&gt;It makes local and production setups more similar&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cluster
&lt;/h3&gt;

&lt;p&gt;A cluster is the whole Kubernetes environment. It is the group of machines that work together.&lt;/p&gt;

&lt;p&gt;I like to think of it like a small company. The company is the cluster. Inside it, there are different employees doing different jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node
&lt;/h3&gt;

&lt;p&gt;A node is one machine inside the cluster. It is where Pods run.&lt;/p&gt;

&lt;p&gt;If the cluster is the company, the node is one office building.&lt;/p&gt;

&lt;h3&gt;
  
  
  Master Node or Control Plane
&lt;/h3&gt;

&lt;p&gt;The master node, also called the control plane, is the brain of Kubernetes.&lt;/p&gt;

&lt;p&gt;It decides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which node gets a Pod&lt;/li&gt;
&lt;li&gt;What happens when a Pod dies&lt;/li&gt;
&lt;li&gt;When replicas should be created or removed&lt;/li&gt;
&lt;li&gt;How the cluster stays in the desired state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do not normally run my app there. I let it manage the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worker Node
&lt;/h3&gt;

&lt;p&gt;Worker nodes are the machines that actually run my Pods and containers.&lt;/p&gt;

&lt;p&gt;If the control plane is the manager, worker nodes are the people doing the actual work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pod
&lt;/h3&gt;

&lt;p&gt;A Pod is the smallest deployable unit in Kubernetes.&lt;/p&gt;

&lt;p&gt;This was one of my biggest mental shifts. Kubernetes does not usually run a raw container directly. It runs a Pod, and the Pod contains one or more containers.&lt;/p&gt;

&lt;p&gt;I usually used one container per Pod in this project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Container inside a Pod
&lt;/h3&gt;

&lt;p&gt;A container inside a Pod is the actual process running my app.&lt;/p&gt;

&lt;p&gt;The Pod gives it networking and shared storage if needed. The container is still the thing running Node.js or React.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replica
&lt;/h3&gt;

&lt;p&gt;A replica is another copy of the same Pod.&lt;/p&gt;

&lt;p&gt;If one Pod is not enough to handle traffic, Kubernetes can run two, three, or more copies. That helped me understand scaling in a very simple way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Namespace
&lt;/h3&gt;

&lt;p&gt;A namespace is a way to separate resources inside the same cluster.&lt;/p&gt;

&lt;p&gt;I think of it like separate rooms inside the same building. Everything is still inside the cluster, but the rooms help keep things organized.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service
&lt;/h3&gt;

&lt;p&gt;A Service gives Pods a stable network identity.&lt;/p&gt;

&lt;p&gt;This matters because Pods can come and go. If I try to talk to a Pod directly, the address may change. A Service gives me one stable name and forwards traffic to the matching Pods.&lt;/p&gt;

&lt;p&gt;That idea became very important later when I explained ClusterIP and internal service communication.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Kubernetes Architecture
&lt;/h2&gt;

&lt;p&gt;Once I understood the pieces, I needed to understand how they connect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
   |
   v
Ingress Controller
   |
   v
Service
   |
   v
Pod
   |
   v
Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How the connection works
&lt;/h3&gt;

&lt;p&gt;The request usually starts from the browser.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The browser sends traffic to a host name such as posts.com&lt;/li&gt;
&lt;li&gt;The Ingress Controller receives the request&lt;/li&gt;
&lt;li&gt;Ingress routes the request to the correct Service&lt;/li&gt;
&lt;li&gt;The Service finds a matching Pod&lt;/li&gt;
&lt;li&gt;The Pod forwards the traffic to the container running the app&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That chain is what made Kubernetes feel real to me. It is not random. It is a path.&lt;/p&gt;

&lt;h3&gt;
  
  
  A larger picture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  +----------------------+
                  |   Kubernetes Cluster |
                  +----------+-----------+
                             |
           +-----------------+-----------------+
           |                                   |
           v                                   v
     Worker Node 1                        Worker Node 2
           |                                   |
        Pod(s)                              Pod(s)
           |                                   |
      Container(s)                       Container(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cluster decides where the Pods go. The nodes run them. Services give access to them. Ingress gives the outside world one entry point.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Deployments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Deployments exist
&lt;/h3&gt;

&lt;p&gt;When I first saw a Deployment YAML, I wondered why I could not just create a Pod directly and stop there.&lt;/p&gt;

&lt;p&gt;The answer is that Pods are temporary, but Deployments manage Pods for me.&lt;/p&gt;

&lt;p&gt;A Deployment makes sure the desired number of Pod replicas exists. If a Pod dies, the Deployment creates a new one. If I want to update the app, it helps perform a rolling update.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Pods the right way
&lt;/h3&gt;

&lt;p&gt;I used Deployments instead of manually creating Pods because I wanted Kubernetes to manage the lifecycle.&lt;/p&gt;

&lt;p&gt;For example, the Posts service deployment looks like this in spirit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;posts-depl&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;posts&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;posts&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;posts&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;toqeer43553/posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What each part means
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apiVersion: apps/v1&lt;/code&gt; means I am using the apps API group&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kind: Deployment&lt;/code&gt; means this object manages Pods&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;metadata.name&lt;/code&gt; gives the Deployment a name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spec.replicas: 1&lt;/code&gt; asks Kubernetes to keep one Pod running&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spec.selector.matchLabels&lt;/code&gt; tells the Deployment which Pods belong to it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spec.template&lt;/code&gt; is the Pod template&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spec.template.metadata.labels&lt;/code&gt; must match the selector&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spec.template.spec.containers&lt;/code&gt; defines the container inside the Pod&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;image&lt;/code&gt; tells Kubernetes which image to run&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Replica management
&lt;/h3&gt;

&lt;p&gt;If I change &lt;code&gt;replicas&lt;/code&gt; from 1 to 3, Kubernetes should keep three Pods alive.&lt;/p&gt;

&lt;p&gt;That is useful when traffic grows. It is also useful when I want redundancy. If one Pod crashes, the others can still serve traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rolling updates
&lt;/h3&gt;

&lt;p&gt;Rolling updates mean I can replace old Pods with new ones gradually.&lt;/p&gt;

&lt;p&gt;That is safer than shutting down everything at once.&lt;/p&gt;

&lt;p&gt;The benefit is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fewer outages&lt;/li&gt;
&lt;li&gt;less risk&lt;/li&gt;
&lt;li&gt;easier rollback if something breaks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Self healing
&lt;/h3&gt;

&lt;p&gt;Self healing was one of the most satisfying things to watch.&lt;/p&gt;

&lt;p&gt;If I deleted a Pod manually, the Deployment created a replacement. That taught me the meaning of “desired state.” I say what I want. Kubernetes tries to keep it true.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling
&lt;/h3&gt;

&lt;p&gt;Scaling becomes easy once a Deployment owns the Pods.&lt;/p&gt;

&lt;p&gt;Instead of managing each container by hand, I can ask for more replicas and let Kubernetes handle the rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I needed Deployments in this project
&lt;/h3&gt;

&lt;p&gt;I had six app components. I did not want to manually babysit each one. Deployments gave me a controlled way to run them all and recover from failures.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Services
&lt;/h2&gt;

&lt;p&gt;Services were one of the most important concepts I learned.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Services exist
&lt;/h3&gt;

&lt;p&gt;Pods are temporary. Their IP addresses can change. That means one service cannot safely call another by Pod IP.&lt;/p&gt;

&lt;p&gt;A Service gives me a stable name and a stable way to reach matching Pods.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service types
&lt;/h3&gt;

&lt;p&gt;I learned four common Service types.&lt;/p&gt;

&lt;h4&gt;
  
  
  ClusterIP
&lt;/h4&gt;

&lt;p&gt;ClusterIP is the default service type. It exposes the service only inside the cluster.&lt;/p&gt;

&lt;p&gt;I used ClusterIP for almost all internal services in this project.&lt;/p&gt;

&lt;p&gt;Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;only other services inside Kubernetes need access&lt;/li&gt;
&lt;li&gt;you want internal service discovery&lt;/li&gt;
&lt;li&gt;you do not want public access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;secure by default&lt;/li&gt;
&lt;li&gt;stable internal address&lt;/li&gt;
&lt;li&gt;great for microservice-to-microservice communication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not reachable directly from outside the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;posts-clusterip-srv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;comments-srv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;query-srv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;event-bus-srv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;moderation-srv&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  NodePort
&lt;/h4&gt;

&lt;p&gt;NodePort exposes a service on a port on every node.&lt;/p&gt;

&lt;p&gt;I used it for the Posts service in the project to help with external access in some learning setups.&lt;/p&gt;

&lt;p&gt;Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you want a simple way to access a service from outside&lt;/li&gt;
&lt;li&gt;you are learning or prototyping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple to understand&lt;/li&gt;
&lt;li&gt;easy to test locally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;less flexible than Ingress&lt;/li&gt;
&lt;li&gt;awkward for many services&lt;/li&gt;
&lt;li&gt;not ideal for production&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  LoadBalancer
&lt;/h4&gt;

&lt;p&gt;LoadBalancer asks a cloud provider to create an external load balancer.&lt;/p&gt;

&lt;p&gt;Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you are in a cloud environment&lt;/li&gt;
&lt;li&gt;you want a public entry point with managed infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple cloud integration&lt;/li&gt;
&lt;li&gt;external traffic routing is handled for you&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;depends on cloud support&lt;/li&gt;
&lt;li&gt;not very useful in a pure local Minikube setup&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ExternalName
&lt;/h4&gt;

&lt;p&gt;ExternalName maps a service name to an external DNS name.&lt;/p&gt;

&lt;p&gt;Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes needs to refer to something outside the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple DNS aliasing&lt;/li&gt;
&lt;li&gt;useful for external dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;does not act like normal proxy routing&lt;/li&gt;
&lt;li&gt;not good for every use case&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real examples from my project
&lt;/h3&gt;

&lt;p&gt;The internal services all use ClusterIP so that one service can talk to another by name.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://event-bus-srv:5000/events
http://comments-srv:8001/events
http://query-srv:8002/events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was much cleaner than hardcoding IPs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I chose ClusterIP for internal communication
&lt;/h3&gt;

&lt;p&gt;Because the services talk to each other inside the cluster.&lt;/p&gt;

&lt;p&gt;I did not want the browser or the public internet calling my internal moderation service directly. ClusterIP kept those services private while still making them reachable from inside the cluster.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Load Balancing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why load balancing is important
&lt;/h3&gt;

&lt;p&gt;If one service gets a lot of traffic, one Pod may not be enough. Load balancing spreads requests across available Pods.&lt;/p&gt;

&lt;p&gt;That matters because it improves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;performance&lt;/li&gt;
&lt;li&gt;reliability&lt;/li&gt;
&lt;li&gt;availability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How Kubernetes balances traffic
&lt;/h3&gt;

&lt;p&gt;When I send traffic to a Service, Kubernetes forwards it to one of the matching Pods.&lt;/p&gt;

&lt;p&gt;If there are multiple replicas, the Service acts like a traffic distributor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
  |
  v
Service
  |
  +-------&amp;gt; Pod 1
  +-------&amp;gt; Pod 2
  +-------&amp;gt; Pod 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What happens if one Pod crashes
&lt;/h3&gt;

&lt;p&gt;If a Pod crashes, the Deployment replaces it.&lt;/p&gt;

&lt;p&gt;If traffic is still flowing, the Service stops sending requests to the dead Pod and keeps using the healthy ones.&lt;/p&gt;

&lt;p&gt;That gave me a lot more confidence in Kubernetes. It was not just running containers. It was actively keeping the system alive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple example
&lt;/h3&gt;

&lt;p&gt;If I had three replicas of the Query service, the Service could send requests to any healthy replica.&lt;/p&gt;

&lt;p&gt;That means users would not care which exact Pod answered. They only care that the app works.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Communication Between Microservices
&lt;/h2&gt;

&lt;p&gt;This is where the project became interesting.&lt;/p&gt;

&lt;h3&gt;
  
  
  The event flow I built
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Posts Service
    |
    v
Event Bus
    |
    v
Query Service

Comments Service
    |
    v
Event Bus
    |
    +-------------------+
    |                   |
    v                   v
Moderation Service   Query Service
    |
    v
Event Bus
    |
    v
Comments Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data does not move in a straight line like a normal form submit. It moves as events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why ClusterIP is used here
&lt;/h3&gt;

&lt;p&gt;Because these services should talk privately inside the cluster.&lt;/p&gt;

&lt;p&gt;The browser does not need to know where Moderation lives. The browser only talks to the public entry point. The internal services talk to each other through internal DNS names such as &lt;code&gt;query-srv&lt;/code&gt; or &lt;code&gt;event-bus-srv&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not use Pod IPs
&lt;/h3&gt;

&lt;p&gt;Because Pod IPs are temporary.&lt;/p&gt;

&lt;p&gt;If a Pod restarts, Kubernetes may assign a new IP. That would break hardcoded service addresses.&lt;/p&gt;

&lt;p&gt;Using service names solves that problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service discovery
&lt;/h3&gt;

&lt;p&gt;Service discovery means services can find each other by name.&lt;/p&gt;

&lt;p&gt;I did not need to ask, “What is the IP address of the Event Bus today?” I could just use the service DNS name.&lt;/p&gt;

&lt;p&gt;That is a huge reason Kubernetes feels manageable once the basics click.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal DNS
&lt;/h3&gt;

&lt;p&gt;Inside the cluster, a service name becomes a DNS entry.&lt;/p&gt;

&lt;p&gt;That is why code like this works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://event-bus-srv:5000/events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service name is resolved inside Kubernetes, not by my laptop’s normal DNS setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example from the project
&lt;/h3&gt;

&lt;p&gt;When the Posts service creates a post, it stores it locally and then sends a &lt;code&gt;PostCreated&lt;/code&gt; event to the Event Bus.&lt;/p&gt;

&lt;p&gt;The Event Bus forwards that event to the Query service, which updates the read model.&lt;/p&gt;

&lt;p&gt;That is the central idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;write side changes data&lt;/li&gt;
&lt;li&gt;event bus shares the change&lt;/li&gt;
&lt;li&gt;read side updates asynchronously&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is event-driven microservices in plain language.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Ingress Controller
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Ingress exists
&lt;/h3&gt;

&lt;p&gt;Before Ingress, each service needed its own exposure strategy.&lt;/p&gt;

&lt;p&gt;That becomes messy fast.&lt;/p&gt;

&lt;p&gt;Ingress gives me one external entry point and lets me route requests based on host or path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problems before Ingress
&lt;/h3&gt;

&lt;p&gt;Without Ingress, I would have needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple NodePorts&lt;/li&gt;
&lt;li&gt;manual routing logic outside the cluster&lt;/li&gt;
&lt;li&gt;more open ports&lt;/li&gt;
&lt;li&gt;a harder mental model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is not fun once a project grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  One entry point
&lt;/h3&gt;

&lt;p&gt;Ingress lets me say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;requests to &lt;code&gt;/create-post&lt;/code&gt; go to the Posts service&lt;/li&gt;
&lt;li&gt;requests to &lt;code&gt;/get-posts&lt;/code&gt; go to the Query service&lt;/li&gt;
&lt;li&gt;requests to &lt;code&gt;/post/.../comments&lt;/code&gt; go to the Comments service&lt;/li&gt;
&lt;li&gt;everything else goes to the React client&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Host-based routing
&lt;/h3&gt;

&lt;p&gt;The host in my setup is &lt;code&gt;posts.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That means the app can be reached by a friendly domain instead of by a raw IP and port.&lt;/p&gt;

&lt;h3&gt;
  
  
  Path-based routing
&lt;/h3&gt;

&lt;p&gt;Path-based routing means different URL paths go to different backend services.&lt;/p&gt;

&lt;p&gt;Here is the idea in my project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts.com/create-post  -&amp;gt; Posts Service
posts.com/get-posts    -&amp;gt; Query Service
posts.com/post/...     -&amp;gt; Comments Service
posts.com/             -&amp;gt; Client Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  My ingress configuration
&lt;/h3&gt;

&lt;p&gt;The real Ingress manifest uses the nginx ingress class and a regex path for the comments route.&lt;/p&gt;

&lt;p&gt;That setup lets Kubernetes route requests to the correct service without the browser needing to know internal service names.&lt;/p&gt;

&lt;h3&gt;
  
  
  How a request travels
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
  |
  v
posts.com/get-posts
  |
  v
Ingress Controller
  |
  v
query-srv
  |
  v
Query Pod
  |
  v
Response to browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That path was one of the clearest signs that I was learning real Kubernetes networking, not just memorizing YAML.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Host File Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why I edited the hosts file
&lt;/h3&gt;

&lt;p&gt;My browser needs to know what &lt;code&gt;posts.com&lt;/code&gt; means.&lt;/p&gt;

&lt;p&gt;In local development, that name does not exist on the public internet. So I had to teach my laptop to map &lt;code&gt;posts.com&lt;/code&gt; to the Minikube IP.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1 posts.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, more commonly in Minikube setups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Minikube IP&amp;gt; posts.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why browsers need this
&lt;/h3&gt;

&lt;p&gt;When I type a domain into the browser, the browser asks DNS where to send the request.&lt;/p&gt;

&lt;p&gt;If the domain is only for local development, I need a manual mapping. The hosts file gives me that mapping.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I learned
&lt;/h3&gt;

&lt;p&gt;This part felt small at first, but it was very important.&lt;/p&gt;

&lt;p&gt;If the host file is wrong, Ingress may be working perfectly and the browser will still fail. That made me realize how many layers exist between “type URL” and “see page.”&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Minikube
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Minikube?
&lt;/h3&gt;

&lt;p&gt;Minikube is a local Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;It lets me practice Kubernetes on my own machine without needing a cloud account or a production cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use Minikube
&lt;/h3&gt;

&lt;p&gt;I used Minikube because it made learning possible.&lt;/p&gt;

&lt;p&gt;It gave me a safe place to test things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployments&lt;/li&gt;
&lt;li&gt;Services&lt;/li&gt;
&lt;li&gt;Ingress&lt;/li&gt;
&lt;li&gt;Pods&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;local image handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Local Kubernetes
&lt;/h3&gt;

&lt;p&gt;Minikube is Kubernetes, but small and local.&lt;/p&gt;

&lt;p&gt;That was perfect for me because I could test, break, and retry quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cluster creation
&lt;/h3&gt;

&lt;p&gt;I learned that starting a cluster is the first step before applying manifests.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;That command gives me a local cluster to work with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cluster deletion
&lt;/h3&gt;

&lt;p&gt;When I wanted a clean restart, I could delete the cluster and create it again.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;That saved me many times when I had a messy local state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Status
&lt;/h3&gt;

&lt;p&gt;I used status checks to make sure the cluster was healthy before blaming my YAML.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dashboard
&lt;/h3&gt;

&lt;p&gt;The Minikube dashboard helped me see what was running.&lt;/p&gt;

&lt;p&gt;Seeing Pods, Services, and Deployments in a UI made the system much less abstract.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessing services
&lt;/h3&gt;

&lt;p&gt;Minikube also helped me access Services through IPs and tunnel-like behavior depending on the setup.&lt;/p&gt;

&lt;p&gt;That became especially important when I started learning NodePort, Ingress, and the hosts file.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Minikube IP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Minikube has its own IP
&lt;/h3&gt;

&lt;p&gt;Minikube runs as its own local Kubernetes environment. It is not the same thing as my laptop’s &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That means the cluster may have a separate IP address.&lt;/p&gt;

&lt;h3&gt;
  
  
  localhost vs Minikube IP
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;localhost&lt;/code&gt; means my own machine.&lt;/p&gt;

&lt;p&gt;Minikube IP means the address where the Minikube cluster can be reached.&lt;/p&gt;

&lt;p&gt;These are sometimes related, but they are not the same thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why NodePort works
&lt;/h3&gt;

&lt;p&gt;NodePort exposes a port on the node.&lt;/p&gt;

&lt;p&gt;If I know the Minikube IP and the NodePort, I can reach the service from outside the cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why localhost sometimes works
&lt;/h3&gt;

&lt;p&gt;In some setups, Minikube is close enough to my local environment that &lt;code&gt;localhost&lt;/code&gt; can work through port forwarding or driver-specific behavior.&lt;/p&gt;

&lt;p&gt;That made me realize there is no single universal local Kubernetes setup. The exact access path depends on the driver and cluster configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal networking
&lt;/h3&gt;

&lt;p&gt;Inside the cluster, Services and Pods talk on the internal Kubernetes network.&lt;/p&gt;

&lt;p&gt;Outside the cluster, I need Ingress, NodePort, port forwarding, or another access method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple diagram
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop browser
    |
    v
localhost or Minikube IP
    |
    v
NodePort / Ingress
    |
    v
Service
    |
    v
Pod
    |
    v
Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That separation helped me understand why some URLs worked and some did not.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Docker Inside Minikube
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How Docker works inside Minikube
&lt;/h3&gt;

&lt;p&gt;This was a painful but useful lesson.&lt;/p&gt;

&lt;p&gt;I first built images on my laptop and expected Minikube to see them automatically.&lt;/p&gt;

&lt;p&gt;That is because Minikube often uses its own image environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why images are built inside Minikube
&lt;/h3&gt;

&lt;p&gt;If Kubernetes inside Minikube tries to pull an image, it needs that image to exist in a place it can access.&lt;/p&gt;

&lt;p&gt;When I build locally, my host Docker may have the image. But Minikube’s cluster may not see it unless I either push it to a registry or build it in Minikube’s Docker environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Kubernetes cannot see local Docker images
&lt;/h3&gt;

&lt;p&gt;This was one of my most confusing early mistakes.&lt;/p&gt;

&lt;p&gt;I had the image on my machine, so I assumed Kubernetes would just use it. But the cluster is not my laptop shell. The cluster is its own environment.&lt;/p&gt;

&lt;p&gt;That is why image visibility matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker-env
&lt;/h3&gt;

&lt;p&gt;The Minikube Docker environment helps me build images directly into Minikube’s image store.&lt;/p&gt;

&lt;p&gt;The command looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eval $(minikube docker-env)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why this helps
&lt;/h3&gt;

&lt;p&gt;After running that command, my Docker CLI talks to Minikube’s Docker daemon instead of my local one.&lt;/p&gt;

&lt;p&gt;That means when I run &lt;code&gt;docker build&lt;/code&gt;, the image is built where Kubernetes can see it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple explanation
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;My laptop Docker -&amp;gt; local image only
Minikube -&amp;gt; cannot see it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;My laptop Docker CLI -&amp;gt; Minikube Docker daemon -&amp;gt; image available to Kubernetes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What I learned
&lt;/h3&gt;

&lt;p&gt;I stopped thinking of Docker as one global thing. It is really a client talking to a Docker environment.&lt;/p&gt;

&lt;p&gt;That small realization made Minikube much easier to work with.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Skaffold
&lt;/h2&gt;

&lt;p&gt;Skaffold was the tool that made the whole workflow feel smoother.&lt;/p&gt;

&lt;h3&gt;
  
  
  What problem Skaffold solves
&lt;/h3&gt;

&lt;p&gt;Without Skaffold, my workflow looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;edit code
 -&amp;gt; docker build
 -&amp;gt; docker push
 -&amp;gt; kubectl apply
 -&amp;gt; check rollout
 -&amp;gt; repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is slow and annoying during development.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Skaffold automates everything
&lt;/h3&gt;

&lt;p&gt;Skaffold watches files, rebuilds images, and redeploys Kubernetes resources for me.&lt;/p&gt;

&lt;p&gt;That means I can focus on changing code instead of repeating the same commands all day.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;skaffold dev&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command starts a development loop.&lt;/p&gt;

&lt;p&gt;It watches for file changes and updates the cluster.&lt;/p&gt;

&lt;p&gt;That was huge for productivity because I could edit, save, and see changes with much less manual work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image rebuild
&lt;/h3&gt;

&lt;p&gt;When source files change, Skaffold can rebuild the matching image.&lt;/p&gt;

&lt;h3&gt;
  
  
  File watching
&lt;/h3&gt;

&lt;p&gt;It watches my app files so I do not have to trigger everything manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sync
&lt;/h3&gt;

&lt;p&gt;For some files, Skaffold can sync changes directly into a running container instead of rebuilding every time.&lt;/p&gt;

&lt;p&gt;That makes the feedback loop faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto deployment
&lt;/h3&gt;

&lt;p&gt;When images change, Skaffold applies the Kubernetes manifests again.&lt;/p&gt;

&lt;p&gt;That closes the loop between code and cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hot reload
&lt;/h3&gt;

&lt;p&gt;In practice, it feels close to hot reload for the whole stack, even though the exact behavior depends on the app and file syncing setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Skaffold YAML
&lt;/h3&gt;

&lt;p&gt;The config in this project defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Kubernetes manifests to deploy&lt;/li&gt;
&lt;li&gt;the local build settings&lt;/li&gt;
&lt;li&gt;the images for client and each service&lt;/li&gt;
&lt;li&gt;the sync rules for JavaScript files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was a clean way to keep development repeatable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Skaffold mattered so much
&lt;/h3&gt;

&lt;p&gt;It reduced friction.&lt;/p&gt;

&lt;p&gt;When a workflow gets too manual, I spend more time operating tools than learning the system. Skaffold pushed me back toward learning the app itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  15. Important Commands Explained
&lt;/h2&gt;

&lt;p&gt;This is a compact cheat sheet for the commands I kept using.&lt;/p&gt;

&lt;h3&gt;
  
  
  kubectl commands
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubectl get pods&lt;/code&gt;: shows Pods.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl get services&lt;/code&gt;: shows Services.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl get deployments&lt;/code&gt;: shows Deployments.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl describe pod&lt;/code&gt;: shows Pod events.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl logs&lt;/code&gt;: prints logs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl delete pod&lt;/code&gt;: removes a Pod.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl apply -f&lt;/code&gt;: applies YAML.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl delete -f&lt;/code&gt;: deletes YAML resources.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl rollout restart&lt;/code&gt;: restarts a Deployment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl exec&lt;/code&gt;: runs a command inside a container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl port-forward&lt;/code&gt;: forwards a local port.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl get nodes&lt;/code&gt;: shows cluster nodes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl get ingress&lt;/code&gt;: shows Ingress resources.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl describe ingress&lt;/code&gt;: helps debug routing.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f infra/k8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Minikube commands
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;minikube start&lt;/code&gt;: starts the cluster.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minikube stop&lt;/code&gt;: pauses the cluster.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minikube delete&lt;/code&gt;: resets the cluster.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minikube ip&lt;/code&gt;: prints the cluster IP.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minikube dashboard&lt;/code&gt;: opens the dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minikube docker-env&lt;/code&gt;: prints Docker settings.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eval $(minikube docker-env)&lt;/code&gt;: points my shell at Minikube Docker.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Skaffold commands
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;skaffold dev&lt;/code&gt;: starts watch mode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skaffold run&lt;/code&gt;: does one build and deploy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Docker commands
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker build&lt;/code&gt;: creates an image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker images&lt;/code&gt;: shows local images.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker ps&lt;/code&gt;: shows running containers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker logs&lt;/code&gt;: shows container output.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker exec&lt;/code&gt;: runs a command inside a container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker tag&lt;/code&gt;: renames an image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker push&lt;/code&gt;: uploads an image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I knew these commands, I could move from confusion to inspection much faster.&lt;/p&gt;




&lt;h2&gt;
  
  
  16. Common Problems I Faced
&lt;/h2&gt;

&lt;p&gt;This project taught me that Kubernetes is full of small mistakes that look big at first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pods not starting
&lt;/h3&gt;

&lt;p&gt;Usually this happened because of an image issue, a bad manifest, or a port mismatch.&lt;/p&gt;

&lt;p&gt;How I debugged it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ran &lt;code&gt;kubectl get pods&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;used &lt;code&gt;kubectl describe pod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;checked &lt;code&gt;kubectl logs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ImagePullBackOff
&lt;/h3&gt;

&lt;p&gt;This often meant Kubernetes could not find the image.&lt;/p&gt;

&lt;p&gt;My fix was usually one of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build the image in Minikube Docker&lt;/li&gt;
&lt;li&gt;make sure the image name matches the manifest&lt;/li&gt;
&lt;li&gt;ensure the tag is correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CrashLoopBackOff
&lt;/h3&gt;

&lt;p&gt;This usually meant the container started and then crashed repeatedly.&lt;/p&gt;

&lt;p&gt;Common causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;app error&lt;/li&gt;
&lt;li&gt;missing environment assumptions&lt;/li&gt;
&lt;li&gt;wrong port&lt;/li&gt;
&lt;li&gt;bad startup code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Service not found
&lt;/h3&gt;

&lt;p&gt;This usually meant I typed the wrong service name or expected the wrong DNS name.&lt;/p&gt;

&lt;p&gt;I learned to compare the service name in YAML with the one used in code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ingress not working
&lt;/h3&gt;

&lt;p&gt;This happened when the ingress controller was missing, the host file was wrong, or the route path did not match.&lt;/p&gt;

&lt;p&gt;I learned to check all three layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ingress resource exists&lt;/li&gt;
&lt;li&gt;ingress controller is running&lt;/li&gt;
&lt;li&gt;browser hostname resolves correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hosts file issues
&lt;/h3&gt;

&lt;p&gt;If the host mapping was wrong, nothing else mattered.&lt;/p&gt;

&lt;p&gt;That was a reminder that local networking issues can look like app issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minikube IP changed
&lt;/h3&gt;

&lt;p&gt;When the Minikube IP changed, my hosts file mapping became stale.&lt;/p&gt;

&lt;p&gt;The fix was to run &lt;code&gt;minikube ip&lt;/code&gt; again and update the mapping.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker image not updating
&lt;/h3&gt;

&lt;p&gt;This happened when I changed code but the cluster kept using an old image.&lt;/p&gt;

&lt;p&gt;The fix was usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rebuild the image&lt;/li&gt;
&lt;li&gt;use Skaffold&lt;/li&gt;
&lt;li&gt;verify Minikube Docker context&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Skaffold not rebuilding
&lt;/h3&gt;

&lt;p&gt;This usually meant the file sync rules were too narrow or the config did not match the file location.&lt;/p&gt;

&lt;p&gt;I learned to check the sync paths carefully.&lt;/p&gt;

&lt;h3&gt;
  
  
  My debugging habit
&lt;/h3&gt;

&lt;p&gt;The biggest lesson was not to guess.&lt;/p&gt;

&lt;p&gt;I tried to inspect one layer at a time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the Pod running?&lt;/li&gt;
&lt;li&gt;Is the Service correct?&lt;/li&gt;
&lt;li&gt;Is the Ingress correct?&lt;/li&gt;
&lt;li&gt;Does the host file point to the right place?&lt;/li&gt;
&lt;li&gt;Does the app log show errors?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That simple checklist saved me a lot of time.&lt;/p&gt;




&lt;h2&gt;
  
  
  17. Pros and Cons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pros of Kubernetes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It keeps services running and replaces failed Pods&lt;/li&gt;
&lt;li&gt;It supports service discovery&lt;/li&gt;
&lt;li&gt;It works from local dev to production&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons of Kubernetes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It has a steep learning curve&lt;/li&gt;
&lt;li&gt;YAML can be tedious&lt;/li&gt;
&lt;li&gt;Local setups can differ from production&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pros of Skaffold
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It speeds up development&lt;/li&gt;
&lt;li&gt;It reduces manual commands&lt;/li&gt;
&lt;li&gt;It watches files and rebuilds automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons of Skaffold
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The config can be confusing at first&lt;/li&gt;
&lt;li&gt;Sync rules need care&lt;/li&gt;
&lt;li&gt;It adds another tool to learn&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pros of Ingress
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;One entry point for many services&lt;/li&gt;
&lt;li&gt;Clean routing by host and path&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons of Ingress
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Needs an ingress controller&lt;/li&gt;
&lt;li&gt;Debugging can be tricky&lt;/li&gt;
&lt;li&gt;Host and path rules must match exactly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pros of Microservices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clear service boundaries&lt;/li&gt;
&lt;li&gt;Easier to scale parts separately&lt;/li&gt;
&lt;li&gt;Better for event-driven systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons of Microservices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;More moving parts&lt;/li&gt;
&lt;li&gt;More networking problems&lt;/li&gt;
&lt;li&gt;More concepts to learn&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  18. Lessons I Learned
&lt;/h2&gt;

&lt;p&gt;This is the part that matters most to me.&lt;/p&gt;

&lt;p&gt;At first, Kubernetes felt like too many objects with strange names. What helped was not reading more definitions. What helped was building something real.&lt;/p&gt;

&lt;p&gt;Once I saw the posts app create an event, then watched the query model update, then saw comments go through moderation, I started to understand why these parts exist.&lt;/p&gt;

&lt;p&gt;The biggest mental shift was this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker runs containers&lt;/li&gt;
&lt;li&gt;Kubernetes manages them&lt;/li&gt;
&lt;li&gt;Services make them reachable&lt;/li&gt;
&lt;li&gt;Ingress makes them public in a controlled way&lt;/li&gt;
&lt;li&gt;Skaffold makes development faster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also learned that microservices are not just about splitting code. They are about splitting responsibility.&lt;/p&gt;

&lt;p&gt;Another thing that became easier was reading YAML. At first it felt dry and repetitive, but later I saw it as a contract between me and Kubernetes.&lt;/p&gt;




&lt;h2&gt;
  
  
  19. Best Practices
&lt;/h2&gt;

&lt;p&gt;Here are the beginner-friendly practices that helped me most.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep services small
&lt;/h3&gt;

&lt;p&gt;Each service should have one clear job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use service names, not Pod IPs
&lt;/h3&gt;

&lt;p&gt;Pod IPs can change. Service names are stable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separate write and read paths when it makes sense
&lt;/h3&gt;

&lt;p&gt;The CQRS idea helped my app stay organized.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use ClusterIP for internal services
&lt;/h3&gt;

&lt;p&gt;Do not expose everything publicly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Ingress for the public entry point
&lt;/h3&gt;

&lt;p&gt;This keeps routing clean.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check logs early
&lt;/h3&gt;

&lt;p&gt;Logs often tell the truth faster than guesswork.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn the manifest one piece at a time
&lt;/h3&gt;

&lt;p&gt;Do not try to memorize everything at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep your local environment clean
&lt;/h3&gt;

&lt;p&gt;If Minikube becomes messy, reset it instead of fighting stale state forever.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Skaffold during development
&lt;/h3&gt;

&lt;p&gt;It saves time and keeps the feedback loop short.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand the path of a request
&lt;/h3&gt;

&lt;p&gt;Always ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where did the request start?&lt;/li&gt;
&lt;li&gt;Which service received it?&lt;/li&gt;
&lt;li&gt;Which Service forwarded it?&lt;/li&gt;
&lt;li&gt;Which Pod handled it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That question alone will save you a lot of confusion.&lt;/p&gt;




&lt;h2&gt;
  
  
  20. Conclusion
&lt;/h2&gt;

&lt;p&gt;I started this project wanting to learn Kubernetes. I ended up learning much more.&lt;/p&gt;

&lt;p&gt;I learned how containers package software.&lt;/p&gt;

&lt;p&gt;I learned how Kubernetes runs those containers across a cluster.&lt;/p&gt;

&lt;p&gt;I learned why Deployments matter, why Services matter, why Ingress matters, and why local tools like Minikube and Skaffold make practice possible.&lt;/p&gt;

&lt;p&gt;Most of all, I learned that Kubernetes becomes easier when it is tied to a real project.&lt;/p&gt;

&lt;p&gt;On their own, the concepts can feel abstract. But when I used them to run a real microservices app, they started to make sense.&lt;/p&gt;

&lt;p&gt;If you are a beginner, do not try to understand everything in one day. Build something small, break it, and repeat.&lt;/p&gt;

&lt;p&gt;That is how the pieces connect.&lt;/p&gt;




&lt;h2&gt;
  
  
  21. What’s Next?
&lt;/h2&gt;

&lt;p&gt;This project gave me a good base, but I know there is more to learn.&lt;/p&gt;

&lt;p&gt;The next topics I want to study are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ConfigMaps&lt;/li&gt;
&lt;li&gt;Secrets&lt;/li&gt;
&lt;li&gt;Persistent Volumes&lt;/li&gt;
&lt;li&gt;Helm&lt;/li&gt;
&lt;li&gt;Horizontal Pod Autoscaler&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;CI/CD&lt;/li&gt;
&lt;li&gt;Production Kubernetes&lt;/li&gt;
&lt;li&gt;Cloud Kubernetes such as AWS EKS, GKE, and AKS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I feel more ready for those topics now because I understand the basics from a real app, not just from theory.&lt;/p&gt;




&lt;h2&gt;
  
  
  SEO Details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SEO title
&lt;/h3&gt;

&lt;p&gt;Building a Microservices App with Kubernetes: My Beginner-Friendly Journey Through Docker, Minikube, Ingress, and Skaffold&lt;/p&gt;

&lt;h3&gt;
  
  
  Meta description
&lt;/h3&gt;

&lt;p&gt;A beginner-friendly story of building a microservices application with Kubernetes, covering Docker, Pods, Deployments, Services, Ingress, Minikube, Docker inside Minikube, and Skaffold in simple English.&lt;/p&gt;

&lt;h3&gt;
  
  
  Suggested URL slug
&lt;/h3&gt;

&lt;p&gt;building-microservices-app-with-kubernetes-docker-minikube-ingress-skaffold&lt;/p&gt;

&lt;h3&gt;
  
  
  Dev.to tags
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;kubernetes&lt;/li&gt;
&lt;li&gt;docker&lt;/li&gt;
&lt;li&gt;microservices&lt;/li&gt;
&lt;li&gt;devops&lt;/li&gt;
&lt;li&gt;beginners&lt;/li&gt;
&lt;li&gt;minikube&lt;/li&gt;
&lt;li&gt;skaffold&lt;/li&gt;
&lt;li&gt;ingress&lt;/li&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;react&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Suggested cover image idea
&lt;/h3&gt;

&lt;p&gt;A clean illustration of a laptop connected to a Kubernetes cluster, with small boxes labeled Docker, Pods, Services, Ingress, and Skaffold, styled like a simple system map.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>kubernetes</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Kubernetes Internals Explained: From Linux Primitives to Ingress</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Tue, 07 Jul 2026 08:15:51 +0000</pubDate>
      <link>https://dev.to/m_toqeer/kubernetes-internals-explained-from-linux-primitives-to-ingress-21a5</link>
      <guid>https://dev.to/m_toqeer/kubernetes-internals-explained-from-linux-primitives-to-ingress-21a5</guid>
      <description>&lt;p&gt;If you've ever felt like Kubernetes is a pile of magic YAML files, this article is for you. We're going to build understanding from the ground up — starting with plain Linux, moving to containers, and then climbing all the way to Pods, Nodes, Deployments, Services, and Ingress. By the end, you won't just know the &lt;em&gt;names&lt;/em&gt; of these pieces — you'll know &lt;em&gt;why&lt;/em&gt; they exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Start With Linux?
&lt;/h2&gt;

&lt;p&gt;Kubernetes didn't invent containers. It's an &lt;strong&gt;orchestrator&lt;/strong&gt; sitting on top of Linux features that already existed. If you understand the Linux primitives first, every Kubernetes concept afterward becomes "oh, that's just automating this thing I already understand" instead of new magic to memorize.&lt;/p&gt;

&lt;p&gt;Two Linux kernel features make containers possible:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Namespaces — Isolation
&lt;/h3&gt;

&lt;p&gt;A namespace makes a process think it's the only thing running on the machine. Linux has several kinds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PID namespace&lt;/strong&gt; – the process sees itself as PID 1, even though the host sees it as PID 48213.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network namespace&lt;/strong&gt; – gives the process its own network interfaces, IP address, and routing table, separate from the host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mount namespace&lt;/strong&gt; – gives the process its own view of the filesystem (its own &lt;code&gt;/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UTS namespace&lt;/strong&gt; – lets the process have its own hostname.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IPC namespace&lt;/strong&gt; – isolates inter-process communication (shared memory, semaphores).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User namespace&lt;/strong&gt; – lets a process be "root" inside the container but be an unprivileged user on the host.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;strong&gt;container is just a regular Linux process&lt;/strong&gt; that has been put inside a bundle of these namespaces so it &lt;em&gt;believes&lt;/em&gt; it's alone on the machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Control Groups (cgroups) — Resource Limits
&lt;/h3&gt;

&lt;p&gt;Namespaces isolate &lt;em&gt;what a process can see&lt;/em&gt;. Cgroups control &lt;em&gt;how much it can use&lt;/em&gt; — CPU shares, memory limits, disk I/O, network bandwidth. This is how you can say "this container gets max 512Mi of RAM" and have the kernel actually enforce it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; Container = Linux process + namespaces (isolation) + cgroups (resource limits) + a filesystem bundle (the image). Nothing more mystical than that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Container Images
&lt;/h2&gt;

&lt;p&gt;An image is a &lt;strong&gt;read-only template&lt;/strong&gt; for creating containers. It's built in &lt;strong&gt;layers&lt;/strong&gt; — each instruction in a Dockerfile (&lt;code&gt;FROM&lt;/code&gt;, &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt;, etc.) creates a new layer stacked on top of the previous one using a union filesystem (like OverlayFS).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;Layer 4: COPY . /app          &amp;lt;- your app code
Layer 3: RUN npm install      &amp;lt;- dependencies
Layer 2: RUN apt-get update   &amp;lt;- OS packages
Layer 1: FROM node:20-alpine  &amp;lt;- base OS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why layers matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layers are &lt;strong&gt;cached&lt;/strong&gt; — if only your app code changes, Docker/containerd reuses the lower layers instead of rebuilding everything.&lt;/li&gt;
&lt;li&gt;Layers are &lt;strong&gt;shared&lt;/strong&gt; across images — ten images built &lt;code&gt;FROM node:20-alpine&lt;/code&gt; share that base layer on disk instead of duplicating it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Images live in &lt;strong&gt;registries&lt;/strong&gt; (Docker Hub, GitHub Container Registry, AWS ECR, etc.). When Kubernetes needs to run a container, it pulls the image from a registry to the Node first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Container Runtime
&lt;/h2&gt;

&lt;p&gt;This is the piece that actually &lt;strong&gt;takes an image and turns it into a running, namespaced, cgroup-limited process&lt;/strong&gt;. Kubernetes doesn't talk to Docker directly anymore — it talks through a standard interface called &lt;strong&gt;CRI (Container Runtime Interface)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The common runtime stack today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubelet
   │  (talks CRI protocol)
   ▼
containerd  (high-level runtime: manages images, pulls, storage)
   │
   ▼
runc  (low-level runtime: actually calls Linux syscalls to create
       namespaces/cgroups and start the process)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;runc&lt;/code&gt; is doing the literal work we described in the Linux section: calling &lt;code&gt;clone()&lt;/code&gt; with namespace flags, setting up cgroups, and &lt;code&gt;exec()&lt;/code&gt;-ing your process. Everything above it is orchestration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pods — The Smallest Deployable Unit
&lt;/h2&gt;

&lt;p&gt;Here's a question that trips people up: &lt;strong&gt;why doesn't Kubernetes just schedule containers directly?&lt;/strong&gt; Why the extra layer of a "Pod"?&lt;/p&gt;

&lt;p&gt;Answer: because real applications are often more than one process that need to share resources tightly — think of a main app container plus a logging sidecar that both need to read the same files and talk over &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Pod&lt;/strong&gt; is a group of one or more containers that share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The same network namespace&lt;/strong&gt; — all containers in a Pod share one IP address and can reach each other over &lt;code&gt;localhost&lt;/code&gt;. Container A on port 8080 and container B on port 9090 in the same Pod just call &lt;code&gt;localhost:8080&lt;/code&gt; / &lt;code&gt;localhost:9090&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optionally, storage volumes&lt;/strong&gt; — a Pod can define a volume that multiple containers mount.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The same lifecycle&lt;/strong&gt; — they're scheduled together, and they die together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How is the shared network namespace actually implemented? Kubernetes silently creates a hidden &lt;strong&gt;"pause" container&lt;/strong&gt; for every Pod. This container does nothing except hold open the network namespace. Every other container in the Pod joins &lt;em&gt;that&lt;/em&gt; namespace instead of creating its own. That's the real implementation detail behind "Pods share networking."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pod (shares one network namespace + IP: 10.244.1.7)
├── pause container      (holds the namespace, does nothing)
├── app container        (localhost:8080)
└── sidecar container    (localhost:9090, e.g. log shipper)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is also why the Pod, not the container, is the atomic unit Kubernetes schedules, scales, and replaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nodes — Where Pods Actually Run
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Node&lt;/strong&gt; is a worker machine (VM or bare metal) running the components needed to host Pods. Every Node runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kubelet&lt;/strong&gt; – the agent that talks to the control plane, receives instructions ("run this Pod"), and tells the container runtime to do it via CRI. It also continuously reports Pod health back up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;container runtime&lt;/strong&gt; – containerd + runc, as covered above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-proxy&lt;/strong&gt; – maintains network rules on the Node (via &lt;code&gt;iptables&lt;/code&gt; or &lt;code&gt;IPVS&lt;/code&gt;) so that traffic sent to a Service IP gets routed to the correct backend Pod, even though the Pod could be on a completely different Node.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of the kubelet as a very obedient local supervisor: the control plane never SSHs into a Node to start something — it just updates a desired state, and kubelet on each Node notices and reconciles reality to match it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Control Plane — The Brain
&lt;/h2&gt;

&lt;p&gt;This is the part that decides &lt;em&gt;what should run where&lt;/em&gt;, and it's separate from the Nodes that actually run your workloads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kube-apiserver&lt;/strong&gt; – the front door. Every single interaction with the cluster — &lt;code&gt;kubectl&lt;/code&gt;, the scheduler, controllers, kubelets — goes through the API server. It validates requests and is the only component that talks directly to storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;etcd&lt;/strong&gt; – a distributed key-value store that holds the entire cluster state (every object, every desired configuration). If etcd is healthy, the cluster's "memory" is safe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-scheduler&lt;/strong&gt; – watches for Pods with no Node assigned yet, and decides which Node they should run on, based on resource availability, affinity rules, taints/tolerations, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-controller-manager&lt;/strong&gt; – runs the reconciliation loops. Example: the ReplicaSet controller constantly checks "do I have 3 Pods running as declared? If not, create more."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The core pattern to internalize:&lt;/strong&gt; you &lt;em&gt;declare&lt;/em&gt; desired state (via YAML → API server → etcd). Controllers continuously &lt;em&gt;watch&lt;/em&gt; for drift between desired state and actual state, and correct it. Kubernetes never "runs a command once" — it enforces a state forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployments
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Deployment&lt;/strong&gt; is a controller that manages &lt;strong&gt;ReplicaSets&lt;/strong&gt;, which in turn manage &lt;strong&gt;Pods&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deployment
   └── ReplicaSet (v1)
           ├── Pod
           ├── Pod
           └── Pod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why the extra ReplicaSet layer instead of Deployment managing Pods directly? &lt;strong&gt;Rolling updates.&lt;/strong&gt; When you update the image in a Deployment, it doesn't touch existing Pods — it creates a &lt;em&gt;new&lt;/em&gt; ReplicaSet with the new Pod template, scales it up gradually, and scales the old ReplicaSet down. This is how you get zero-downtime deploys and easy rollbacks (rolling back = pointing back at the old ReplicaSet).&lt;/p&gt;

&lt;h2&gt;
  
  
  Services — Solving the Pod IP Problem
&lt;/h2&gt;

&lt;p&gt;Pods are mortal. They get killed and recreated constantly (crashes, scaling, node failures) — and every time, they get a &lt;strong&gt;new IP address&lt;/strong&gt;. So how does anything reliably talk to "the backend"?&lt;/p&gt;

&lt;p&gt;This is what a &lt;strong&gt;Service&lt;/strong&gt; solves. A Service gets a &lt;strong&gt;stable virtual IP&lt;/strong&gt; and DNS name, and uses &lt;strong&gt;labels and selectors&lt;/strong&gt; to find which Pods should receive traffic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-backend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any Pod labeled &lt;code&gt;app: my-backend&lt;/code&gt; automatically becomes a backend for this Service — no matter how many times those Pods get replaced. kube-proxy on every Node maintains the routing rules that make "traffic to the Service IP" actually land on one of the healthy matching Pods.&lt;/p&gt;

&lt;p&gt;Three Service types matter most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ClusterIP&lt;/strong&gt; (default) – a virtual IP reachable &lt;em&gt;only from inside the cluster&lt;/em&gt;. Good for internal service-to-service communication (e.g., frontend Pod → backend Service).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NodePort&lt;/strong&gt; – opens a specific port (30000–32767 range) on &lt;em&gt;every&lt;/em&gt; Node's own IP, forwarding it into the Service. Lets external traffic in via &lt;code&gt;&amp;lt;NodeIP&amp;gt;:&amp;lt;NodePort&amp;gt;&lt;/code&gt;, but it's clunky for production (you're exposing raw node IPs and non-standard ports).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoadBalancer&lt;/strong&gt; – asks the cloud provider (AWS/GCP/Azure) to provision an actual external load balancer that points at the Service. This is the standard way to expose something to the internet on a cloud cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Port Forwarding vs Services
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;kubectl port-forward pod/my-pod 8080:80&lt;/code&gt; is a &lt;strong&gt;developer debugging tool&lt;/strong&gt;, not a production traffic mechanism. It opens a temporary tunnel from your local machine straight to a single Pod, bypassing Services and kube-proxy entirely. The moment you kill that terminal, the connection is gone. It's for "let me poke this one Pod directly while debugging" — never how real traffic reaches your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ingress — HTTP Routing at the Edge
&lt;/h2&gt;

&lt;p&gt;If Services (specifically LoadBalancer) already expose things externally, why do we need Ingress too?&lt;/p&gt;

&lt;p&gt;Problem: if you have 10 HTTP microservices, giving each one its own cloud LoadBalancer means 10 expensive external load balancers, and no shared logic for things like TLS termination, host-based routing (&lt;code&gt;api.example.com&lt;/code&gt; vs &lt;code&gt;app.example.com&lt;/code&gt;), or path-based routing (&lt;code&gt;/users&lt;/code&gt; vs &lt;code&gt;/orders&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingress&lt;/strong&gt; is a set of routing rules — "if the request comes in for &lt;code&gt;api.example.com/users&lt;/code&gt;, send it to the &lt;code&gt;users-service&lt;/code&gt; ClusterIP Service." But Ingress &lt;em&gt;rules alone do nothing&lt;/em&gt; — you need an &lt;strong&gt;Ingress Controller&lt;/strong&gt; (NGINX Ingress Controller, Traefik, etc.) running as Pods in your cluster that actually reads those rules and does the routing. The Ingress Controller is usually the one component that does get a single LoadBalancer Service, and everything else flows through it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
   │
   ▼
LoadBalancer Service (1 external IP)
   │
   ▼
Ingress Controller Pods (reads Ingress rules)
   │
   ├── /users  → users-service (ClusterIP)  → users Pods
   ├── /orders → orders-service (ClusterIP) → orders Pods
   └── api.example.com → api-service (ClusterIP) → api Pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting the Whole Request Path Together
&lt;/h2&gt;

&lt;p&gt;Here's the full journey of one HTTP request hitting your cluster, tying every piece above together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Request hits the &lt;strong&gt;cloud LoadBalancer&lt;/strong&gt;, which forwards to the &lt;strong&gt;Ingress Controller Pods&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Ingress Controller reads the &lt;strong&gt;Ingress rules&lt;/strong&gt; and decides which internal &lt;strong&gt;Service&lt;/strong&gt; should handle this path/host.&lt;/li&gt;
&lt;li&gt;It forwards the request to that Service's &lt;strong&gt;ClusterIP&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-proxy&lt;/strong&gt;'s iptables/IPVS rules on the Node intercept traffic to that ClusterIP and redirect it to one of the matching &lt;strong&gt;Pods&lt;/strong&gt;, chosen via the Service's &lt;strong&gt;label selector&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Traffic lands inside the Pod's shared &lt;strong&gt;network namespace&lt;/strong&gt;, reaching the actual container process — a Linux process running inside namespaces and cgroups, started by &lt;strong&gt;runc&lt;/strong&gt;, pulled from an &lt;strong&gt;image&lt;/strong&gt; by &lt;strong&gt;containerd&lt;/strong&gt;, on instructions from the &lt;strong&gt;kubelet&lt;/strong&gt;, which got that instruction from the &lt;strong&gt;API server&lt;/strong&gt;, which persisted it from a &lt;strong&gt;Deployment&lt;/strong&gt; you applied, which is stored durably in &lt;strong&gt;etcd&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the entire stack, front to back, in one sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Every Kubernetes concept exists to solve a concrete problem:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Kubernetes Answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Isolate processes on Linux&lt;/td&gt;
&lt;td&gt;namespaces + cgroups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package an app reproducibly&lt;/td&gt;
&lt;td&gt;container images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Actually run a container&lt;/td&gt;
&lt;td&gt;container runtime (containerd + runc)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group tightly-coupled containers&lt;/td&gt;
&lt;td&gt;Pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run Pods somewhere&lt;/td&gt;
&lt;td&gt;Nodes (kubelet, kube-proxy, runtime)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decide cluster-wide state&lt;/td&gt;
&lt;td&gt;Control plane (API server, etcd, scheduler, controllers)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero-downtime updates &amp;amp; scaling&lt;/td&gt;
&lt;td&gt;Deployments + ReplicaSets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stable networking to mortal Pods&lt;/td&gt;
&lt;td&gt;Services + labels/selectors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smart HTTP routing at the edge&lt;/td&gt;
&lt;td&gt;Ingress + Ingress Controller&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Once you see it this way, Kubernetes stops being "a huge list of YAML kinds to memorize" and becomes "a small number of real problems, each solved by one clean abstraction."&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If this helped clarify things, I'll be following up with a deeper dive into Services and networking internals (ClusterIP vs NodePort vs LoadBalancer under the hood) — let me know in the comments what you'd like covered next.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>kubernetes</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Database Sharding Explained: How to Scale Your Database Like a Pro</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Tue, 07 Jul 2026 07:27:48 +0000</pubDate>
      <link>https://dev.to/m_toqeer/database-sharding-explained-how-to-scale-your-database-like-a-pro-djc</link>
      <guid>https://dev.to/m_toqeer/database-sharding-explained-how-to-scale-your-database-like-a-pro-djc</guid>
      <description>&lt;p&gt;If you've ever worked on an application that grew from a handful of users to thousands—or millions—you've probably hit a point where your database starts groaning under the weight. Queries get slower. CPU spikes. Storage fills up. And no amount of indexing seems to fix it.&lt;/p&gt;

&lt;p&gt;This is usually where &lt;strong&gt;database sharding&lt;/strong&gt; enters the conversation.&lt;/p&gt;

&lt;p&gt;In this article, we'll break down what sharding actually is, why it matters, the most common strategies used in production systems, and how to decide if (and when) your project actually needs it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Database Sharding?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Database sharding&lt;/strong&gt; is a technique for splitting one large database into multiple smaller, independent databases called &lt;strong&gt;shards&lt;/strong&gt;. Each shard holds a portion of the total data, and together, all the shards make up the complete dataset.&lt;/p&gt;

&lt;p&gt;Think of it like a library system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single library holding 10 million books becomes slow to search and hard to manage.&lt;/li&gt;
&lt;li&gt;So instead, the books get split across branches:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Branch A&lt;/strong&gt; → Books A–F&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Branch B&lt;/strong&gt; → Books G–L&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Branch C&lt;/strong&gt; → Books M–R&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Branch D&lt;/strong&gt; → Books S–Z&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each branch only manages a fraction of the total collection, which makes browsing, searching, and organizing dramatically faster. Sharding applies this same idea to databases—except instead of book titles, we're splitting rows of data across servers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Do We Need Sharding?
&lt;/h2&gt;

&lt;p&gt;As an application scales, a single database server eventually runs into hard limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Slow query performance&lt;/li&gt;
&lt;li&gt; High CPU and memory usage&lt;/li&gt;
&lt;li&gt; Storage limitations&lt;/li&gt;
&lt;li&gt; Too many concurrent users/connections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sharding solves these problems by spreading data—and the load that comes with it—across multiple servers instead of forcing one machine to do all the work.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical Example
&lt;/h2&gt;

&lt;p&gt;Imagine you're running a social media platform with &lt;strong&gt;100 million users&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without sharding&lt;/strong&gt;, everything lives on one server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB Server 1
------------
User 1
User 2
User 3
...
User 100,000,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every read, write, and query hits this same machine. Eventually, it becomes a bottleneck no matter how much you optimize.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With sharding&lt;/strong&gt;, the data is distributed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shard 1 → Users 1 – 25M
Shard 2 → Users 25M – 50M
Shard 3 → Users 50M – 75M
Shard 4 → Users 75M – 100M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now four servers share the load instead of one. Each shard only needs to handle a quarter of the traffic and storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Sharding Strategies
&lt;/h2&gt;

&lt;p&gt;There's no one-size-fits-all approach to sharding. Here are the three strategies you'll encounter most often.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Range-Based Sharding
&lt;/h3&gt;

&lt;p&gt;Data is split according to a defined range of values (like ID ranges).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shard 1 → User IDs 1–1000
Shard 2 → User IDs 1001–2000
Shard 3 → User IDs 2001–3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Query example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application already knows this record lives in &lt;strong&gt;Shard 2&lt;/strong&gt;, so it routes the query there directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple to understand and implement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uneven growth can cause "hot" shards—one shard may end up much larger or busier than the others&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Hash-Based Sharding
&lt;/h3&gt;

&lt;p&gt;A hash function decides which shard a piece of data belongs to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shard = userId % 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 % 3 = 1 → Shard 1
2 % 3 = 2 → Shard 2
3 % 3 = 0 → Shard 3
4 % 3 = 1 → Shard 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Distributes data evenly across shards, avoiding hotspots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding or removing shards later is difficult, since it changes the hash math and can require re-shuffling large amounts of data (this is where consistent hashing usually comes in to soften the blow)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Geographic Sharding
&lt;/h3&gt;

&lt;p&gt;Data is partitioned based on the user's region or location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shard Pakistan → Pakistani users
Shard USA      → American users
Shard Europe   → European users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is common in global applications, since it also helps reduce latency (users are served from a shard closer to them) and can assist with data residency/compliance requirements.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Does the Application Know Which Shard to Use?
&lt;/h2&gt;

&lt;p&gt;This is handled through a &lt;strong&gt;shard key&lt;/strong&gt;—a value used to determine where a specific piece of data lives.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the shard is calculated, the application routes the query to the correct database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shard&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DB1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shard&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DB2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ...and so on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In real-world systems, this routing logic is often abstracted into a middleware layer or handled by the database driver/proxy itself, so individual services don't need to reimplement it everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-Life Example: E-Commerce Platform
&lt;/h2&gt;

&lt;p&gt;Let's say you're building an e-commerce platform and want to shard your &lt;code&gt;users&lt;/code&gt; table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User ID
--------
1
2
3
4
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sharding strategy:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shard 1 → User IDs ending in 0–4
Shard 2 → User IDs ending in 5–9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;strong&gt;User 1234&lt;/strong&gt; logs in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1234 % 2 = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request is routed to &lt;strong&gt;Shard 1&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sharding vs. Replication: What's the Difference?
&lt;/h2&gt;

&lt;p&gt;People often confuse sharding with replication, but they solve different problems.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Sharding&lt;/th&gt;
&lt;th&gt;Replication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Splits data across servers&lt;/td&gt;
&lt;td&gt;Copies the same data to multiple servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Used for scaling &lt;strong&gt;writes&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Used for scaling &lt;strong&gt;reads&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Each shard holds different data&lt;/td&gt;
&lt;td&gt;Every replica holds the same data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Increases storage capacity&lt;/td&gt;
&lt;td&gt;Improves availability &amp;amp; redundancy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Sharding example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB1 → Users 1–1000
DB2 → Users 1001–2000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replication example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      Primary DB
          |
   +------+------+
   |             |
Replica 1     Replica 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In many production systems, these two techniques are actually combined—each shard has its own set of replicas for both scalability &lt;em&gt;and&lt;/em&gt; availability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Interview-Ready Summary
&lt;/h2&gt;

&lt;p&gt;If you're prepping for a system design interview, here's a concise way to explain it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Database sharding is a horizontal scaling technique where data is partitioned across multiple database servers called shards. Each shard contains a subset of the data and handles part of the application's workload. Sharding improves scalability, performance, and storage capacity for large-scale systems. Common strategies include range-based, hash-based, and geographic sharding.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  When Should You Actually Use Sharding?
&lt;/h2&gt;

&lt;p&gt;Sharding is powerful, but it's not something you should reach for on day one. It adds real architectural complexity—cross-shard queries, distributed transactions, and rebalancing all become harder problems once you shard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider sharding when:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your database size is becoming too large for a single server&lt;br&gt;
 Write traffic is very high&lt;br&gt;
 Queries are slowing down despite indexing and optimization&lt;br&gt;
 Vertical scaling (more CPU/RAM) is no longer enough&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before reaching for sharding, try:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query optimization&lt;/li&gt;
&lt;li&gt;Better indexing&lt;/li&gt;
&lt;li&gt;Caching layers (Redis, Memcached, etc.)&lt;/li&gt;
&lt;li&gt;Read replicas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sharding is typically introduced once an application reaches a large scale—think millions of users or datasets that no longer fit comfortably on one machine. Until then, simpler scaling techniques usually get you further with far less complexity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Sharding is one of those concepts that sounds intimidating until you break it down—at its core, it's just "divide the data so no single server has to do all the work." The real challenge isn't understanding the concept, it's choosing the right shard key and strategy for your specific access patterns, and being ready for the operational complexity that comes with a distributed database layer.&lt;/p&gt;

&lt;p&gt;If you're designing for scale, start simple, measure where your actual bottlenecks are, and only shard when you have clear evidence that you need to.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, feel free to follow for more system design breakdowns!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>database</category>
      <category>distributedsystems</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>JavaScript Under the Hood: Scope, Closures, Prototypes &amp;</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Mon, 29 Jun 2026 18:02:21 +0000</pubDate>
      <link>https://dev.to/m_toqeer/javascript-under-the-hood-scope-closures-prototypes--48o3</link>
      <guid>https://dev.to/m_toqeer/javascript-under-the-hood-scope-closures-prototypes--48o3</guid>
      <description>&lt;p&gt;&lt;em&gt;A deep dive into the concepts that separate junior devs from engineers who actually understand the runtime.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Scope and Closures
&lt;/h2&gt;

&lt;h3&gt;
  
  
  var vs let vs const — It's Not Just Syntax
&lt;/h3&gt;

&lt;p&gt;Most devs learn early that &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; are "the modern way" and &lt;code&gt;var&lt;/code&gt; is old. But the real difference runs deeper than style.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hoisting
&lt;/h4&gt;

&lt;p&gt;JavaScript hoists declarations to the top of their scope before execution. But how that hoisting behaves depends on which keyword you use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// undefined (not an error!)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toqeer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ReferenceError: Cannot access 'age' before initialization&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;var&lt;/code&gt; is hoisted &lt;strong&gt;and initialized&lt;/strong&gt; to &lt;code&gt;undefined&lt;/code&gt;. So the engine sees this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// hoisted to top&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// undefined&lt;/span&gt;
&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toqeer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; are also hoisted — but they are &lt;strong&gt;not initialized&lt;/strong&gt;. They exist in what's called the &lt;strong&gt;Temporal Dead Zone (TDZ)&lt;/strong&gt; from the start of the block until the declaration is reached.&lt;/p&gt;

&lt;h4&gt;
  
  
  Temporal Dead Zone (TDZ)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// TDZ for `score` starts here&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ReferenceError&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// TDZ ends here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variable exists in the scope, the engine knows about it — but you cannot touch it yet. This is intentional. It prevents you from accidentally using a variable before it's ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;const&lt;/code&gt;&lt;/strong&gt; adds one more layer: once assigned, the binding cannot be reassigned. For objects and arrays, the reference is locked — but the contents can still mutate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// fine&lt;/span&gt;
&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;         &lt;span class="c1"&gt;// TypeError: Assignment to constant variable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Scope: Function vs Block
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;var&lt;/code&gt; is &lt;strong&gt;function-scoped&lt;/strong&gt;. It leaks out of blocks like &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "active" — leaked out of the if block&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; are &lt;strong&gt;block-scoped&lt;/strong&gt;. They stay inside &lt;code&gt;{}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ReferenceError&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why the classic loop bug exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Prints: 3, 3, 3&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Prints: 0, 1, 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;var&lt;/code&gt;, there's only one &lt;code&gt;i&lt;/code&gt; shared across all iterations. With &lt;code&gt;let&lt;/code&gt;, each iteration gets its own block-scoped &lt;code&gt;i&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Closures: One of JavaScript's Superpowers
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;closure&lt;/strong&gt; is the combination of a function and the lexical environment in which it was declared. That environment stays alive and accessible to the function — even after the outer scope that created it has finished executing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeCounter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 2&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;makeCounter&lt;/code&gt; has long since returned. Its execution context is gone. But &lt;code&gt;count&lt;/code&gt; lives on — because the inner function &lt;strong&gt;closed over&lt;/strong&gt; it.&lt;/p&gt;

&lt;h4&gt;
  
  
  How Closures Form
&lt;/h4&gt;

&lt;p&gt;Every function has an associated closure, created automatically at the moment the function itself is created. This happens for every function — not just ones nested inside another function — and whether or not the function body ever references a variable from an outer scope. Nesting a function inside another simply makes the closure easy to observe, because the inner function can then reach variables in the outer function's scope that would otherwise be gone once the outer function returns. The function holds a &lt;strong&gt;reference&lt;/strong&gt; to its lexical environment, not a copy of the variables in it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Practical Uses
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;1. Data privacy / encapsulation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createBankAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialBalance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initialBalance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBankAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 1500&lt;/span&gt;
&lt;span class="c1"&gt;// `balance` is not accessible from outside&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Function factories:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;multiplier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;multiplier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;triple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;multiplier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 10&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;triple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Memoization:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factorial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  IIFE — Immediately Invoked Function Expression
&lt;/h3&gt;

&lt;p&gt;An IIFE is a function that runs the moment it's defined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Runs immediately&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;

&lt;span class="c1"&gt;// `secret` is not accessible out here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The outer &lt;code&gt;()&lt;/code&gt; wraps the function expression (makes it an expression, not a declaration), and the trailing &lt;code&gt;()&lt;/code&gt; invokes it immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before ES modules and block scoping with &lt;code&gt;let&lt;/code&gt;/&lt;code&gt;const&lt;/code&gt;, IIFEs were the primary way to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a private scope and avoid polluting the global namespace&lt;/li&gt;
&lt;li&gt;Initialize libraries and plugins&lt;/li&gt;
&lt;li&gt;Wrap module code
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;_privateState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;_privateState&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;_privateState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today, ES modules handle this naturally. But IIFEs still appear in bundled output and are worth understanding.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: Prototypes and Inheritance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Prototype Chain
&lt;/h3&gt;

&lt;p&gt;JavaScript is a &lt;strong&gt;prototype-based&lt;/strong&gt; language. Every object has an internal link — &lt;code&gt;[[Prototype]]&lt;/code&gt; — that points to another object. When you access a property, JavaScript first looks on the object itself, then walks up the chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;breathe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;breathing...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;woof!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bark&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;    &lt;span class="c1"&gt;// "woof!" — found on dog&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;breathe&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// "breathing..." — found on animal (via prototype)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// found on Object.prototype&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chain: &lt;code&gt;dog&lt;/code&gt; → &lt;code&gt;animal&lt;/code&gt; → &lt;code&gt;Object.prototype&lt;/code&gt; → &lt;code&gt;null&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When the end (&lt;code&gt;null&lt;/code&gt;) is reached without finding the property, &lt;code&gt;undefined&lt;/code&gt; is returned (or a &lt;code&gt;TypeError&lt;/code&gt; for method calls).&lt;/p&gt;

&lt;h3&gt;
  
  
  Object.create and Object.getPrototypeOf
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Object.create(proto)&lt;/code&gt; creates a new object whose prototype is &lt;code&gt;proto&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vehicleProto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`I am a &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wheels&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; wheels`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vehicleProto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;car&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// "I am a car with 4 wheels"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Object.getPrototypeOf(obj)&lt;/code&gt; lets you inspect the chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPrototypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;vehicleProto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Constructor Functions
&lt;/h3&gt;

&lt;p&gt;Before ES6 classes, constructor functions were the way to create objects with shared behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hi, I'm &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toqeer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toqeer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toqeer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// "Hi, I'm Toqeer"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;greet&lt;/code&gt; lives on &lt;code&gt;Person.prototype&lt;/code&gt;, not on each instance — so all instances share it without duplication.&lt;/p&gt;




&lt;h3&gt;
  
  
  The &lt;code&gt;new&lt;/code&gt; Keyword — What Actually Happens
&lt;/h3&gt;

&lt;p&gt;When you call a function with &lt;code&gt;new&lt;/code&gt;, four things happen under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. A new empty object is created: {}
2. Its [[Prototype]] is linked to Constructor.prototype
3. `this` inside the function points to that new object
4. The function returns `this` (the new object) unless it explicitly returns another object
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see this manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myNew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Constructor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// step 1 &amp;amp; 2&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// step 3&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// step 4&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;myNew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toqeer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// "Hi, I'm Toqeer"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is exactly what &lt;code&gt;new&lt;/code&gt; does — now it's not magic.&lt;/p&gt;




&lt;h3&gt;
  
  
  ES6 Classes — Syntactic Sugar
&lt;/h3&gt;

&lt;p&gt;Classes in JavaScript are not a new object model. They're a cleaner syntax over the same prototype mechanism.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; makes a sound.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; barks.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// "Rex barks."&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Animal&lt;/code&gt; is a constructor function&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;speak&lt;/code&gt; is added to &lt;code&gt;Animal.prototype&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;extends&lt;/code&gt; sets up the prototype chain: &lt;code&gt;Dog.prototype → Animal.prototype&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;super()&lt;/code&gt; calls the parent constructor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output is identical to doing it manually with constructor functions and &lt;code&gt;Object.create&lt;/code&gt;. Classes just make the intent clearer.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Differences from Constructor Functions
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Constructor Function&lt;/th&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hoisted&lt;/td&gt;
&lt;td&gt;Yes (as &lt;code&gt;undefined&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No (TDZ applies)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strict mode&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Always strict&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Callable without &lt;code&gt;new&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes (bad idea)&lt;/td&gt;
&lt;td&gt;TypeError&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inherited methods&lt;/td&gt;
&lt;td&gt;Manual prototype chain&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;extends&lt;/code&gt; keyword&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;super&lt;/code&gt; call&lt;/td&gt;
&lt;td&gt;Manual with &lt;code&gt;.call&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Putting It All Together
&lt;/h3&gt;

&lt;p&gt;Here's everything combined — a real-world-ish example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventEmitter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// for chaining&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Store&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;EventEmitter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;State changed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// "State changed: { count: 1 }"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Private class fields (&lt;code&gt;#listeners&lt;/code&gt;, &lt;code&gt;#state&lt;/code&gt;) for encapsulation — closure-like privacy at the class level&lt;/li&gt;
&lt;li&gt;Prototype inheritance via &lt;code&gt;extends&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;super()&lt;/code&gt; to call the parent constructor&lt;/li&gt;
&lt;li&gt;Method chaining via &lt;code&gt;return this&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scoping Rules
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;var&lt;/code&gt; → function-scoped, hoisted and initialized to &lt;code&gt;undefined&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;let&lt;/code&gt; / &lt;code&gt;const&lt;/code&gt; → block-scoped, hoisted but in TDZ until declaration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const&lt;/code&gt; → block-scoped + immutable binding (contents can mutate)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Closures
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Every function has a closure, formed automatically at creation time — nesting and referencing outer variables aren't required, they just make the closure observable&lt;/li&gt;
&lt;li&gt;The function holds a reference to its lexical environment, not a copy&lt;/li&gt;
&lt;li&gt;Useful for: encapsulation, factories, memoization, event handlers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  IIFE
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Runs immediately, creates isolated scope&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(function() { ... })()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Less common today due to ES modules, but still found in bundled code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prototype Chain
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Every object has a &lt;code&gt;[[Prototype]]&lt;/code&gt; link&lt;/li&gt;
&lt;li&gt;Property lookup walks the chain until &lt;code&gt;null&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Object.create(proto)&lt;/code&gt; sets the prototype explicitly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;new&lt;/code&gt; Keyword Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create empty object&lt;/li&gt;
&lt;li&gt;Link prototype to &lt;code&gt;Constructor.prototype&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Execute constructor with &lt;code&gt;this&lt;/code&gt; = new object&lt;/li&gt;
&lt;li&gt;Return the new object (unless constructor returns an object)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Classes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Syntactic sugar over prototypes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;extends&lt;/code&gt; wires prototype chain&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;super()&lt;/code&gt; calls parent constructor&lt;/li&gt;
&lt;li&gt;Always strict, not hoistable&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Understanding these fundamentals will make you a better debugger, a better code reviewer, and a more confident engineer — whether you're writing Node.js APIs, React apps, or contributing to open source.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Drop a comment if you want me to go deeper on any of these. Happy to cover the event loop, async/await internals, or WeakMaps and memory management next.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fine-Tuning Large Language Models: A Practical Guide</title>
      <dc:creator>M TOQEER ZIA</dc:creator>
      <pubDate>Tue, 16 Jun 2026 08:59:56 +0000</pubDate>
      <link>https://dev.to/m_toqeer/fine-tuning-large-language-models-a-practical-guide-7b9</link>
      <guid>https://dev.to/m_toqeer/fine-tuning-large-language-models-a-practical-guide-7b9</guid>
      <description>&lt;h2&gt;
  
  
  What Is Fine-Tuning?
&lt;/h2&gt;

&lt;p&gt;A pre-trained model learns from billions of tokens of general text. It develops broad language understanding. Fine-tuning takes that base model and trains it further on a smaller, task-specific dataset.&lt;/p&gt;

&lt;p&gt;The result: a model that performs well on your specific use case, whether that is medical diagnosis, legal summarization, customer support, or code generation.&lt;/p&gt;

&lt;p&gt;Fine-tuning is not training from scratch. You preserve general knowledge and adapt behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  How a Neural Network Learns
&lt;/h2&gt;

&lt;p&gt;Before fine-tuning makes sense, you need to understand what happens inside a neural network during training.&lt;/p&gt;

&lt;p&gt;A neural network is a system of layers. Each layer contains nodes (neurons). Each connection between nodes has a weight. These weights determine what the network outputs given any input.&lt;/p&gt;

&lt;p&gt;At the start, weights are random. Training adjusts them so the network produces correct outputs.&lt;/p&gt;

&lt;p&gt;Here is the full training loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Training Data
      ↓
Forward Pass
      ↓
Prediction
      ↓
Loss Calculation
      ↓
Backpropagation
      ↓
Gradient Calculation
      ↓
Weight Update
      ↓
Repeat
      ↓
Trained Model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Phase 1: Forward Pass
&lt;/h3&gt;

&lt;p&gt;The model receives an input (a sentence, an image, a token). Data flows forward through every layer. Each neuron applies a mathematical function and passes its result to the next layer.&lt;/p&gt;

&lt;p&gt;At the end, the model produces a prediction. For a language model, this is a probability distribution over the next token.&lt;/p&gt;

&lt;p&gt;No learning happens here. This phase only produces output.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 2: Loss Calculation
&lt;/h3&gt;

&lt;p&gt;The model's prediction is compared to the correct answer. A loss function measures how wrong the prediction is.&lt;/p&gt;

&lt;p&gt;Common loss functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-entropy loss: used for classification and language modeling&lt;/li&gt;
&lt;li&gt;Mean squared error: used for regression tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A high loss means the model is far off. A loss near zero means the prediction was close to correct.&lt;/p&gt;

&lt;p&gt;The loss is a single number. It summarizes the error for one batch of data.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 3: Backpropagation
&lt;/h3&gt;

&lt;p&gt;This is where learning begins.&lt;/p&gt;

&lt;p&gt;Backpropagation works backward through the network. It calculates how much each weight contributed to the total loss. The math tool used here is the chain rule from calculus.&lt;/p&gt;

&lt;p&gt;Think of it as blame assignment. Each weight receives a gradient: a number that says "changing this weight by X changes the loss by Y."&lt;/p&gt;

&lt;p&gt;A large gradient means that weight had a big effect on the error. A small gradient means it had little effect.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 4: Gradient Calculation
&lt;/h3&gt;

&lt;p&gt;After backpropagation, every weight in the network has a gradient.&lt;/p&gt;

&lt;p&gt;The gradient is a vector. It points in the direction of steepest increase in loss. To reduce the loss, you move in the opposite direction.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 5: Weight Update (Optimization)
&lt;/h3&gt;

&lt;p&gt;An optimizer uses the gradients to update every weight.&lt;/p&gt;

&lt;p&gt;The most basic optimizer is Stochastic Gradient Descent (SGD):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;new_weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;old_weight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="n"&gt;gradient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The learning rate controls the step size. Too large: the model overshoots. Too small: training takes too long.&lt;/p&gt;

&lt;p&gt;Modern optimizers like Adam adjust the learning rate automatically for each weight. Adam tracks the history of gradients and adapts accordingly. It converges faster than basic SGD in most cases.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Full Loop
&lt;/h3&gt;

&lt;p&gt;One pass through the data is one epoch. Training repeats this loop thousands of times across many epochs. With each pass, the weights improve. The loss decreases. The model gets better at the task.&lt;/p&gt;




&lt;h2&gt;
  
  
  Types of Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;There are three main approaches. They differ in what gets updated during training.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Full Fine-Tuning
&lt;/h3&gt;

&lt;p&gt;All weights in the model are updated.&lt;/p&gt;

&lt;p&gt;You take the pre-trained model and run the training loop on your dataset. Every parameter is fair game. The optimizer adjusts all of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maximum flexibility&lt;/li&gt;
&lt;li&gt;Best performance ceiling for large datasets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires significant GPU memory (storing the model, gradients, and optimizer states for billions of parameters)&lt;/li&gt;
&lt;li&gt;Expensive and slow&lt;/li&gt;
&lt;li&gt;Risk of catastrophic forgetting: the model overwrites general knowledge with task-specific patterns&lt;/li&gt;
&lt;li&gt;A 7B parameter model at full precision requires roughly 28 GB of GPU memory for weights alone, plus optimizer states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full fine-tuning makes sense when you have large datasets and substantial compute.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. PEFT (Parameter-Efficient Fine-Tuning)
&lt;/h3&gt;

&lt;p&gt;PEFT is a family of methods. The core idea: freeze most of the model and only train a small number of parameters.&lt;/p&gt;

&lt;p&gt;You add new, trainable components to the frozen model. Only those new components are updated. The original weights stay fixed.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;GPU memory required (no gradients for frozen weights)&lt;/li&gt;
&lt;li&gt;Training time&lt;/li&gt;
&lt;li&gt;Risk of catastrophic forgetting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PEFT methods include LoRA, prefix tuning, prompt tuning, and adapter layers. LoRA is the most widely used.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. LoRA (Low-Rank Adaptation)
&lt;/h3&gt;

&lt;p&gt;LoRA is the dominant PEFT method in 2024 and 2025.&lt;/p&gt;

&lt;p&gt;The insight behind LoRA: weight updates during fine-tuning tend to have low intrinsic rank. You do not need to update a full weight matrix. You approximate the update with two much smaller matrices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a weight matrix W of size (m × n), instead of updating W directly, LoRA adds two matrices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Matrix A: size (m × r)&lt;/li&gt;
&lt;li&gt;Matrix B: size (r × n)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where r is the rank, typically 4, 8, 16, or 64. You only train A and B. The product AB approximates the full weight update.&lt;/p&gt;

&lt;p&gt;During inference, the update is merged back: W_new = W + AB&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter count comparison:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A weight matrix of size 4096 × 4096 has 16,777,216 parameters.&lt;br&gt;
LoRA with rank 8 adds two matrices: (4096 × 8) + (8 × 4096) = 65,536 parameters.&lt;br&gt;
That is a 256x reduction in trainable parameters for that layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trains on consumer GPUs (a 7B model with LoRA fits on 8-16 GB VRAM)&lt;/li&gt;
&lt;li&gt;Multiple LoRA adapters for different tasks, swapped at inference time&lt;/li&gt;
&lt;li&gt;The base model stays frozen and reusable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slightly below full fine-tuning on performance in some benchmarks&lt;/li&gt;
&lt;li&gt;Requires choosing rank (r) and which layers to target&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. QLoRA (Quantized LoRA)
&lt;/h3&gt;

&lt;p&gt;QLoRA combines quantization with LoRA. It pushes LoRA further in terms of memory efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What quantization does:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Standard model weights use 16-bit or 32-bit floating point numbers. Quantization converts them to lower precision: 8-bit, 4-bit, or even 3-bit integers.&lt;/p&gt;

&lt;p&gt;A 7B model in 16-bit uses roughly 14 GB of memory. The same model in 4-bit uses roughly 3.5 GB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;QLoRA workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load the base model in 4-bit precision (NF4 format, designed for neural network weights)&lt;/li&gt;
&lt;li&gt;Freeze all quantized weights&lt;/li&gt;
&lt;li&gt;Add LoRA adapters in 16-bit precision&lt;/li&gt;
&lt;li&gt;Train only the LoRA adapters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The gradients flow through the quantized model, through the LoRA adapters. Only the adapters are updated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Double quantization:&lt;/strong&gt; QLoRA applies a second round of quantization to the quantization constants themselves, saving an additional 0.5 GB on a 7B model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-tune a 70B model on a single 48 GB GPU&lt;/li&gt;
&lt;li&gt;Fine-tune a 7B model on a single 8 GB consumer GPU&lt;/li&gt;
&lt;li&gt;Performance within 1-2% of full fine-tuning on most benchmarks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slower than LoRA (dequantization overhead during forward/backward pass)&lt;/li&gt;
&lt;li&gt;Quantization introduces approximation errors&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Trainable Params&lt;/th&gt;
&lt;th&gt;VRAM (7B model)&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full Fine-Tuning&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;80+ GB&lt;/td&gt;
&lt;td&gt;Highest&lt;/td&gt;
&lt;td&gt;Large datasets, max compute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PEFT (general)&lt;/td&gt;
&lt;td&gt;0.1 - 1%&lt;/td&gt;
&lt;td&gt;Varies&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Resource-constrained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA&lt;/td&gt;
&lt;td&gt;0.1 - 1%&lt;/td&gt;
&lt;td&gt;16-24 GB&lt;/td&gt;
&lt;td&gt;Near full&lt;/td&gt;
&lt;td&gt;Most fine-tuning tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;0.1 - 1%&lt;/td&gt;
&lt;td&gt;6-10 GB&lt;/td&gt;
&lt;td&gt;Near LoRA&lt;/td&gt;
&lt;td&gt;Consumer GPU fine-tuning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Choosing the Right Method
&lt;/h2&gt;

&lt;p&gt;Start with QLoRA if you are on a single GPU with less than 24 GB VRAM. It lets you work with models up to 13B or 30B parameters.&lt;/p&gt;

&lt;p&gt;Use LoRA if you have 24-40 GB VRAM and want faster training than QLoRA.&lt;/p&gt;

&lt;p&gt;Use full fine-tuning if you have a multi-GPU setup, a large high-quality dataset (100k+ samples), and need maximum task performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Hyperparameters in Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Learning rate:&lt;/strong&gt; Typically much lower for fine-tuning than pre-training. Common range: 1e-5 to 3e-4. Too high and you destroy pre-trained representations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rank (r) in LoRA:&lt;/strong&gt; Higher rank captures more complex updates but uses more memory. Start with 8 or 16.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LoRA alpha:&lt;/strong&gt; Scales the LoRA update. Common setting: alpha = 2 × rank.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Epochs:&lt;/strong&gt; Fine-tuning usually needs far fewer epochs than pre-training. 1 to 5 epochs on domain-specific data is typical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Batch size:&lt;/strong&gt; Larger batches give smoother gradients. Use gradient accumulation if your GPU limits batch size.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes Good Fine-Tuning Data
&lt;/h2&gt;

&lt;p&gt;Data quality matters more than quantity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1,000 high-quality examples often outperform 100,000 noisy ones&lt;/li&gt;
&lt;li&gt;Format your data to match the task: instruction-response pairs for chat models, completions for base models&lt;/li&gt;
&lt;li&gt;Balance your dataset across categories to avoid the model over-indexing on one topic&lt;/li&gt;
&lt;li&gt;Remove duplicates; they waste training compute and bias the model&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Neural networks learn by repeating a loop: forward pass, loss calculation, backpropagation, gradient calculation, weight update. Fine-tuning runs this loop on task-specific data starting from a pre-trained model.&lt;/p&gt;

&lt;p&gt;Full fine-tuning updates everything. PEFT methods freeze most weights. LoRA approximates weight updates with low-rank matrices. QLoRA adds quantization to push memory requirements lower.&lt;/p&gt;

&lt;p&gt;Your choice depends on your GPU, your dataset size, and your performance target.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>machinelearning</category>
      <category>nlp</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
