The Backend Questions That Stumped Me at Meta, Uber, and Stripe
After 300+ backend interviews and building systems that handle millions of requests, I've learned that interviews aren't about memorizing HTTP status codes—they're about demonstrating you can design resilient, scalable systems.
My worst backend interview was at a unicorn startup. The interviewer asked, "Design an API for a high-traffic social media feed." I jumped straight into endpoint design. Big mistake. They interrupted: "That's fine, but how do you handle 10 million users? What about caching? Database sharding? Rate limiting?"
That moment taught me backend interviews aren't about crafting perfect JSON responses. They're about thinking through the entire system—from the first HTTP request to the database query, from caching strategies to failover mechanisms. The best backend engineers don't just write APIs; they architect systems that don't break under pressure.
This guide covers 40 questions that separate junior backend developers from senior engineers. Each answer reflects real-world experience building systems at scale, complete with trade-offs, gotchas, and the reasoning that interviewers want to hear.
What Backend Interviewers Evaluate
- API Design: RESTful principles, versioning, error handling, documentation
- Database Mastery: SQL optimization, NoSQL trade-offs, transaction handling
- System Architecture: Microservices, caching layers, message queues
- Performance & Scale: Bottleneck identification, monitoring, optimization
- Security Mindset: Authentication, authorization, data protection, threat modeling
REST APIs & Web Services (Questions 1-10)
1. What are the key principles of RESTful API design?
Tests fundamental understanding of REST architecture
Answer:
- Stateless: Each request contains all information needed to process it
- Resource-based URLs: Use nouns, not verbs (/users/123, not /getUser/123)
- HTTP Methods: GET (read), POST (create), PUT (update), DELETE (remove)
- Standard Status Codes: 200 (success), 201 (created), 400 (bad request), 404 (not found)
- Uniform Interface: Consistent data formats and naming conventions
- HATEOAS: Include links to related resources in responses
2. How do you handle API versioning?
Tests understanding of backward compatibility and API evolution
Answer:
URL Versioning: /v1/users, /v2/users (most common, clear and cacheable)
Header Versioning: Accept: application/vnd.api+json;version=1
Query Parameter: /users?version=1 (simple but can clutter URLs)
# Best practice: URL versioning with semantic versioning
GET /v2/users/123
Accept: application/json
Strategy: Maintain 2-3 versions maximum, deprecate gracefully with clear timelines.
3. Explain the difference between PUT and PATCH.
Answer:
- PUT: Idempotent, replaces entire resource. Send full object even if updating one field.
- PATCH: Partial update, send only changed fields. More network efficient.
- Idempotency: Both should be idempotent (same result if called multiple times)
- Use PUT for: Complete resource replacement
- Use PATCH for: Partial updates, especially for large resources
4. How do you implement API rate limiting?
Answer:
Token Bucket: Refill tokens at fixed rate, consume on requests
Sliding Window: Track requests in rolling time window
Fixed Window: Simple counter reset at intervals
# Implementation with Redis
INCR user:123:requests:2026-01-23-14
EXPIRE user:123:requests:2026-01-23-14 3600
# Return 429 if count > limit
Headers: Include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
5. What's the difference between authentication and authorization?
Answer:
- Authentication: "Who are you?" - Verifying identity (login, JWT token validation)
- Authorization: "What can you do?" - Checking permissions (roles, RBAC)
- Example: JWT token authenticates user, RBAC authorizes specific actions
- Security principle: Always authenticate first, then authorize specific actions
- Implementation: Middleware handles auth, business logic handles authorization
6. How do you handle API errors and status codes?
Answer:
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 (Validation Error)
5xx Server Errors: 500 (Internal Error), 502 (Bad Gateway), 503 (Service Unavailable)
# Consistent error response format
{
"error": "VALIDATION_FAILED",
"message": "Invalid email format",
"details": [{"field": "email", "code": "INVALID_FORMAT"}
}
7. What is CORS and how do you configure it?
Answer:
CORS (Cross-Origin Resource Sharing): Mechanism allowing web pages to access resources from different domains.
Same-Origin Policy: Browsers block cross-origin requests by default for security
Preflight Request: OPTIONS request for complex requests (POST with JSON)
# Express.js CORS configuration
app.use(cors({
origin: ['https://myapp.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE']
}))
8. How do you implement pagination in APIs?
Answer:
Offset-based: ?page=2&limit=20 (simple but inefficient for large datasets)
Cursor-based: ?after=eyJpZCI6MTIzfQ&limit=20 (better performance, consistent results)
# Cursor-based pagination response
{
"data": [...],
"pagination": {
"has_more": true,
"next_cursor": "eyJpZCI6MTQ1fQ"
}
}
Headers: Include Link header for navigation (GitHub style)
9. What are webhooks and how do you implement them securely?
Answer:
Webhooks: HTTP callbacks triggered by events, enabling real-time communication
Security measures:
- HTTPS only
- Signature verification (HMAC-SHA256)
- Timestamp validation (prevent replay attacks)
- IP whitelisting when possible
# Webhook signature verification
const signature = req.headers['x-webhook-signature']
const payload = JSON.stringify(req.body)
const expected = crypto.createHmac('sha256', secret)
.update(payload).digest('hex')
10. How do you document APIs effectively?
Answer:
- OpenAPI/Swagger: Industry standard, interactive documentation
- Include examples: Request/response samples for all endpoints
- Error scenarios: Document all possible error responses
- Authentication: Clear auth flow documentation
- Rate limits: Document limits and headers
- SDKs: Provide client libraries for popular languages
- Changelog: Track API changes and deprecations
Database Design & Optimization (Questions 11-20)
11. Explain database normalization and when you might denormalize.
Tests database design fundamentals and trade-off understanding
Answer:
Normalization: Reducing data redundancy by organizing into separate tables
- 1NF: Atomic values, no repeating groups
- 2NF: No partial dependencies on composite keys
- 3NF: No transitive dependencies
When to denormalize: Read-heavy workloads, complex joins affecting performance, data warehousing scenarios
Trade-offs: Normalization reduces storage and update complexity; denormalization improves read performance
12. How do you optimize slow database queries?
Answer:
- Analyze query execution plan (EXPLAIN in PostgreSQL/MySQL)
- Add appropriate indexes on WHERE, JOIN, ORDER BY columns
- Avoid SELECT * - only fetch needed columns
- Optimize WHERE clauses - put most selective conditions first
- Consider query restructuring - break complex queries into simpler ones
- Use query caching for repeated queries
# Example optimization
-- Slow: table scan
SELECT * FROM users WHERE email = 'user@example.com'
-- Fast: with index on email column
CREATE INDEX idx_users_email ON users(email)
13. What's the difference between SQL and NoSQL databases?
Answer:
SQL Databases:
- • ACID compliance
- • Fixed schema
- • Complex relationships
- • Strong consistency
- • Vertical scaling
- • Examples: PostgreSQL, MySQL
NoSQL Databases:
- • Flexible schema
- • Horizontal scaling
- • Eventual consistency
- • High performance reads
- • Document/Key-value storage
- • Examples: MongoDB, Redis, DynamoDB
When to use: SQL for complex transactions, NoSQL for high-scale, flexible data
14. Explain database transactions and ACID properties.
Answer:
ACID Properties:
- Atomicity: All operations succeed or all fail (no partial updates)
- Consistency: Database remains in valid state after transaction
- Isolation: Concurrent transactions don't interfere with each other
- Durability: Committed changes persist even after system failure
# Example transaction
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
15. How do you handle database connection pooling?
Answer:
Connection pooling: Reuse database connections to reduce overhead
Benefits: Faster response times, reduced resource usage, better concurrency control
Configuration considerations:
- Pool size: Balance between resource usage and concurrency
- Connection timeout: How long to wait for available connection
- Idle timeout: When to close unused connections
- Health checks: Validate connections before use
# Example with Node.js pg-pool
const pool = new Pool({
max: 20, // maximum pool size
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000
})
16. What are database indexes and how do they work?
Answer:
Indexes: Data structures that improve query performance by creating shortcuts to data
Types:
- B-tree: Default, good for equality and range queries
- Hash: Fast for equality lookups only
- Bitmap: Efficient for low-cardinality data
- Partial: Index subset of rows based on condition
Trade-offs: Faster reads vs slower writes, additional storage overhead
Best practices: Index frequently queried columns, avoid over-indexing, monitor index usage
17. How do you implement database migrations safely?
Answer:
Safe migration practices:
- Backward compatibility: New code works with old schema
- Forward compatibility: Old code works with new schema
- Incremental changes: Small, reversible migrations
- Test on staging: Identical environment testing
- Rollback plan: Always have a rollback strategy
- Zero-downtime: Avoid locking operations during peak hours
# Example safe column addition
-- Step 1: Add nullable column
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
-- Step 2: Deploy code that uses new column
-- Step 3: Backfill data if needed
-- Step 4: Make column NOT NULL if required
18. What is database sharding and when would you use it?
Answer:
Sharding: Horizontal partitioning of data across multiple databases
When to shard: Single database can't handle load, data size exceeds single machine capacity
Sharding strategies:
- Range-based: Partition by data range (user_id 1-1000, 1001-2000)
- Hash-based: Use hash function on key (user_id % 4)
- Directory-based: Lookup service maps keys to shards
Challenges: Cross-shard queries, rebalancing, increased complexity
Alternatives: Read replicas, vertical partitioning, caching layers
19. Explain the CAP theorem and its implications.
Answer:
CAP Theorem: You can only guarantee 2 of 3 properties in distributed systems:
- Consistency: All nodes see same data at same time
- Availability: System remains operational
- Partition tolerance: System continues despite network failures
Real-world examples:
- CA: Traditional RDBMS (not partition tolerant)
- CP: MongoDB, Redis (sacrifice availability for consistency)
- AP: DynamoDB, Cassandra (eventual consistency)
Practical implication: In network partitions, choose consistency or availability
20. How do you backup and recover databases effectively?
Answer:
Backup strategies:
- Full backups: Complete database snapshot (weekly)
- Incremental: Only changed data since last backup (daily)
- Point-in-time: Transaction log backups for specific recovery points
- Hot vs Cold: Online backups vs offline with downtime
3-2-1 Rule: 3 copies of data, 2 different media types, 1 offsite
Recovery considerations: RTO (Recovery Time Objective), RPO (Recovery Point Objective)
Testing: Regularly test backup restoration process
Caching & Performance (Questions 21-27)
21. Explain different caching strategies and when to use them.
Tests understanding of caching patterns and trade-offs
Answer:
Cache-Aside (Lazy Loading): Application manages cache
• Read: Check cache → if miss, fetch from DB and cache
• Write: Update DB, invalidate cache
Write-Through: Write to cache and DB simultaneously
• Consistent but slower writes
Write-Behind (Write-Back): Write to cache first, DB later
• Faster writes but risk of data loss
# Cache-aside example
data = cache.get(key)
if (!data) {
data = database.get(key)
cache.set(key, data, ttl)
}
22. How do you handle cache invalidation?
Answer:
"There are only two hard things in Computer Science: cache invalidation and naming things"
Strategies:
- TTL (Time To Live): Automatic expiration after fixed time
- Event-based: Invalidate on data updates (pub/sub pattern)
- Tag-based: Group related cache entries with tags
- Manual: Explicit invalidation in business logic
# Event-based invalidation with Redis
// On user update
await updateUser(userId, data)
await cache.del(`user:${ userId }`)
await pubsub.publish('user:updated', userId)
23. What's the difference between Redis and Memcached?
Answer:
Redis:
- • Rich data structures (sets, hashes, lists)
- • Persistence options (RDB, AOF)
- • Pub/Sub messaging
- • Lua scripting
- • Single-threaded
- • Built-in replication
Memcached:
- • Simple key-value storage
- • No persistence
- • Multi-threaded
- • Lower memory overhead
- • Simpler architecture
- • Better for simple caching
Choose Redis for: Complex data structures, persistence. Memcached for: Simple, high-performance caching.
24. How do you implement distributed caching?
Answer:
Distributed caching patterns:
- Shared Cache: Single cache cluster accessed by all app instances
- Replicated Cache: Each app instance has copy of cache data
- Partitioned Cache: Cache data distributed across multiple nodes
Consistent hashing: Distribute keys evenly, minimize rehashing when nodes change
# Consistent hashing example
const hash = hashFunction(key)
const nodeIndex = hash % nodes.length
const cacheNode = nodes[nodeIndex]
Challenges: Network latency, cache coherency, failover handling
25. What is CDN and how does it improve performance?
Answer:
CDN (Content Delivery Network): Geographically distributed servers that cache content closer to users
Benefits:
- • Reduced latency (geographic proximity)
- • Lower bandwidth costs
- • Improved availability and redundancy
- • DDoS protection
- • Reduced origin server load
Best practices:
- • Set appropriate cache headers (Cache-Control, ETag)
- • Use versioned URLs for cache busting
- • Optimize cache hit ratio
- • Monitor cache performance metrics
26. How do you measure and improve API performance?
Answer:
Key metrics:
- Latency: Response time (p50, p95, p99 percentiles)
- Throughput: Requests per second
- Error rate: Percentage of failed requests
- Resource utilization: CPU, memory, database connections
Optimization techniques:
- • Database query optimization
- • Implement caching layers
- • Use compression (gzip)
- • Connection pooling
- • Async processing for heavy operations
- • Load balancing and horizontal scaling
27. What are the common causes of memory leaks in backend applications?
Answer:
Common causes:
- Unclosed resources: Database connections, file handles, HTTP connections
- Event listeners: Not removing event handlers
- Global variables: Accumulating data in global scope
- Circular references: Objects referencing each other
- Caches without limits: Unbounded cache growth
# Prevention example
try {
const connection = await pool.connect()
// ... use connection
} finally {
connection?.release() // Always cleanup
}
Detection: Memory monitoring, profiling tools, heap dumps
Security & Authentication (Questions 28-33)
28. How do you implement JWT authentication securely?
Tests security best practices for token-based authentication
Answer:
JWT Security best practices:
- Short expiration times: 15-30 minutes for access tokens
- Use refresh tokens: Longer-lived, revocable tokens
- Strong secret keys: Use cryptographically secure random keys
- HTTPS only: Never send JWTs over HTTP
- Secure storage: HttpOnly cookies, not localStorage
- Validate claims: Check issuer, audience, expiration
# Secure JWT implementation
const token = jwt.sign(
{ userId, email },
process.env.JWT_SECRET,
{ expiresIn: '15m', issuer: 'myapp' }
)
29. What are the common security vulnerabilities in web applications?
Answer (OWASP Top 10):
30. How do you protect against SQL injection attacks?
Answer:
Prevention techniques:
- Parameterized queries: Use prepared statements with parameters
- Stored procedures: When properly implemented
- Input validation: Whitelist allowed characters
- Principle of least privilege: Database user with minimal permissions
- Escape special characters: When parameterized queries aren't possible
# Vulnerable code
const query = `SELECT * FROM users WHERE email = '${ email }'`
# Secure code
const query = 'SELECT * FROM users WHERE email = $1'
const result = await client.query(query, [email])
31. How do you implement secure password storage?
Answer:
Password hashing best practices:
- Use bcrypt, scrypt, or Argon2: Slow hashing algorithms with salt
- Never store plaintext: Hash passwords before storage
- Unique salts: Different salt per password
- High work factor: Adjust cost parameter for security vs performance
- Password policies: Minimum length, complexity requirements
# Secure password hashing
const bcrypt = require('bcrypt')
const saltRounds = 12
// Hash password
const hashedPassword = await bcrypt.hash(password, saltRounds)
// Verify password
const isValid = await bcrypt.compare(password, hashedPassword)
32. What is CORS and how do you configure it securely?
Answer:
CORS (Cross-Origin Resource Sharing): Browser security feature that restricts cross-origin HTTP requests
Secure configuration:
- Specific origins: Never use * in production
- Limit methods: Only allow necessary HTTP methods
- Control headers: Restrict allowed and exposed headers
- Credentials handling: Be careful with Access-Control-Allow-Credentials
# Secure CORS configuration
app.use(cors({
origin: ['https://myapp.com', 'https://admin.myapp.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
optionsSuccessStatus: 200
}))
33. How do you implement role-based access control (RBAC)?
Answer:
RBAC Components:
- Users: Individual accounts
- Roles: Groups of permissions (admin, editor, viewer)
- Permissions: Specific actions (read, write, delete)
- Resources: Objects being protected
# RBAC middleware example
const requireRole = (roles) => async (req, res, next) => {
const userRoles = await getUserRoles(req.user.id)
const hasPermission = roles.some(role =>
userRoles.includes(role))
if (!hasPermission) return res.status(403).json(...)
next()
}
Best practices: Principle of least privilege, role hierarchies, audit logs
Microservices & System Design (Questions 34-40)
34. What are microservices and when should you use them?
Tests understanding of distributed architecture patterns
Answer:
Microservices: Architectural pattern where applications are built as independent, loosely coupled services
Benefits:
- • Independent deployment and scaling
- • Technology diversity
- • Team autonomy
- • Fault isolation
Challenges:
- • Distributed system complexity
- • Network latency and reliability
- • Data consistency
- • Monitoring and debugging
When to use: Large teams, complex domains, need for different scaling patterns. Start with monolith for small teams/simple domains.
35. How do you handle communication between microservices?
Answer:
Synchronous communication:
- REST APIs: HTTP-based, stateless, widely supported
- GraphQL: Single endpoint, flexible queries
- gRPC: High-performance, type-safe, HTTP/2
Asynchronous communication:
- Message queues: RabbitMQ, Apache Kafka, AWS SQS
- Event streaming: Pub/sub patterns, event sourcing
- Service mesh: Istio, Linkerd for service-to-service communication
Best practices: Circuit breakers, retries with backoff, timeouts, idempotency
36. What is the Circuit Breaker pattern?
Answer:
Circuit Breaker: Design pattern that prevents cascading failures in distributed systems
States:
- Closed: Normal operation, requests pass through
- Open: Failures detected, requests fail fast
- Half-Open: Limited requests to test if service recovered
# Circuit breaker implementation concept
if (circuitBreaker.state === 'OPEN') {
throw new Error('Circuit breaker is OPEN')
}
try {
const result = await externalService.call()
circuitBreaker.recordSuccess()
return result
} catch (error) {
circuitBreaker.recordFailure()
throw error
}
Benefits: Prevents cascade failures, improves system resilience, provides fallback mechanisms
37. How do you handle distributed transactions?
Answer:
Two-Phase Commit (2PC): Coordinator ensures all participants commit or abort
- • Phase 1: Vote to commit/abort
- • Phase 2: Commit or abort based on votes
- • Problems: Blocking protocol, single point of failure
Saga Pattern (Preferred): Sequence of local transactions with compensating actions
- Choreography: Events trigger next steps
- Orchestration: Central coordinator manages flow
# Saga example: Order processing
1. Reserve inventory
2. Charge payment
3. Ship order
# If payment fails: Release inventory
Eventual Consistency: Accept temporary inconsistency for better availability
38. What are message queues and when do you use them?
Answer:
Message Queues: Asynchronous communication mechanism between services
Benefits:
- • Decoupling of services
- • Reliability (persistence, acknowledgments)
- • Scalability (handle traffic spikes)
- • Fault tolerance
Use cases:
- • Background job processing
- • Event-driven architecture
- • Load balancing across workers
- • Handling traffic spikes
Popular solutions: RabbitMQ (feature-rich), Apache Kafka (high-throughput), AWS SQS (managed), Redis Pub/Sub (simple)
39. How do you implement service discovery in microservices?
Answer:
Service Discovery: Mechanism for services to find and communicate with each other
Patterns:
- Client-side discovery: Client queries service registry (Eureka, Consul)
- Server-side discovery: Load balancer handles discovery (AWS ELB, Kubernetes)
Service Registry: Database of available service instances
- • Health checks
- • Load balancing
- • Failover handling
# Kubernetes service discovery
http://user-service.default.svc.cluster.local:8080
# Consul service discovery
const services = await consul.health.service('user-service')
40. How do you design a system that can handle millions of users?
Answer:
Scalability principles:
Horizontal scaling:
- • Load balancers
- • Stateless services
- • Auto-scaling groups
- • Database sharding
Performance optimization:
- • Multi-level caching (CDN, Redis, in-memory)
- • Database optimization (indexes, read replicas)
- • Async processing (queues, workers)
- • Connection pooling
Architecture patterns:
- • Microservices for independent scaling
- • Event-driven architecture
- • CQRS (Command Query Responsibility Segregation)
- • Data partitioning strategies
Monitoring & reliability: Comprehensive logging, metrics, alerting, circuit breakers, graceful degradation
The Backend Engineering Mindset
After years of backend development and hundreds of interviews, I've learned that the best backend engineers share common traits:
✓ What Great Engineers Show:
- • Systems thinking - understanding how components interact
- • Performance awareness - always considering scalability
- • Security mindset - building defense in depth
- • Reliability focus - designing for failure scenarios
- • Trade-off analysis - explaining why you chose approach A over B
- • Monitoring culture - you can't manage what you don't measure
❌ Common Interview Mistakes:
- • Jumping to solutions without understanding requirements
- • Ignoring error handling and edge cases
- • Over-engineering simple problems
- • Not considering operational aspects (monitoring, debugging)
- • Focusing only on happy path scenarios
- • Not discussing scalability and performance implications
The most successful backend engineers I know understand that building great systems is about more than just writing code. It's about creating reliable, secure, and scalable foundations that enable businesses to grow. Master these concepts, practice explaining your reasoning, and remember that every great backend system started with someone asking "How do we make this work at scale?"
