Tags: backend, api, database, system-design, concurrency, architecture
Level: intermediate
TL;DR: Core backend concepts, architecture patterns, and ready answers for technical interview rounds.
REST APIs rely on stateless requests, standard HTTP verbs, and predictable status codes.
Idempotency ensures identical repeated requests yield the same server state (e.g., PUT, DELETE).
Relational databases guarantee ACID properties for strict transactional consistency.
NoSQL databases offer flexible schemas and horizontal scalability optimized for specific access patterns.
Indexing improves query read latency but introduces overhead on write and update operations.
Connection pooling reuses open database connections to avoid TCP handshake and authentication overhead.
Caching strategies like cache-aside reduce database load but require robust invalidation policies.
Horizontal scaling adds independent nodes, requiring stateless services and load balancers.
Message queues decouple producers and consumers, enabling asynchronous processing and traffic smoothing.
Microservices trade operational simplicity for independent deployability and domain boundary isolation.
Q: How do you ensure idempotency in API endpoints that process payments or resource creation?R: I accept an Idempotency-Key header from the client and store it alongside the request status and result in Redis or the database, returning cached results on duplicates.
Q: How do you approach database schema migrations in zero-downtime production deployments?R: I use expand-and-contract migrations, ensuring the codebase supports both old and new columns before deprecating or dropping old schema elements.
Q: What is the difference between optimistic and pessimistic locking?R: I use optimistic locking with version fields when collisions are rare, and pessimistic locking via database row locks when collision frequency is high or data sensitivity is critical.
Q: How do you resolve performance bottlenecks in slow SQL queries?R: I analyze execution plans using EXPLAIN ANALYZE, check missing or unused indexes, rewrite N+1 queries, and add targeted caching where appropriate.
Q: How do you handle distributed transactions across independent backend services?R: I avoid two-phase commits in favor of the Saga pattern with compensating actions or event-driven choreography using transactional outboxes.
Generating an N+1 query pattern by fetching related relational records inside application loops.
Storing unhashed or weakly hashed user passwords instead of modern key-derivation functions like Argon2 or bcrypt.
Omitting request timeouts and circuit breakers when calling external downstream microservices.
Relying entirely on in-memory state in web applications, breaking horizontal auto-scaling capabilities.
Performing long-running compute or external API calls synchronously inside an incoming HTTP request cycle.
```-- Check query execution plan and cost in PostgreSQL
EXPLAIN ANALYZE
SELECT id, email, created_at
FROM users
WHERE status = 'ACTIVE'
ORDER BY created_at DESC
LIMIT 50;
// Transactional outbox pattern structure
interface OutboxEvent {
id: string;
aggregateId: string;
eventType: string;
payload: Record<string, unknown>;
createdAt: Date;
publishedAt: Date | null;
}
```
[ ] Database isolation levels: Read Uncommitted, Read Committed, Repeatable Read, Serializable
[ ] CAP theorem trade-offs and eventual consistency models
[ ] Rate limiting algorithms: Token Bucket, Leaky Bucket, Sliding Window Log
[ ] Connection pool tuning parameters: max connections, idle timeouts, acquisition timeouts
[ ] Zero-downtime deployment patterns: Blue-Green and Canary