🧠 Simple Definition (Word-for-word)
Vertical: add more CPU/RAM to one machine — simple, no code changes, but limited and expensive, single point of failure.
⚡ Super Simple Line
Horizontal: add more machines — theoretically unlimited scale, resilient.
⚡ Key Details & Explanation
Vertical: add more CPU/RAM to one machine — simple, no code changes, but limited and expensive, single point of failure. Horizontal: add more machines — theoretically unlimited scale, resilient. Horizontal is hard when: state needs to be shared (sessions, file uploads — need shared Redis/S3), database writes don't scale easily horizontally (sharding adds complexity), stateful WebSocket connections tied to one server. Stateless apps scale horizontally easily — this is why stateless JWT and external state stores (Redis) matter.
🌱 Beginner Explanation
Vertical scaling is "buy bigger machine." Horizontal scaling is "add more machines." Horizontal sounds better, but becomes hard when system keeps important state in one machine's memory or local disk.
Stateless app servers are easiest to scale horizontally. Shared state usually moves to Redis, S3, or database.
🗣️ How To Explain In Interview
Vertical scaling is simpler but limited and creates a stronger single point of failure. Horizontal scaling gives better long-term scale and resilience, but shared state, database writes, and stateful connections like WebSockets make it harder.
❓ Follow-up Questions
Why are stateless services easier to scale?
Any request can go to any server.Why are writes harder than reads?
Because correctness and coordination across nodes are harder.How do sessions scale horizontally?
Shared Redis session store or stateless tokens.
🧠 Simple Definition (Word-for-word)
CDN (Content Delivery Network): geographically distributed servers that cache and serve static assets (images, JS, CSS, videos) from the edge server closest to the user.
⚡ Super Simple Line
How it works: request hits CDN edge, on cache miss the edge fetches from your origin server and caches it (TTL-based).
⚡ Key Details & Explanation
CDN (Content Delivery Network): geographically distributed servers that cache and serve static assets (images, JS, CSS, videos) from the edge server closest to the user. How it works: request hits CDN edge, on cache miss the edge fetches from your origin server and caches it (TTL-based). Subsequent requests are served from the edge without hitting your origin. In Next.js: next/image automatically uses the CDN configured in Vercel, static assets in /public are CDN-served by default. Cache-Control headers control CDN caching behavior.
🌱 Beginner Explanation
CDN is basically a distributed cache in front of your origin server. Closest edge server answers request if it already has file. That reduces latency and also protects origin from repeated traffic.
Important beginner words: edge, cache hit, cache miss, TTL, origin.
🗣️ How To Explain In Interview
A CDN stores cacheable content at edge locations near users. On a cache hit, user gets content quickly from nearby server. On a cache miss, edge fetches from origin, stores it, and future requests become faster.
❓ Follow-up Questions
What is cache hit ratio?
Percent of requests served directly from CDN cache.Why does CDN reduce load?
Origin does not serve every repeated asset request.Can dynamic responses use CDN?
Yes, if caching rules allow it.
🧠 Simple Definition (Word-for-word)
In a distributed system you can guarantee at most 2 of 3: Consistency (every read gets the latest write), Availability (every request gets a response), Partition Tolerance (system works despite network failures).
⚡ Super Simple Line
Since network partitions WILL happen, the real choice is C vs A during a partition.
⚡ Key Details & Explanation
In a distributed system you can guarantee at most 2 of 3: Consistency (every read gets the latest write), Availability (every request gets a response), Partition Tolerance (system works despite network failures). Since network partitions WILL happen, the real choice is C vs A during a partition. CP systems (PostgreSQL, MongoDB with majority reads): return error rather than stale data. AP systems (Cassandra, DynamoDB): return possibly stale data but remain available. Choose based on your domain: banking = CP, social feed = AP.
🌱 Beginner Explanation
CAP theorem confuses many beginners because they think it means you can freely pick any two. Better way to think:
When network breaks between nodes, do I prefer correct data or always returning a response?
That is real tradeoff. Partition tolerance is not optional in distributed systems because networks fail.
🗣️ How To Explain In Interview
CAP theorem matters only when a partition happens. At that moment, I cannot have both perfect consistency and full availability. So for something like payments I would prefer consistency, while for something like a social feed I may prefer availability.
❓ Follow-up Questions
What does consistency mean here?
Every read sees latest write, not just "data validity".Why is partition tolerance mandatory?
Because network failures happen in real distributed systems.Can one product mix CP and AP?
Yes, different subsystems can make different tradeoffs.
🧠 Simple Definition (Word-for-word)
Eventual consistency: in a distributed system, all replicas will eventually converge to the same value — but at any point in time, different nodes may serve different values.
⚡ Super Simple Line
Acceptable for: social media feeds (stale posts for a few seconds is fine), shopping cart (eventual sync is ok), DNS propagation, search indexes.
⚡ Key Details & Explanation
Eventual consistency: in a distributed system, all replicas will eventually converge to the same value — but at any point in time, different nodes may serve different values. Acceptable for: social media feeds (stale posts for a few seconds is fine), shopping cart (eventual sync is ok), DNS propagation, search indexes. Not acceptable for: financial transactions (money debits), inventory counts where overselling is a problem, authentication (revoked tokens must be respected immediately).
🌱 Beginner Explanation
Eventual consistency means data may be briefly different on different replicas, but if no new writes happen, they will converge. This is often acceptable when being a few seconds behind is not a business disaster.
Good mental model: you update profile photo, one region shows new image now, another region shows old image for a few seconds, then catches up.
🗣️ How To Explain In Interview
Eventual consistency means replicas may temporarily disagree, but they converge over time. I would accept that for feeds, search indexes, and analytics, but not for bank balances or strict inventory counts.
❓ Follow-up Questions
Why would a system choose eventual consistency?
To improve availability, latency, and geographic scale.Where is it dangerous?
Payments, auth revocation, strict inventory.How do clients handle it?
Show loading states, versioning, retries, or read-after-write strategies.
🧠 Simple Definition (Word-for-word)
Long polling: client makes request, server holds it open until data is available, then responds, client immediately makes new request — lots of overhead, HTTP overhead per message.
⚡ Super Simple Line
SSE (Server-Sent Events): persistent one-directional connection (server to client only) over HTTP — simple, auto-reconnect, EventSource API.
⚡ Key Details & Explanation
Long polling: client makes request, server holds it open until data is available, then responds, client immediately makes new request — lots of overhead, HTTP overhead per message. SSE (Server-Sent Events): persistent one-directional connection (server to client only) over HTTP — simple, auto-reconnect, EventSource API. WebSockets: full-duplex persistent TCP connection — both directions, lower overhead per message, more complex. Use SSE for: notifications, live feeds (server pushes only). Use WebSockets for: chat, collaborative editing, games (bidirectional real-time).
🌱 Beginner Explanation
Difference is mainly connection style and direction.
Long polling: repeated HTTP requests, highest overhead
SSE: one-way server-to-client stream
WebSocket: full two-way persistent connection
Simple rule: notifications/live feed often fit SSE; chat/games/collaboration need WebSockets.
🗣️ How To Explain In Interview
If server only needs to push updates to client, SSE is often simpler than WebSockets. If both client and server need to send frequent messages, like chat or multiplayer features, WebSockets are more appropriate. Long polling is older and heavier because every update still involves repeated HTTP requests.
❓ Follow-up Questions
Why is SSE simpler?
Built on normal HTTP and one-directional.Why not use long polling first?
It creates more request overhead and latency.Why WebSockets for chat?
Because chat needs low-latency communication in both directions.
🧠 Simple Definition (Word-for-word)
An API gateway is the single entry point for clients in front of multiple services.
⚡ Super Simple Line
It can handle authentication, rate limiting, request routing, header normalization, logging, and sometimes response aggregation.
⚡ Key Details & Explanation
An API gateway is the single entry point for clients in front of multiple services. It can handle authentication, rate limiting, request routing, header normalization, logging, and sometimes response aggregation. This keeps cross-cutting concerns out of every service. Tradeoff: it can become a bottleneck or too smart if you push business logic into it. Keep it focused on routing and platform concerns, not domain behavior.
🌱 Beginner Explanation
API gateway is front door of distributed system. Clients talk to gateway, and gateway routes request to correct internal service. This keeps clients simpler and centralizes cross-cutting concerns.
Gateway should handle platform concerns, not business logic.
🗣️ How To Explain In Interview
I would use an API gateway as a single entry point in front of multiple services. It can centralize authentication, rate limiting, routing, logging, and header normalization. But I would avoid putting core domain logic there so it does not become an overly smart bottleneck.
❓ Follow-up Questions
Why not let clients call services directly?
That increases client complexity and duplicates concerns like auth and rate limiting.Can gateway aggregate responses?
Yes, sometimes, but keep it limited.Big risk of gateway?
It can become bottleneck or single point of failure if poorly designed.
🧠 Simple Definition (Word-for-word)
Message queues decouple producers and consumers, enable async processing, and add resilience.
⚡ Super Simple Line
RabbitMQ: traditional message broker, complex routing (exchanges, bindings), good for task queues, RPC patterns, small-medium volume.
⚡ Key Details & Explanation
Message queues decouple producers and consumers, enable async processing, and add resilience. RabbitMQ: traditional message broker, complex routing (exchanges, bindings), good for task queues, RPC patterns, small-medium volume. Kafka: high-throughput event streaming platform, messages retained and replayable, consumer groups, event sourcing — best for data pipelines, analytics, millions of events/sec. Bull/BullMQ: Redis-backed job queue for Node.js — simple, great for background jobs (email, thumbnail generation), retry logic, no separate infra if you already use Redis.
🌱 Beginner Explanation
All three tools move work asynchronously, but they solve slightly different problems.
Kafka is event streaming with replay
RabbitMQ is broker-style messaging and routing
BullMQ is practical app job queue for Node.js
If beginner answer feels too broad, use real examples: BullMQ for sending emails, RabbitMQ for routing tasks across services, Kafka for clickstream analytics.
🗣️ How To Explain In Interview
I would pick based on workload. For simple application background jobs in a Node.js stack, BullMQ is enough. For broker-style routing between services, RabbitMQ is strong. For very high-throughput event streams and replay, Kafka is best.
❓ Follow-up Questions
What is replay?
Consumers can read old retained events again.What is dead-letter queue?
Failed messages go there after retry attempts.Why not direct HTTP call instead of queue?
Queue reduces coupling and handles spikes better.
🧠 Simple Definition (Word-for-word)
Event-driven architecture is a design pattern where the flow of the application is driven by events.
⚡ Super Simple Line
Instead of directly calling functions, components communicate by emitting and listening to events.
⚡ Key Details & Explanation
Benefits include:
- Loose coupling between components
- Better scalability
- Easier to extend features
In Node.js, this pattern is very common because of EventEmitter and async nature.
For example, in a notification system, one service emits an event and multiple services like email or logging can react to it independently.
🌱 Beginner Explanation
Event-driven architecture means one part of system emits event like OrderPlaced, and other parts react to it. Producer does not need to know every consumer directly.
That makes system easier to extend. New service can subscribe later without changing original producer.
🗣️ How To Explain In Interview
In event-driven architecture, services communicate by publishing and consuming events rather than calling each other directly for every action. This reduces coupling, improves scalability, and makes it easier to add new consumers like notifications, analytics, or auditing.
❓ Follow-up Questions
What is main benefit?
Loose coupling between services.What is challenge?
Debugging and guaranteeing delivery/order can be harder.Good example event?
OrderPlacedconsumed by email, billing, analytics.
🧠 Simple Definition (Word-for-word)
Microservices: split an app into independently deployable services, each owning its data and running in its own process.
⚡ Super Simple Line
Benefits: independent scaling, independent deployment, tech heterogeneity, team autonomy.
⚡ Key Details & Explanation
Microservices: split an app into independently deployable services, each owning its data and running in its own process. Benefits: independent scaling, independent deployment, tech heterogeneity, team autonomy. Problems they introduce: distributed system complexity (network failures, latency), service discovery, distributed tracing, data consistency across services (no ACID transactions), API contracts between teams, operational overhead (many deployments, many logs). Start with a monolith, extract services only when there's a clear scaling or team boundary reason.
🌱 Beginner Explanation
Microservices are not automatically "better architecture." They trade simplicity for team independence and scaling flexibility. A monolith is one deployable application. Microservices split that into many networked applications.
When beginners answer this, strongest line is: microservices solve some problems by creating new problems.
🗣️ How To Explain In Interview
Microservices help when teams or scaling needs are large enough to justify separate services, deployments, and ownership. But they also create network failures, tracing challenges, data consistency issues, and more operational work, so I would not start with them unless there is a clear reason.
❓ Follow-up Questions
Why start with monolith?
Faster development, simpler deployment, easier debugging.What replaces one big DB transaction?
Saga or compensation patterns.What monitoring becomes important?
Distributed tracing, centralized logs, service metrics.
🧠 Simple Definition (Word-for-word)
To handle a sudden spike in traffic: I would first ensure that my infrastructure can scale (using cloud services like AWS or GCP).
⚡ Super Simple Line
Implement auto-scaling to add more servers as needed.
⚡ Key Details & Explanation
- I would first ensure that my infrastructure can scale (using cloud services like AWS or GCP).
- Implement auto-scaling to add more servers as needed.
- Use a load balancer to distribute traffic evenly across servers.
- Implement caching to reduce database load.
- And if necessary, I would also consider using a CDN to serve static assets faster.
This way, I can maintain performance and prevent downtime during traffic spikes.
🌱 Beginner Explanation
Sudden 100x traffic means answer should be structured, not random.
Protect system first
Scale stateless layers
Reduce load on bottlenecks
Gracefully degrade if needed
This is not only about adding servers. Usually real bottleneck is database, cache, queue, or third-party dependency.
🗣️ How To Explain In Interview
My first step is to stabilize the system: rate limit abusive traffic, cache aggressively, and scale stateless app servers behind a load balancer. Then I would protect the database with read replicas, query optimization, and queue-based async work. If needed, I would degrade noncritical features temporarily so core user flows stay alive.
❓ Follow-up Questions
What do you protect first?
Database and core critical paths.Why graceful degradation?
It keeps checkout/login/core flows alive by disabling nonessential features.What metrics matter during spike?
Latency, error rate, CPU, DB connections, cache hit rate, queue depth.
🧠 Simple Definition (Word-for-word)
A Rate Limiter is a system component that limits the number of requests a client can make within a given timeframe to protect API resources from abuse or overloading. The Token Bucket algorithm maintains a bucket filled with tokens at a constant rate; requests consume tokens, allowing bursts of traffic. The Sliding Window Counter tracks request timestamps within a sliding window interval, offering strict rate limits with low memory overhead.
📊 Comparison of Rate Limiting Algorithms
| Algorithm | Traffic Bursts | Memory Usage | Implementation Complexity |
|---|---|---|---|
| Token Bucket | ✅ Allowed | Low (stores integer) | Easy |
| Leaky Bucket | ❌ Smoothed out | Low (FIFO queue) | Medium |
| Sliding Window Log | ❌ Strict limit | ⚠️ High (stores all timestamps) | Hard |
| Sliding Window Counter | ❌ Strict limit | Low (stores counters) | Medium |
🧪 Redis Implementation Concept
Redis is commonly used to build distributed rate limiters because operations are atomic:
// Sliding Window Counter using Redis sorted set (ZSET)
async function isRateLimited(userId) {
const now = Date.now();
const windowMs = 60000; // 1 minute
const maxRequests = 100;
const key = `rate_limit:${userId}`;
await redis.multi()
.zremrangebyscore(key, 0, now - windowMs) // Remove old timestamps
.zcard(key) // Count remaining request logs
.zadd(key, now, now) // Log current request
.expire(key, 60)
.exec();
}
⚡ One-line Interview Answer
Rate limiters protect APIs using algorithms like Token Bucket, which permits momentary bursts of traffic, or Sliding Window Counter, which strictly throttles traffic with low memory footprints.
🌱 Beginner Explanation
Rate limiter protects system from abuse and sudden spikes. First decide what you are limiting: per IP, per user, per API key, or per endpoint. Then decide behavior.
Token bucket: good when small bursts are okay
Sliding window: good when you want stricter fairness
In practice, Redis is common because you need one shared counter store across many app servers.
🗣️ How To Explain In Interview
I would store rate-limit state in Redis so all app servers share the same counters. If I want to allow bursts, I would use token bucket. If I want stricter control over requests in the last N seconds, I would use a sliding-window approach.
❓ Follow-up Questions
Why not in-memory counters?
Because multiple app servers would each have different counts.What response code is common?
429 Too Many Requests.What headers can you return?
Remaining quota, reset time, retry-after.
🧠 Simple Definition (Word-for-word)
Core: generate a short code (6-8 chars) mapped to a long URL.
⚡ Super Simple Line
Schema: {id, shortCode, longUrl, userId, createdAt, clickCount}.
⚡ Key Details & Explanation
Core: generate a short code (6-8 chars) mapped to a long URL. Schema: {id, shortCode, longUrl, userId, createdAt, clickCount}. Shortcode generation: base62 encoding of auto-incremented ID, or random string with collision check. API: POST /shorten → returns shortUrl; GET /:code → 301 redirect to longUrl. Scalability: cache hot URLs in Redis (most URLs are accessed rarely — 80/20 rule), CDN for the redirect service, DB sharding by shortCode hash for massive scale. Analytics: async queue for click tracking.
🌱 Beginner Explanation
Think of this as a very fast dictionary.
shortCode -> longUrlWhen user creates short URL, system stores mapping. When someone opens short URL, system finds original URL and redirects. Since redirects happen far more often than URL creation, this is a read-heavy system. That is why caching is so important.
Start simple in interviews:
API for create
DB table for mapping
Redis for hot links
Async analytics
🗣️ How To Explain In Interview
I would model a URL shortener as a read-heavy key-value lookup system. The write path creates a unique short code and stores it with the long URL. The read path looks up the short code and returns a redirect, ideally from Redis first and then the database. At scale, I would separate analytics from the redirect path so user latency stays low.
❓ Follow-up Questions
How do you prevent collisions?
Use a unique index onshortCodeand retry generation if insert fails.Why cache here?
Because redirect traffic is much higher than create traffic.301 or 302?
301 for permanent redirects, 302 if destination may change.
🧠 Simple Definition (Word-for-word)
Avoid sending large files through your server.
⚡ Super Simple Line
Pattern: client requests a presigned URL from your API, your API calls S3 to generate a presigned URL, client uploads directly to S3 bypassing your server.
⚡ Key Details & Explanation
Avoid sending large files through your server. Pattern: client requests a presigned URL from your API, your API calls S3 to generate a presigned URL, client uploads directly to S3 bypassing your server. For very large files (>100MB): multipart upload — split into chunks (5MB+ each), upload each chunk with its own presigned URL, complete multipart upload when all chunks arrive. Your API just coordinates metadata. Resumability: track which chunks are uploaded, resume from where it failed.
🌱 Beginner Explanation
Main beginner mistake is sending entire file through backend server. That wastes app-server bandwidth and makes scaling expensive. Better pattern:
Client asks backend for permission -> Backend returns presigned upload URL -> Client uploads directly to object storage -> Backend stores metadata onlyFor very large files, multipart upload is important because failed chunks can be retried instead of restarting whole upload.
🗣️ How To Explain In Interview
I would keep the backend out of the file data path. The backend authenticates the user and creates a presigned upload session, but the client uploads directly to S3 or similar object storage. For large files, I would use multipart upload so uploads can resume and only failed chunks need retrying.
❓ Follow-up Questions
Why direct upload?
Less backend load, cheaper bandwidth, easier scaling.How do you support resume?
Multipart upload plus chunk tracking.How do you secure it?
Short-lived presigned URLs and server-side validation of size/type.
🧠 Simple Definition (Word-for-word)
Frontend should debounce input and cancel stale requests.
⚡ Super Simple Line
Backend options depend on scale: SQL prefix search for simple cases, Elasticsearch/OpenSearch for large-scale ranking, typo tolerance, and advanced relevance.
⚡ Key Details & Explanation
Frontend should debounce input and cancel stale requests. Backend options depend on scale: SQL prefix search for simple cases, Elasticsearch/OpenSearch for large-scale ranking, typo tolerance, and advanced relevance. Return small payloads quickly, usually top 5-10 suggestions. Cache hot queries, track analytics for no-result searches, and rank by a combination of prefix match, popularity, and recent behavior where relevant.
🌱 Beginner Explanation
Autocomplete is about giving a few good suggestions very quickly. Fast response matters more than returning huge result sets.
Start small:
Debounce input on frontend
Cancel stale requests
Return top 5-10 suggestions
For simple systems, SQL prefix search can work. For advanced ranking and typo tolerance, search engine like Elasticsearch/OpenSearch is better.
🗣️ How To Explain In Interview
I would optimize for low latency and relevance. The frontend should debounce and cancel stale requests. Backend can start with prefix search in SQL for small scale, but for typo tolerance, ranking, and large query volume, I would move to Elasticsearch or OpenSearch and cache hot queries.
❓ Follow-up Questions
Why debounce?
Prevents request on every keystroke.Why cancel stale requests?
So old slow responses do not overwrite newer user input.What ranking signals matter?
Prefix match, popularity, recency, user behavior.
🧠 Simple Definition (Word-for-word)
WebSockets for persistent connections — but can't hold 1M open connections on one server.
⚡ Super Simple Line
Solution: use a pub/sub layer (Redis Pub/Sub or Kafka).
⚡ Key Details & Explanation
WebSockets for persistent connections — but can't hold 1M open connections on one server. Solution: use a pub/sub layer (Redis Pub/Sub or Kafka). Each notification server subscribes to channels for its connected users. When a notification is generated, publish to the user's channel — the server holding that user's connection delivers it. Use a message queue for delivery guarantees and retry. Store undelivered notifications in DB for users who are offline. Send push notifications (FCM/APNs) for mobile.
🌱 Beginner Explanation
Notification system has two jobs:
Store notification so it is not lost
Deliver notification fast to online users
Do not depend only on WebSocket. If user is offline, WebSocket cannot help. So store notification in DB first, then push in real time if user is connected. For 1M users, many WebSocket servers sit behind load balancer, and a pub/sub layer helps route messages to correct server.
🗣️ How To Explain In Interview
I would separate notification persistence from delivery. First, store notification durably in the database. Then publish an event so the WebSocket server holding that user's connection can deliver it in real time. If the user is offline, the notification remains unread in storage and can be fetched later.
❓ Follow-up Questions
Why not just WebSockets?
Because offline users would lose notifications.Why use Redis Pub/Sub or Kafka?
Because user connections are spread across many servers.How do mobile users get alerts?
Use push providers like FCM or APNs.
🧠 Simple Definition (Word-for-word)
For a chat system, I would start with conversations, participants, and messages as the core data model.
⚡ Super Simple Line
When a user sends a message, the backend should validate the user, save the message to the database first, and then deliver it in real time to online users through WebSockets.
⚡ Key Details & Explanation
For a chat system, I would start with conversations, participants, and messages as the core data model. When a user sends a message, the backend should validate the user, save the message to the database first, and then deliver it in real time to online users through WebSockets. If the recipient is offline, the message stays stored and can be loaded when they reconnect, and the system can also send a push notification. Message history should use cursor pagination because chat history can become large and users usually load older messages gradually. The system also needs delivery status, read receipts if required, and ordering by a reliable timestamp or sequence number. At scale, WebSocket servers need shared state through Redis Pub/Sub, Kafka, or another messaging layer so a message can reach users connected to different servers. The most important tradeoff is reliability versus complexity: for simple chat, saving messages and pushing live updates is enough, but for large-scale chat, ordering, retries, and multi-server delivery become the hard parts.
🌱 Beginner Explanation
Chat system has both storage problem and delivery problem.
Storage: save messages reliably and paginate history
Delivery: send instantly to online users, later to offline users
Good beginner design: save message first, then push through WebSocket. That avoids losing chat messages if delivery fails.
🗣️ How To Explain In Interview
I would save each message durably before attempting real-time delivery. Online users get it immediately through WebSockets, while offline users fetch it later from storage and may receive a push notification. At scale, multiple socket servers need shared pub/sub so users connected to different servers still receive messages.
❓ Follow-up Questions
Why cursor pagination for history?
Chat history grows large and users load older messages gradually.How do you handle ordering?
Use server timestamps or sequence IDs per conversation.What about delivery receipts?
Store message status like sent, delivered, read.
🧠 Simple Definition (Word-for-word)
Two main approaches: Operational Transformation (OT) — transforms concurrent operations to be compatible (used by Google Docs); CRDT (Conflict-free Replicated Data Types) — data structures designed to merge automatically without conflict (used by Figma, Notion).
⚡ Super Simple Line
For a simpler system: use Yjs library (CRDT-based), connect via WebSocket (Socket.io or native), broadcast operations to all connected clients.
⚡ Key Details & Explanation
Two main approaches: Operational Transformation (OT) — transforms concurrent operations to be compatible (used by Google Docs); CRDT (Conflict-free Replicated Data Types) — data structures designed to merge automatically without conflict (used by Figma, Notion). For a simpler system: use Yjs library (CRDT-based), connect via WebSocket (Socket.io or native), broadcast operations to all connected clients. Persist: save document state to DB on debounced changes or a dedicated save action. The hard part is handling concurrent edits and cursor positions.
🌱 Beginner Explanation
Collaborative editors are hard because many users may edit same text at same time. System must merge edits safely and keep everyone in sync. That is why answers mention OT and CRDT.
If beginner, it is perfectly okay in interview to say: I would use proven library like Yjs instead of inventing OT/CRDT algorithm myself.
🗣️ How To Explain In Interview
I would use WebSockets for real-time communication and a proven conflict-resolution model like CRDT through Yjs. Clients send operations, server broadcasts them, and document state is persisted periodically. Main challenges are concurrent edits, cursor positions, and offline edits.
❓ Follow-up Questions
Why not save full document on every keystroke?
Too much bandwidth and conflict risk.What is CRDT advantage?
Concurrent changes can merge automatically.Why WebSocket here?
Need fast bidirectional low-latency updates.