.png)
10 Backend Concepts Every Developer Should Know (With Real-World Examples)
Writing backend code that works is one thing. Writing backend systems that stay fast, stable, and scalable under real-world load is a completely different skill.
The gap between the two usually comes down to a handful of design and optimization patterns, the kind senior engineers apply almost automatically, and the kind that show up constantly in system design interviews.
Here are 10 of the most important ones, explained simply, with practical examples.
1. Batch Processing
The problem: Inserting or updating records one at a time means one database round-trip per record. Do that for 1,000 records, and you have made 1,000 expensive calls.
The fix: Batch processing groups multiple operations together and executes them in a single call.
How it works:
- Collect - gather multiple requests or records.
- Group - cluster similar operations together.
- Process - execute the entire batch in one go.
- Complete - return results once the batch finishes.
Example: Instead of inserting 1,000 records one row at a time, you insert all 1,000 in a single batch operation.
Common use cases: CSV imports, bulk inserts and updates, log processing, data migrations, and report generation.
Why it matters:
- Faster - fewer database calls means less overhead and lower latency.
- Efficient - better resource utilization and higher throughput.
- Scalable - handles large data volumes without breaking a sweat.
.png)
2. Database Connection Pooling
The problem: Opening a new database connection for every request is slow and expensive. Each connection requires a handshake, authentication, and teardown.
The fix: Keep a pool of ready-to-use connections that get reused across requests instead of created and destroyed each time.
How it works:
- Request - the app asks for a connection.
- Borrow - it grabs an available connection from the pool.
- Use - the connection executes queries.
- Return - the connection goes back to the pool for the next request.
Without pooling: Open -> Use -> Close, repeated for every request. Slow and resource-heavy.
With pooling: Connections are reused from a shared pool of idle, ready connections. Fast and efficient.
Why it matters:
- Faster response - no overhead of creating a connection every time.
- Better performance - more efficient use of database resources.
- More stable - prevents connection spikes and failures.
- Reusable - the same connection serves multiple requests over its lifetime.
.png)
3. Pagination and Sorting
The problem: Fetching thousands of records in a single API call increases response time and consumes more heap space on the application server.
The fix: Pagination breaks large result sets into smaller chunks. Sorting organizes those results based on a specific field.
How it works:
- Client requests a specific page (for example,
page=2, size=10). - Server fetches only the required records (for example, rows 11 to 20).
- Response returns the data along with pagination metadata.
- Client displays the current page to the user.
A typical paginated response looks like:
{
"data": ["...10 records..."],
"page": 2,
"size": 10,
"totalRecords": 1250,
"totalPages": 125
}Example API call:
GET /api/users?page=2&size=10&sort=age&order=ascpage-> which pagesize-> records per pagesort-> field to sort byorder-> ascending or descending
Why it matters:
- Faster response - smaller data chunks process and deliver quicker.
- Lower server load - consumes less memory and CPU.
- Better UX - organized, navigable data in manageable pages.
.png)
4. Event-Driven Architecture
The problem: In traditional synchronous systems, Service A calls Service B calls Service C directly. If one service goes down, the whole chain breaks. That is tight coupling.
The fix: Services communicate by publishing and consuming events through a message broker (like Kafka or RabbitMQ) instead of calling each other directly.
How it works:
- A publisher (for example, Order Service) publishes an event.
- The event broker stores the event until consumers are ready.
- Consumers (Email Service, Inventory Service, Notification Service) independently consume the event whenever they are ready.
Because events sit in the broker until consumed, a failure in one downstream service does not affect the others.
Traditional (API calls):
- Tight coupling between services.
- Failure in one service can break the whole flow.
- Real-time calls can introduce latency.
Event-driven (event bus):
- Loose coupling between services.
- Other services continue even if one fails.
- Better scalability and fault tolerance.
Common tools: Kafka (high-throughput distributed event streaming) and RabbitMQ (reliable message queuing and routing).
Why it matters: loose coupling, resilience, scalability, and flexibility to add new consumers without touching existing code.
.png)
5. API Rate Limiting
The problem: Without limits, a single client (or bot) can flood your API with requests, degrading performance for everyone else.
The fix: Rate limiting controls how many requests a client can make within a specific time window.
How it works:
- Client sends an API request.
- Rate limit check - the server checks if the request is within the allowed limit.
- Allow or block - the request proceeds if within limit, otherwise it is blocked.
- Response - the server sends the appropriate response (or a
429 Too Many Requests).
Example: A limit of 100 requests per minute means the 101st request within that minute gets rejected with a 429.
Common strategies:
- Fixed Window - limits requests within a fixed time block.
- Sliding Window - considers requests over a rolling window for smoother control.
- Token Bucket - allows bursts using tokens that refill over time.
- Leaky Bucket - processes requests at a constant rate, queuing the rest.
Where it is used: public APIs, authentication endpoints, payment gateways, search APIs, and email or SMS services.
Why it matters: prevents abuse, protects the server, ensures fair usage across users, and keeps the API reliable for everyone.
.png)
6. Optimistic Locking
The problem: When multiple users try to update the same record simultaneously, you can get race conditions. One update can silently overwrite another.
The fix: Optimistic locking assumes conflicts are rare. It checks a record's version before committing an update, rather than locking it upfront.
How it works:
- Read - read the record along with its version number.
- Modify - make changes locally.
- Validate - before updating, check if the version is still the same.
- Update - if the version matches, commit and increment the version. If not, the transaction fails.
Example: User A and User B both read a record at Version 5. User A updates first and version becomes 6. User B then tries to update, but the version check fails (expected 5, actual 6), preventing a silent overwrite.
Why it matters:
- Prevents race conditions without locking anything upfront.
- High performance - no locks held, better concurrency and throughput.
- Reduces server load - avoids unnecessary rollbacks.
- Ideal for read-heavy systems where conflicts are rare.
.png)
7. Pessimistic Locking
The problem: Sometimes conflicts are not rare, and letting transactions fail and retry is not enough. You need to guarantee no one else touches the record while you are working on it.
The fix: Pessimistic locking locks a record as soon as a transaction begins, blocking other transactions until the lock is released.
How it works:
- Request - a transaction requests a record.
- Lock - the database places an exclusive lock on it.
- Block others - other transactions must wait.
- Process - the transaction updates the record.
- Release - the lock is released after commit or rollback.
- Unblock - waiting transactions acquire the lock one by one.
Example: User A locks a record to update an account balance. User B tries to update the same record and must wait until User A completes the transaction.
Advantages: effectively prevents race conditions, ensures data consistency, simple to reason about, and works well for write-heavy operations.
Disadvantages: can cause blocking and wait times, reduces concurrency and throughput, may lead to deadlocks in complex scenarios, and adds latency under heavy load.
Optimistic vs pessimistic: choose optimistic locking for read-heavy systems with rare conflicts, and pessimistic locking for write-heavy systems where conflicts are common and consistency is critical.
.png)
8. Database Sharding
The problem: A single database can only scale so far vertically (bigger servers). At some point, you need horizontal scaling.
The fix: Sharding splits a large database into smaller, independent databases called shards, each storing a subset of the data.
How it works:
- Split - the large database is split based on a sharding key (for example, User ID).
- Distribute - data is distributed across multiple shards.
- Route - the application routes requests to the correct shard.
- Query - each shard handles its own queries independently.
- Combine - results are combined and returned if needed.
Example: A 1TB database split by User ID range. Shard 1 handles users 1 to 1,000,000, Shard 2 handles 1,000,001 to 2,000,000, and Shard 3 handles 2,000,001 and above.
Why it matters:
- Horizontal scaling - add more shards as data grows.
- Better performance - smaller datasets per shard mean faster queries.
- High availability - if one shard fails, others stay operational.
- Efficient data management - distributes load and storage.
.png)
9. Caching with Redis
The problem: Querying the database for the same data over and over (user sessions, product catalogs, leaderboards) wastes time and resources.
The fix: Caching stores frequently accessed data in-memory so it can be retrieved much faster than querying the database every time.
How it works:
- User request comes in.
- Check cache - the application checks if the data exists in Redis.
- Cache hit - if found, return data from Redis immediately.
- Cache miss - if not found, fetch from the database, then store it in Redis.
Common use cases: user sessions, page caching, leaderboards, product catalogs, notifications, and API response caching.
Why it matters:
- Ultra fast - in-memory access is measured in microseconds.
- Scalable - handles large request volumes with ease.
- Reduces DB load - less stress on the database and lower cost.
- Distributed - supports caching across multiple servers.
- Reliable - offers persistence options and rich data structures.
.png)
10. Consistent Hashing
The problem: In traditional hashing, adding or removing a server forces most keys to be remapped, causing disruptive redistribution in distributed systems.
The fix: Consistent hashing distributes requests across servers in a circular ring. When a server is added or removed, only a small portion of keys is redistributed.
How it works: Servers and keys are placed on a conceptual ring. Each key is mapped to the next server node in a clockwise direction.
Example - minimal redistribution:
- Initial setup: keys are distributed across Servers A, B, C, and D.
- Add Server E: only keys in E's range move to it.
- Remove Server B: only keys that belonged to B move to the next node.
Consistent hashing vs traditional hashing:
| Aspect | Traditional Hashing | Consistent Hashing |
|---|---|---|
| Adding a server | Most or all keys remapped | Only a small portion remapped |
| Removing a server | Most or all keys remapped | Only a small portion remapped |
| Distribution | Can be uneven | More uniform |
| Scalability | Poor | Excellent |
| Complexity | Simple but inefficient | Slightly complex but efficient |
Where it is used: distributed caches, load balancers, CDNs, databases, and peer-to-peer systems.
Why it matters: minimizes data movement when servers change, improves scalability and performance, and increases fault tolerance.
.png)
Bringing It All Together
Notice the pattern across all 10 concepts: every one of them is an optimization decision.
- Batch processing and connection pooling reduce wasted overhead.
- Pagination, sorting, and caching reduce unnecessary load.
- Rate limiting and locking strategies protect data integrity and fairness under concurrency.
- Sharding and consistent hashing solve scaling before it becomes a crisis.
None of these are exotic. They are foundational patterns behind almost every production-grade backend system. The engineers who stand out are not the ones who know the most frameworks. They are the ones who understand why these patterns exist and when to use each one.
Master these one at a time, and you will not just write backend code that works. You will build systems that scale.
Found this useful? Follow @husnainizhar for more deep dives on system design and backend engineering.
.png)