The Complete Backend Engineering Guide
Everything you need to crack a backend interview — from REST semantics to distributed systems, real-time architecture, and the tricky gotchas that separate great engineers from good ones

Backend Engineering Fundamentals
Modern backend engineering is no longer just about building CRUD APIs. A production backend today handles distributed traffic, concurrent users, database consistency, background jobs, caching layers, security boundaries, real-time communication, observability, and failure recovery — all while remaining scalable, maintainable, and fast.
The challenge is not simply writing endpoints. The real challenge is understanding why backend systems are designed the way they are. Every architectural decision is a trade-off between performance, consistency, complexity, scalability, developer velocity, and operational cost.
A strong backend engineer does not memorize tools. They understand the problems those tools solve.
The Evolution: Node.js → Bun.js
Most backend engineers start with Node.js and Express. Modern production systems increasingly use Bun.js and frameworks like Elysia.js because they provide native TypeScript execution, faster startup times, lower memory usage, and higher throughput.
The concepts remain the same — HTTP, async programming, databases, caching, queues — but the runtime primitives and ecosystem evolve.
// Node.js + Express
const express = require('express');
require('dotenv').config();
// Bun.js + Elysia.js
import { Elysia } from 'elysia';
const app = new Elysia();
const secret = Bun.env.JWT_SECRET;
Bun removes many layers traditionally required in Node.js:
- No
ts-node - No Babel configuration
- No separate dotenv loader
- Native package manager + runtime
The runtime becomes simpler, faster, and more integrated.
---HTTP & REST Fundamentals
Backend systems communicate primarily over HTTP. Understanding HTTP deeply is foundational because every API, browser request, mobile request, and microservice interaction depends on it.
HTTP Methods
Each HTTP method carries semantic meaning.
GET → Fetch data
POST → Create new data
PUT → Replace entire resource
PATCH → Partial update
DELETE → Remove resource
The critical concept here is idempotency.
An operation is idempotent if calling it multiple times produces the same result.
GET /users/1
GET /users/1
GET /users/1
No matter how many times this runs, the resource does not change.
PUT is also idempotent:
PUT /users/1
{
"name": "John"
}
Calling it once or ten times leaves the resource in the same state.
POST is NOT idempotent:
POST /payments
Retrying may create duplicate payments.
This distinction matters enormously in distributed systems where retries happen automatically.
---REST Principles
REST is not merely “JSON over HTTP.” It is an architectural style based on constraints.
1. Statelessness
Every request contains all information needed to process it.
The server should not rely on in-memory session state.
This enables horizontal scaling because any server instance can process any request.
2. Resource-Oriented Design
Resources are nouns, not verbs.
// Good
GET /orders
POST /orders
// Bad
GET /getOrders
POST /createOrder
3. Uniform Interface
Clients interact consistently with resources regardless of implementation details.
4. HATEOAS
Responses can include discoverable links.
{
"id": 101,
"status": "pending",
"links": {
"cancel": "/orders/101/cancel"
}
}
---
Status Codes
Status codes communicate the result of a request.
2xx — Success
200 OK
201 Created
204 No Content
4xx — Client Errors
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
422 Unprocessable Entity
429 Too Many Requests
5xx — Server Errors
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
Strong backend engineers know when each status code should be used — not just what they mean.
---Middleware & Request Pipelines
Middleware forms the backbone of backend request processing.
A request typically flows through authentication, validation, rate limiting, logging, business logic, and response formatting.
app
.onRequest(authMiddleware)
.beforeHandle(validateInput)
.get('/users/:id', handler)
.afterHandle(logResponse);
Execution order matters.
Authentication must happen before authorization. Validation must happen before database writes.
Middleware architecture enables separation of concerns.
---Async Programming & The Event Loop
JavaScript runtimes like Node.js and Bun are single-threaded. Concurrency is achieved through the event loop.
The Event Loop
The execution order is:
Call Stack
↓
Microtask Queue (Promises)
↓
Macrotask Queue (setTimeout, setInterval)
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
Output:
1
4
3
2
Promises execute before timers because microtasks have higher priority.
---Event Loop Blocking
The most dangerous mistake in Node.js/Bun is CPU-bound work on the main thread.
const huge = JSON.parse(bigPayload);
A 50MB parse operation can freeze the event loop for hundreds of milliseconds.
During that time:
- No requests are processed
- No sockets respond
- No timers fire
- The server appears “down”
Solutions
- Worker Threads
- Streaming parsers
- Payload limits
- Background job processing
Database Fundamentals
Databases are the persistence layer of backend systems.
SQL vs NoSQL
SQL Databases
- Structured schema
- ACID guarantees
- Joins & relationships
- Strong consistency
Examples:
- PostgreSQL
- MySQL
NoSQL Databases
- Flexible schema
- Horizontal scalability
- Eventual consistency
- High write throughput
Examples:
- MongoDB
- Cassandra
- DynamoDB
The key engineering skill is knowing when to use which.
---Indexes
An index is a sorted structure enabling fast lookups.
Without an index:
SELECT * FROM users WHERE email = 'a@b.com';
The database scans the entire table.
With an index:
CREATE INDEX idx_users_email ON users(email);
The lookup becomes logarithmic.
Indexes speed reads but slow writes because every insert/update must also update the index.
---Composite Indexes
One of the most important real-world optimization techniques.
SELECT *
FROM orders
WHERE user_id = 42
ORDER BY created_at DESC
LIMIT 20;
On a 500M row table, this becomes extremely slow without the right index.
The Fix
CREATE INDEX idx_orders_user_created
ON orders(user_id, created_at DESC);
This allows efficient filtering and sorting simultaneously.
---Transactions & ACID
Transactions guarantee consistency.
ACID
- Atomicity → All or nothing
- Consistency → Valid state transitions
- Isolation → Concurrent safety
- Durability → Persisted after commit
BEGIN;
UPDATE accounts
SET balance = balance - 100
WHERE id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE id = 2;
COMMIT;
If the second update fails, the first rolls back.
---Redis & Caching
Redis is an in-memory data store commonly used for:
- Caching
- Pub/Sub
- Rate limiting
- Queues
- Session storage
Cache-Aside Pattern
async function getUser(id: string) {
const cached = await redis.get(`user:${id}`);
if (cached) {
return JSON.parse(cached);
}
const user = await db.users.findById(id);
await redis.set(
`user:${id}`,
JSON.stringify(user),
'EX',
300
);
return user;
}
---
The Thundering Herd Problem
Suppose a cache key expires.
10,000 requests suddenly hit the database simultaneously.
The database collapses.
Solutions
- Cache locking
- Jittered TTLs
- Stale-while-revalidate
- Probabilistic early expiration
Kafka & Distributed Messaging
Kafka is a distributed event streaming platform.
It is designed for:
- High throughput
- Fault tolerance
- Event-driven systems
- Asynchronous processing
Core Concepts
- Topic → Stream of events
- Partition → Parallelism unit
- Offset → Consumer position
- Consumer Group → Shared processing
Producer → Topic → Partition → Consumer Group
---
At-Least-Once Delivery
Kafka may redeliver messages after failures.
Consumers MUST be idempotent.
INSERT INTO payments(idempotency_key)
VALUES ('abc123')
ON CONFLICT DO NOTHING;
This prevents duplicate processing.
---Background Jobs with BullMQ
Heavy or delayed work should not block HTTP requests.
Use queues.
await queue.add(
'send-email',
{
to: 'user@example.com'
},
{
attempts: 5,
backoff: {
type: 'exponential',
delay: 1000
}
}
);
Typical background tasks:
- Email sending
- Video processing
- Image compression
- PDF generation
- Analytics aggregation
Authentication & JWT
Authentication answers:
Who are you?
Authorization answers:
What can you do?
JWT Structure
Header.Payload.Signature
JWTs are signed, not encrypted.
The JWT Revocation Problem
JWTs are stateless.
If a user logs out, the token still works until expiry.
Solutions
- Short access token lifetime
- Refresh token rotation
- Redis blocklists
- Opaque session tokens
Rate Limiting
Rate limiting protects systems from abuse and overload.
Token Bucket
Requests consume tokens.
Tokens refill gradually over time.
Capacity: 100 tokens
Refill: 10 tokens/sec
If the bucket empties, requests are rejected.
---WebSockets & Real-Time Systems
HTTP is request-response.
WebSockets enable persistent bidirectional communication.
Handshake
Upgrade: websocket
Connection: Upgrade
The server replies:
101 Switching Protocols
A persistent TCP connection is established.
---Scaling WebSockets
In-memory pub/sub fails across multiple servers.
Solution
Redis Pub/Sub.
Server A → Redis → Server B
This enables horizontal scaling.
---FFmpeg & Media Pipelines
Media processing is CPU-intensive and asynchronous.
Transcoding vs Transmuxing
Transcoding
- Re-encodes video
- Changes codec
- CPU expensive
Transmuxing
- Changes container only
- No quality loss
- Very fast
HLS Streaming
Large videos are split into segments.
video.m3u8
segment1.ts
segment2.ts
segment3.ts
The player adapts quality dynamically based on bandwidth.
---System Design Thinking
System design interviews evaluate reasoning, not memorization.
What Strong Engineers Do
- Ask clarifying questions
- Estimate scale
- Identify bottlenecks
- Discuss trade-offs
- Consider failure modes
- Design for observability
Designing Uber
The challenge is not maps.
The challenge is coordinating millions of moving entities in real time.
Core Components
- Geo-search for nearby drivers
- WebSocket location streams
- Trip state machine
- Surge pricing engine
- Payment idempotency
- Kafka event streams
Important Trade-Off
For driver locations:
Availability > Consistency
Slightly stale location data is acceptable.
Total outage is not.
---Observability
Production systems fail.
Observability determines how quickly you recover.
The Three Pillars
1. Logs
{
"level": "error",
"requestId": "abc123",
"message": "Payment failed"
}
2. Metrics
- Latency
- Error rate
- CPU
- Memory
3. Tracing
Distributed tracing follows requests across services.
API Gateway
→ Auth Service
→ Payment Service
→ Notification Service
Without tracing, debugging distributed systems becomes nearly impossible.
---Graceful Shutdown
Containers and orchestration systems terminate processes constantly.
A production backend must shut down safely.
process.on('SIGTERM', async () => {
await server.stopAcceptingConnections();
await queue.close();
await database.close();
process.exit(0);
});
Without graceful shutdown:
- Requests are dropped
- Jobs are lost
- Data becomes inconsistent
Final Thought
Backend engineering is ultimately about building systems that continue working under stress, failure, scale, concurrency, and unpredictable real-world conditions.
Frameworks change.
Languages evolve.
Infrastructure shifts.
But the core engineering principles remain constant:
- Design for failure
- Understand trade-offs
- Measure bottlenecks
- Prefer simplicity
- Build observable systems
- Optimize only after measurement
The best backend engineers are not the ones who memorize technologies. They are the ones who understand the reasoning beneath them.
That is what separates someone who can build APIs from someone who can build systems.


