How WhatsApp Notifications Work Internally — A System Design Perspective

Have you ever wondered how WhatsApp notifies you instantly when someone sends a message — even when your app is closed? Let’s peel back the layers and look at how WhatsApp’s notification system actually works from a system design point of view.


🧠 The Big Picture

When you receive a new WhatsApp message like

“Ravi: Hey Raju!”

that notification travels through a complex, highly optimized system involving encryption, real-time messaging, and push infrastructure.

Let’s break it down step by step 👇


⚙️ 1. Message Creation — The Journey Begins

When Ravi sends a message to Raju:

  • The WhatsApp client on Ravi’s phone encrypts the message using end-to-end encryption (E2EE).
  • The encrypted payload is sent to WhatsApp’s Message Server through a persistent socket connection.

At this point, the message is unreadable to anyone — even WhatsApp itself.


📡 2. Message Routing — Finding the Recipient

The Message Server receives Ravi’s encrypted message and determines that it needs to reach Raju’s device.

  • If Raju is online, the message is sent immediately via a persistent connection (using XMPP or WebSockets).
  • If Raju is offline, WhatsApp stores the encrypted message temporarily in its Storage Service until Raju reconnects.

📬 3. Triggering a Notification — When the App Is Closed

If Raju’s app is in the background or not connected, WhatsApp triggers a push notification.

Here’s how it works:

  1. The Notification Service in WhatsApp’s backend detects that Raju is offline.
  2. It prepares a lightweight message payload — something like: { "to": "<Raju_Device_Token>", "notification": { "title": "WhatsApp", "body": "Ravi: Hey Raju!" }, "data": { "message_id": "abc123", "chat_id": "ravi_123" } }
  3. It sends this payload to Firebase Cloud Messaging (FCM) for Android or Apple Push Notification Service (APNS) for iPhone.

☁️ 4. Push Infrastructure — Google & Apple Step In

Once WhatsApp sends the notification request:

  • FCM/APNS looks up the device token (a unique ID representing Raju’s phone).
  • They route the message through their global notification delivery networks.
  • Raju’s phone receives it instantly — even if the app isn’t open.

This is possible because FCM and APNS maintain their own persistent channels with your device at the OS level.


🔔 5. Notification Delivery on Device

When Raju’s phone receives the notification:

  • The OS wakes up WhatsApp’s background receiver.
  • A notification appears on the lock screen or notification tray: “Ravi: Hey Raju!”

When Raju taps it:

  • WhatsApp opens and establishes its secure socket connection to the server.
  • The actual encrypted message is fetched and decrypted locally using Raju’s private key.

At this point, the two ticks ✅ (message delivered) appear on Ravi’s chat screen.
When Raju reads it, WhatsApp sends a “read receipt” (blue ticks 💙) back.


🔐 6. End-to-End Encryption (E2EE)

One of WhatsApp’s strongest features is end-to-end encryption.

  • Messages are encrypted on Ravi’s device.
  • Stored in encrypted form on WhatsApp’s server (if needed).
  • Decrypted only on Raju’s device.

Even WhatsApp’s servers can’t see the content — they only know that a message exists and who it’s meant for.


🧩 7. Behind-the-Scenes Components

Here’s what’s happening under the hood:

ComponentRole
Message ServiceHandles message routing between users
Notification ServiceSends push notifications via FCM/APNS
Encryption ServiceEncrypts and decrypts message payloads
Storage ServiceStores encrypted messages for offline users
Delivery TrackerTracks sent, delivered, and read states
User Presence ServiceDetermines whether users are online or offline

🧱 8. System Architecture Overview

Conceptually, it looks like this:

Ravi's Phone ──►(Encrypt Message) ──► Message Server ──► (Route + Store ) ──► Notification Service ──►(Push Notification) ──► FCM/APNS ──► Raju's Phone


⚡ 9. Why This Design Works So Well

GoalHow WhatsApp Achieves It
Real-time deliveryPersistent sockets (XMPP/WebSockets)
Offline supportStored encrypted messages + push
SecurityEnd-to-end encryption keys per user
ScalabilityMicroservices + distributed queues
Low latencyGlobal servers + efficient routing
Battery efficiencyOS-managed push (via FCM/APNS)

🚀 10. Key Takeaways for System Design Interviews

If you’re designing a notification system like WhatsApp’s, remember these principles:

  1. Decouple message delivery and notification logic (use queues).
  2. Use push services (FCM/APNS) for offline or background delivery.
  3. Maintain persistent connections for real-time chat.
  4. Ensure reliability with retry, message persistence, and acknowledgment mechanisms.
  5. Design for privacy with end-to-end encryption and minimal metadata storage.

🏁 Final Thoughts

WhatsApp’s notification system is a perfect blend of real-time communication, security, and scalability.
As a system architect, understanding how these layers interact — from event queues to encryption — is crucial for designing any large-scale, user-facing application.


How to Generate a Release APK Using Gradle (Step by Step) – React Native

When you build an Android app (native or React Native), you often need a Release APK — the file you can share with users or upload to the Play Store.
Let’s understand how this works with just two commands.


1️⃣ Move to the Android Folder

cd android

👉 Why?
Every Android/React Native project has an android folder. Inside it, you’ll find the Gradle build system files.
Gradle is the tool that actually builds your Android app.
So before we run Gradle commands, we must go inside the android directory.


2️⃣ Build the Release APK

./gradlew assembleRelease     # for Mac/Linux
gradlew assembleRelease       # for Windows

👉 What this does:

  • Runs the Gradle build system in release mode.
  • It compiles your Java/Kotlin code, bundles resources (images, layouts, etc.), and optimizes everything.
  • The result is an APK file that you can install on a device or publish.

3️⃣ Where to Find the APK

After the command finishes, you’ll see this path:

android/app/build/outputs/apk/release/app-release.apk

👉 Meaning:

  • android/ → the Android project folder
  • app/ → your app module
  • build/outputs/apk/release/ → Gradle puts the generated APKs here
  • app-release.apk → your final APK file

📦 Debug vs Release APK

  • Debug APK:
  • Release APK:
    • Built using assembleRelease.
    • Signed with your own keystore (for Play Store upload).
    • Optimized (smaller & faster).

🎯 Purpose of These Commands

  • cd android → Go inside the Android project where Gradle is located.
  • ./gradlew assembleRelease → Tell Gradle to build a release-ready APK.
  • android/app/build/outputs/apk/release/app-release.apk → Location of your generated APK.

With this APK:
✅ You can test the final production build on a real device.
✅ You can upload it to the Google Play Store after signing it with your keystore.


🎓 Final Takeaway

  • cd android → enter Android folder
  • ./gradlew assembleRelease → build release APK
  • app-release.apk → your final app file, ready to be shared or published

👉 Think of it like baking a cake:

  • cd android = entering the kitchen
  • ./gradlew assembleRelease = starting the baking process
  • app-release.apk = the finished cake 🎂

Controlled vs Uncontrolled Components in React (Beginner Friendly)

When you start learning React, one of the first confusing topics you’ll face is the difference between Controlled and Uncontrolled Components. Don’t worry — by the end of this post, you’ll understand this clearly with simple examples.


🎯 What Does It Mean?

In simple words:

  • Controlled Component → React is in charge of the input’s value.
  • Uncontrolled Component → The browser (DOM) is in charge of the input’s value.

Think of it like this:

  • In Controlled Components, React acts like a teacher, checking every word you write in your notebook.
  • In Uncontrolled Components, React acts like a teacher who lets you write freely and only checks your notebook at the end.

📝 Controlled Components

In a controlled component, the form input is linked to React state. That means React always knows what’s inside the input, and it updates automatically when you type.

Example: Controlled Input

import React, { useState } from "react";

function ControlledForm() {
  const [name, setName] = useState(""); // React state

  const handleSubmit = (e) => {
    e.preventDefault();
    alert("Submitted Name: " + name);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name} // value comes from React state
        onChange={(e) => setName(e.target.value)} // update state
        placeholder="Enter your name"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

export default ControlledForm;

👉 Here:

  • The input value is always controlled by React.
  • When you type, React updates the state and then shows the updated value back in the input.
  • React is the single source of truth.

📝 Uncontrolled Components

In an uncontrolled component, the input manages its own value just like in plain HTML. You don’t use state, instead you use a ref to get the value when needed.

Example: Uncontrolled Input

import React, { useRef } from "react";

function UncontrolledForm() {
  const nameRef = useRef(); // reference to input

  const handleSubmit = (e) => {
    e.preventDefault();
    alert("Submitted Name: " + nameRef.current.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        ref={nameRef} // React does not control this input
        placeholder="Enter your name"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

export default UncontrolledForm;

👉 Here:

  • The browser keeps track of what you type.
  • React doesn’t know the input value until you read it with ref.
  • DOM is the source of truth.

🔑 Key Differences

FeatureControlledUncontrolled
Value managed byReact stateDOM (browser)
How to read valueFrom stateFrom ref.current.value
React controlHigh (can validate instantly)Low
Use caseValidation, dynamic forms, live feedbackSimple forms, quick data grab

🚀 When Should You Use Which?

  • ✅ Use Controlled Components when you need:
    • Form validation (e.g., check if email is valid while typing)
    • Live updates (e.g., show typed text somewhere else instantly)
    • Complex or large forms
  • ✅ Use Uncontrolled Components when you need:
    • Simple, one-time form submissions
    • Quick prototypes
    • Minimal React state management

🎓 Final Takeaway

  • Controlled → React is the boss. It decides what’s in the input.
  • Uncontrolled → Browser is the boss. React just asks for the value when needed.

👉 As a beginner, it’s usually best to start with Controlled Components because they give you more power and flexibility. But knowing Uncontrolled Components will help you understand how React works with the DOM.


Top 20 Node.js Interview Questions & Answers [2025 Updated]

1. What is Node.js?

  • Concept: Node.js is a JavaScript runtime built on Chrome’s V8 engine, enabling server-side JavaScript execution.
  • Example:
console.log("Hello from Node.js!");

  • Real Use Case: Powering backend servers for apps like Netflix, PayPal, or LinkedIn.

2. What is the Event Loop in Node.js?

  • Concept: The event loop handles asynchronous operations, allowing non-blocking I/O.
  • Example:
setTimeout(() => console.log("Executed later"), 0);
console.log("Executed first");

  • Real Use Case: Efficient handling of thousands of requests in chat apps like WhatsApp Web.

3. What are Streams in Node.js?

  • Concept: Streams let you read/write data piece by piece instead of loading it all at once.
  • Example:
const fs = require('fs');
fs.createReadStream('file.txt').pipe(process.stdout);

  • Real Use Case: Video/audio streaming platforms (YouTube, Spotify).

4. Difference between require and import

  • Concept: require is CommonJS; import is ES Module syntax.
  • Example:
// CommonJS
const fs = require('fs');

// ES Module
import fs from 'fs';

  • Real Use Case: require for legacy Node apps, import for modern apps with ES Modules.

5. What is Middleware in Node.js (Express)?

  • Concept: Functions that execute during request/response lifecycle.
  • Example:
app.use((req, res, next) => {
  console.log("Request Time:", Date.now());
  next();
});

  • Real Use Case: Logging, authentication, error handling in Express apps.

6. What are Promises & Async/Await?

  • Concept: Handle asynchronous code more cleanly than callbacks.
  • Example:
const fetchData = async () => {
  let res = await fetch("https://api.github.com/users");
  console.log(await res.json());
};

  • Real Use Case: API requests in e-commerce checkout systems.

7. What is the difference between Process & Thread in Node.js?

  • Concept: Process = instance of program, Thread = unit of execution inside process. Node.js is single-threaded with background worker threads.
  • Example: Running one Node process with multiple requests handled asynchronously.
  • Real Use Case: Scaling microservices with cluster module.

8. Explain Node.js Clustering

  • Concept: Allows running multiple Node.js processes to utilize multi-core CPUs.
  • Example:
const cluster = require('cluster');
if (cluster.isMaster) {
  cluster.fork();
} else {
  console.log("Worker process running");
}

  • Real Use Case: Improving throughput in high-traffic apps (Uber, Paytm).

9. What is process.nextTick()?

  • Concept: Executes callback after the current operation, before event loop continues.
  • Example:
process.nextTick(() => console.log("Next Tick executed"));
console.log("Main code");

  • Real Use Case: Deferring execution in async libraries like Mongoose.

10. What is an EventEmitter?

  • Concept: A core module for event-driven programming.
  • Example:
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('start', () => console.log('Started!'));
emitter.emit('start');

  • Real Use Case: Notification systems, chat events.

11. What are Worker Threads in Node.js?

  • Concept: Allow running JavaScript in parallel threads.
  • Example:
const { Worker } = require('worker_threads');
new Worker('./worker.js');

  • Real Use Case: Heavy CPU tasks like video compression.

12. Difference between Synchronous & Asynchronous code

  • Concept: Sync blocks execution, async allows parallel tasks.
  • Example:
// Sync
let data = fs.readFileSync('file.txt');

// Async
fs.readFile('file.txt', (err, data) => console.log(data));

  • Real Use Case: Async is critical for APIs and file systems.

13. What is JWT and how is it used in Node.js?

  • Concept: JSON Web Token for authentication.
  • Example:
jwt.sign({ userId: 1 }, "secretKey");

  • Real Use Case: Secure login in apps like Facebook/Google sign-in.

14. What is the difference between fs.readFile and fs.createReadStream?

  • Concept: readFile loads entire file, createReadStream streams it in chunks.
  • Example:
fs.readFile('big.txt', ...); 
fs.createReadStream('big.txt').pipe(process.stdout);

  • Real Use Case: Large file handling (logs, videos).

15. What is Nodemon?

  • Concept: A tool that auto-restarts Node.js server on file changes.
  • Example:
npx nodemon app.js

  • Real Use Case: Developer productivity during backend coding.

16. What are Environment Variables in Node.js?

  • Concept: Variables stored outside the code for configs.
  • Example:
process.env.PORT || 3000;

  • Real Use Case: Storing API keys, DB credentials.

17. Explain CORS in Node.js

  • Concept: Cross-Origin Resource Sharing allows restricted resources to be accessed from another domain.
  • Example:
const cors = require('cors');
app.use(cors());

  • Real Use Case: Allowing frontend (React/Angular) to call backend API.

18. What is the difference between Monolithic & Microservices in Node.js?

  • Concept:
    • Monolithic: Single large codebase.
    • Microservices: Independent services communicating via APIs.
  • Example: Splitting user service, product service, payment service.
  • Real Use Case: Netflix migrated from monolithic to microservices using Node.js.

19. What is Rate Limiting in Node.js?

  • Concept: Restrict number of requests to avoid abuse.
  • Example:
const rateLimit = require("express-rate-limit");
app.use(rateLimit({ windowMs: 60*1000, max: 5 }));

  • Real Use Case: Preventing brute-force login attacks.

20. How does Node.js handle child processes?

  • Concept: Allows spawning new processes.
  • Example:
const { exec } = require('child_process');
exec('ls', (err, stdout) => console.log(stdout));

  • Real Use Case: Running background tasks like sending emails.

React Reconciliation: The Secret Sauce Behind Fast UIs

This is one of the least-known but most important concepts in React. Most beginners know about useState, props, or useEffect, but very few know how React decides what to update in the DOM efficiently. That’s where Reconciliation & Virtual DOM diffing comes in.

Most of us love React because it makes building UIs fast and efficient. But have you ever wondered:

👉 How does React know which part of the screen to update when state changes?

The answer lies in Reconciliation.


🔑 What is Reconciliation?

Reconciliation is React’s process of figuring out what changed in the UI and updating only that part in the real DOM, instead of re-rendering everything.

It works with the Virtual DOM:

  1. React keeps a copy of the UI in memory (Virtual DOM).
  2. When state/props change, React creates a new Virtual DOM tree.
  3. React compares (diffs) the new Virtual DOM with the previous one.
  4. Only the changed nodes are updated in the real DOM.

This is why React apps feel super fast compared to manually updating DOM elements with vanilla JS.


⚡ Why is Reconciliation Important?

  • Prevents unnecessary re-renders 🚫
  • Makes React scalable even in huge apps 🏗️
  • Saves time by batching updates ⏳

Without Reconciliation, React would have to re-render the entire page on every change — which would be very slow.


🧩 Example: Keys in Lists & Reconciliation

React heavily relies on keys when reconciling lists.

const items = ["Apple", "Banana", "Cherry"];

return (
  <ul>
    {items.map((item) => (
      <li key={item}>{item}</li>
    ))}
  </ul>
);

  • If you don’t use proper key values, React may re-render more items than necessary.
  • With proper keys, React knows exactly which item changed and updates only that item.

💡 Pro Tip:

When you see “keys should be unique” warnings — it’s not just React being strict. It’s Reconciliation in action — React needs keys to perform efficient diffing.


🎯 Final Takeaway

Reconciliation is the brain of React.
It’s the reason React apps stay fast and efficient, even as they grow.

Next time you write a component, remember:
👉 React isn’t just re-rendering everything blindly — it’s carefully reconciling changes for performance.