Chapitre 22
22 - Simulations d'Entretiens Backend
22 - Simulations d'Entretiens Backend
Chapitre 22 : Simulations d'Entretiens Backend
Introduction
Ce chapitre propose 60 questions d'entretien back-end (30 comportementales, 30 techniques) ainsi que des coding challenges et des études de cas complètes de system design.
Partie 1 : Questions Comportementales (30)
1.1 Parle-moi de toi
Question : "Parle-moi de ton parcours et de ce qui t'a amené au développement backend."
Structure de réponse :
- Présent (30%) : Rôle actuel, responsabilités
- Passé (50%) : Progression de carrière, projets marquants
- Futur (20%) : Objectifs, pourquoi ce poste
Exemple :
"Je suis ingénieur backend avec 5 ans d'expérience spécialisé en Node.js. J'ai commencé en full-stack, mais j'ai rapidement été attiré par les défis de conception d'API et de systèmes distribués. Chez [entreprise], j'ai mené la migration d'un monolithe vers 12 microservices, réduisant le temps de déploiement de 2 heures à 5 minutes. Je cherche aujourd'hui un poste où je pourrai approfondir l'architecture événementielle et le cloud native."
1.2 Projet le plus complexe
Question : "Quel est le projet le plus complexe sur lequel tu as travaillé ?"
Structure STAR :
- Situation : Contexte
- Task : Objectif
- Action : Ce que vous avez fait
- Result : Résultat mesurable
Exemple :
"Situation : Notre plateforme e-commerce avait des pannes récurrentes pendant les soldes, le monolithe ne tenait pas la charge. Tâche : Repenser l'architecture pour supporter 5x le trafic normal sans downtime. Action : J'ai conçu une architecture microservices avec Kafka pour le découplage, Redis pour le cache, et Kubernetes pour l'orchestration. J'ai implémenté le circuit breaker et le graceful degradation. Résultat : Zéro downtime pendant le Black Friday suivant, temps de réponse -60%, et l'équipe peut déployer indépendamment chaque service."
1.3 Conflit technique
Question : "Raconte un conflit technique que tu as résolu avec un collègue."
Clé : Montrer respect et recherche de la meilleure solution, pas de "gagner".
"Un collègue voulait utiliser MongoDB pour son flexibilité, je préférais PostgreSQL pour la cohérence. Nous avons fait un proof-of-concept de 2 jours chacun, puis benchmarké. MongoDB était 2x plus rapide en écriture, mais PostgreSQL gérait mieux les requêtes complexes. Nous avons choisi PostgreSQL pour le service principal avec une couche de cache Redis. Le collègue a apprécié l'approche data-driven."
1.4 Échec et apprentissage
Question : "Parle-moi d'un échec professionnel et ce que tu en as appris."
"J'ai déployé une migration de base de données sans rollback planifié. La migration a corrompu des données en production. J'ai dû restaurer depuis un backup de 6 heures, perdant les transactions récentes. J'ai appris à toujours avoir un rollback planifié, tester les migrations sur staging, et les exécuter en multiples petites étapes plutôt qu'un gros changement."
1.5 Feedback difficile
Question : "Donne-moi un exemple de feedback difficile que tu as reçu et comment tu y as réagi."
"Un senior m'a dit que mon code était trop 'clever' et pas assez lisible. J'ai réalisé que je privilégiais les solutions élégantes aux solutions maintenables. J'ai adopté le principe KISS et demande systématiquement des code reviews sur la lisibilité. Aujourd'hui, je fais des revues en me demandant 'un junior pourrait-il comprendre ce code ?'"
1.6 Priorisation
Question : "Comment gères-tu des priorités conflictuelles ?"
"Je catégorise par impact et urgence. Quand deux features importantes conflictent, je présente les options au product manager avec les trade-offs techniques : la feature A débloque 3 autres fonctionnalités mais prend 2 semaines, la B rapporte immédiatement mais est un cul-de-sac technique. Je documente aussi la dette technique pour qu'elle soit visible."
1.7 Mentorat
Question : "As-tu déjà mentored des développeurs juniors ?"
"Oui, j'ai encadré 3 juniors. Mon approche est de les laisser coder d'abord, puis de faire une code review détaillée. Je préfère expliquer le 'pourquoi' plus que le 'comment'. Je les encourage à poser des questions et à casser des choses dans leur environnement de dev. Résultat : un junior était autonome sur son premier microservice après 3 mois."
1.8 Technologie préférée
Question : "Quelle est ta technologie préférée et pourquoi ?"
"Redis. C'est incroyablement polyvalent : cache, session store, rate limiter, message broker, geospatial. Sa simplicité combinée à sa puissance en fait l'outil le plus impactant que j'utilise. Les structures de données (sorted sets, hyperloglog, streams) permettent des solutions élégantes à des problèmes complexes."
1.9 Veille technologique
Question : "Comment restes-tu à jour technologiquement ?"
"Je suis plusieurs newsletters (Node Weekly, DB Weekly), des blogs engineering (Netflix, Uber), et des conférences KubeCon. J'expérimente sur des side projects. Actuellement, j'explore WebAssembly côté serveur et les bases de données orientées colonnes."
1.10 Code review
Question : "À quoi ressemble une bonne code review pour toi ?"
"Une bonne review est constructive et bienveillante. Je vérifie : la logique métier, les edge cases, la testabilité, la performance, et l'architecture. Je commente moins sur le style (lint automatisé). Je pose des questions plutôt que de donner des ordres. 'As-tu considéré...' plutôt que 'Tu devrais...'"
1.11 Dette technique
Question : "Comment gères-tu la dette technique ?"
"Je la documente avec ticket Jira et étiquette 'tech-debt'. Je propose des 'fix-it fridays' ou des sprints dédiés. Je montre l'impact : 'cette dette ajoute 2 jours par mois de maintenance'. Le ratio idéal : 80% features, 20% réduction de dette."
1.12 Travail en équipe
Question : "Décris ton équipe idéale."
"Une équipe de 4-6 personnes avec des compétences complémentaires, qui pratique le pair programming et les reviews. Transparente sur les difficultés, avec un PM qui protège l'équipe. Des daily standups courts et des rétros honnêtes."
1.13 Décision impopulaire
Question : "Quand as-tu dû prendre une décision impopulaire ?"
"J'ai désactivé une fonctionnalité populaire car elle causait des pannes récurrentes. Les utilisateurs étaient mécontents, mais la stabilité était prioritaire. Nous l'avons réécrite proprement et redéployée 2 semaines plus tard. L'équipe a compris que parfois il faut reculer pour mieux avancer."
1.14 Apprentissage rapide
Question : "Comment apprends-tu une nouvelle technologie rapidement ?"
"1. Je construis un petit projet pour avoir un contexte. 2. Je lis la documentation officielle. 3. Je regarde des conférences sur le sujet. 4. Je contribue à un projet open-source pour valider ma compréhension. Par exemple, j'ai appris Kafka en 2 semaines en construisant un pipeline de traitement d'événements."
1.15 Amélioration processus
Question : "Quel processus as-tu amélioré dans ton équipe ?"
"Notre déploiement manuel prenait 30 minutes et était sujet aux erreurs. J'ai automatisé avec GitHub Actions + Blue/Green deployment : passage de 30 min à 2 min, zéro erreur, et les déploiements peuvent être faits par n'importe qui."
1.16-1.30 Questions supplémentaires
16. Gestion du stress : "Comment gères-tu une production qui tombe à 3h du matin ?" → Rester calme, suivre le runbook, communiquer, post-mortem.
17. Autonomie : "Préfères-tu des spécifications détaillées ou de l'autonomie ?" → Autonomie cadrée : objectifs clairs, liberté d'exécution.
18. Feedback : "Comment donnes-tu du feedback négatif ?" → SBI model : Situation, Behavior, Impact.
19. Changement de scope : "Comment gères-tu un changement de scope en cours de sprint ?" → Évaluer l'impact, discuter des trade-offs, renégocier.
20. Culture d'entreprise : "Quelle culture d'entreprise recherches-tu ?" → Learning culture, psychological safety, engineering excellence.
21. Fail fast : "Que penses-tu de l'approche 'fail fast' ?" → Bonne pour l'exploration, mauvaise pour la production. Échouer rapidement sur des idées, pas sur la fiabilité.
22. Documentation : "Comment documentes-tu ton code ?" → README pour le projet, JSDoc pour les API, ADR pour les décisions. Pas de commentaires qui répètent le code.
23. Testing : "Quel est ton approach du testing ?" → Pyramid testing : unitaires (70%), intégration (20%), e2e (10%). TDD pour la logique complexe.
24. Open source : "Contribues-tu à l'open source ?" → Oui, je contribue à [projet]. Cela m'aide à lire du code de qualité et à comprendre des architectures matures.
25. Remote work : "Comment restes-tu productif en remote ?" → Routine, espace dédié, async-first communication, over-communication.
26. Cross-team : "Comment travailles-tu avec d'autres équipes ?" → APIs claires, documentation, sync hebdomadaire, contrat d'interface.
27. Legacy code : "Comment abordes-tu une codebase legacy ?" → Strangler Fig pattern : ajouter des tests d'abord, extraire progressivement.
28. Performance : "Comment optimises-tu une API lente ?" → Mesurer (profiling), identifier le bottleneck, optimiser (cache, index, requêtes).
29. Innovation : "Comment introduis-tu une nouvelle techno dans l'équipe ?" → POC → présentation → adoption graduelle. Montrer, pas imposer.
30. Motivation : "Qu'est-ce qui te motive dans le développement backend ?" → Construire des systèmes fiables, scalables et élégants. L'impact de l'infrastructure invisible.
Partie 2 : Questions Techniques (30)
2.1 System Design (15 questions)
Q1 : Conception d'un URL Shortener
Problème : Concevoir un service comme bit.ly ou TinyURL.
Points clés :
- Génération d'IDs uniques (Base62, Snowflake)
- Redirections 301/302
- Cache Redis + CDN
- Analytics (clics, géo, referrer)
- Scalabilité : 100M URLs/mois, 10K req/s redirections
Discussion :
- Stockage : PostgreSQL (sharding par hash) ou Cassandra
- Cache : Redis, Write-through pour les URLs populaires
- CDN : CloudFront/CloudFlare pour les redirections
- Analytics : Kafka → Hadoop/ClickHouse en batch
Q2 : Conception d'un Chat en Temps Réel
Problème : Concevoir WhatsApp/Messenger.
Points clés :
- WebSocket persistent
- Gestion de présence
- Messages hors-ligne
- Chiffrement de bout en bout
- Groupes (jusqu'à 256)
Discussion :
- WebSocket avec heartbeat
- Cassandra pour le stockage des messages (partition par conversation_id)
- Redis pour les sessions et la présence
- APNS/FCM pour les notifications push
- Message sync à la reconnexion
Q3 : Conception d'un Système de Paiement
Problème : Concevoir Stripe ou un système de paiement.
Points clés :
- Idempotence (clé d'idempotence)
- Double-entry accounting
- Gestion des échecs et retours
- Conformité PCI DSS
- Anti-fraud
Discussion :
- État machine : initié → autorisé → capturé → complété
- Saga pattern pour les transactions distribuées
- Idempotency key dans Redis avec TTL
- Event sourcing pour le ledger
- Rate limiting par merchant
Q4 : Conception de News Feed
Problème : Concevoir le feed Facebook/LinkedIn.
Points clés :
- Fan-out on write vs read
- Ranking algorithm
- Content diversity
- Real-time updates
- Scroll infini
Discussion :
- Fan-out on write pour les amis (pré-calculé)
- Fan-out on read pour les célébrités
- ML ranking (affinity, recency, content type)
- Feed cache Redis (sharded par user_id)
- Kafka pour le fan-out asynchrone
Q5 : Conception d'un CDN
Problème : Concevoir un CDN comme CloudFront/CloudFlare.
Points clés :
- Edge servers géo-distribués
- Cache invalidation
- DDoS protection
- SSL termination
- Origin pull vs push
Discussion :
- DNS-based routing (Anycast)
- Reverse proxy cache (Varnish, Nginx, Squid)
- Cache invalidation : purge API, TTL, versioning
- DDoS : rate limiting, WAF, challenge (CloudFlare)
- Origin shielding pour réduire la charge
Q6 : Conception d'un Rate Limiter
Problème : Concevoir un rate limiter distribué (comme celui de Stripe/GitHub).
Points clés :
- Algorithme : Token bucket, Leaky bucket, Sliding window
- Distribution : Redis cluster
- Headers : X-RateLimit-*
- Différenciation par plan/API key
Discussion :
- Sliding window log vs fixed window
- Redis sorted set pour sliding window
- LUA scripting pour atomicité
- Local rate limiting + sync périodique
- Graceful degradation : passer en mode degrading si Redis est down
Q7 : Conception d'un Service de Recherche
Problème : Concevoir un moteur de recherche full-text.
Points clés :
- Index inversé
- Ranking (TF-IDF, BM25, PageRank)
- Facettes et filtres
- Suggestions et autocomplete
- Pagination par curseur
Discussion :
- Index inversé stocké (Elasticsearch, tant que service dédié)
- Sharding par document
- Réplication pour HA
- Autocomplete avec Trie / Finite State Transducer
- Real-time vs batch indexing
Q8 : Conception d'un Système d'Authentification
Problème : Concevoir un système d'auth SSO/OAuth2.
Points clés :
- OAuth2 flow (Authorization Code, PKCE)
- JWT access + refresh tokens
- Session management
- MFA / TOTP
- Social login
Discussion :
- JWT signé avec RS256 (public/private key)
- Refresh token rotation avec family
- Blacklist de tokens (Redis)
- OAuth2 provider (Keycloak, Auth0, custom)
- Rate limiting sur les endpoints d'auth
Q9 : Conception de Video Streaming
Problème : Concevoir YouTube/Netflix.
Points clés :
- Upload et transcodage
- Adaptive bitrate streaming (HLS/DASH)
- CDN delivery
- Recommendations
- Comments et likes
Discussion :
- Upload → queue de transcodage → CDN
- Transcodage avec FFmpeg (resolution ladder)
- HLS segments (10s each) sur CDN
- Pre-processing pipeline (thumbnail, analyse)
- ML recommendations (collaborative filtering + content-based)
Q10 : Conception d'un Système de Réservation
Problème : Concevoir un système de réservation (avion, hôtel).
Points clés :
- Concurrence (2 personnes réservent la même place)
- Gestion d'inventaire
- Overbooking contrôlé
- Paiement et confirmation
Discussion :
- Pessimistic locking (SELECT FOR UPDATE)
- Optimistic locking (version column)
- Queue pour les réservations (Kafka)
- Timeout de réservation (TTL Redis)
- Saga pattern pour le flow booking + payment
Q11 : Conception d'un Système de Logs
Problème : Concevoir un système centralisé de logs (ELK-like).
Points clés :
- Ingestion haute vitesse
- Indexation temps réel
- Search et aggregation
- Retention policies
- Alerting
Discussion :
- Kafka comme buffer d'ingestion
- Logstash/Fluentd pour parsing
- Elasticsearch cluster (hot/warm/cold nodes)
- ILM (Index Lifecycle Management)
- Grafana pour les dashboards
Q12 : Conception d'une API Gateway
Problème : Concevoir une API Gateway (Kong/Apigee).
Points clés :
- Routing et load balancing
- Rate limiting, auth
- Request/response transformation
- Circuit breaker
- Observabilité
Discussion :
- Reverse proxy distribué
- Plugins chaînés (auth → rate limit → transform → proxy)
- Service discovery (Consul, K8s)
- Canary routing par header
- Cache des réponses (Redis)
Q13 : Conception de Distributed Lock
Problème : Concevoir un distributed locking service.
Points clés :
- Redlock algorithm
- Lease avec TTL
- Fencing tokens
- Deadlock detection
- Semaphore distribué
Discussion :
- Redis Redlock : lock sur N/2+1 nœuds
- ZooKeeper : sequential ephemeral nodes
- Fencing token pour prévenir les delayed locks
- Watchdog pour prolonger les leases
- Connection loss handling
Q14 : Conception d'un Message Queue
Problème : Concevoir un message queue (Kafka/RabbitMQ).
Points clés :
- Publishing et consuming
- Durabilité et réplication
- Partitionnement
- Consumer groups
- Exactly-once semantics
Discussion :
- Log-based storage (append-only)
- Partition par clé de message
- Consumer group avec rebalance
- ISR (In-Sync Replicas) pour la durabilité
- Idempotent producer + transactional consumer
Q15 : Conception d'un Financial Ledger
Problème : Concevoir un système de comptabilité financière.
Points clés :
- Double-entry accounting
- Immutabilité (append-only)
- Reconciliation
- Audit trail
- Reporting
Discussion :
- Event sourcing : tous les changements sont des événements
- Ledger comme source of truth
- Projections pour les balances
- Sharding par account_id
- Batch reconciliation quotidienne
2.2 Architecture et Design (15 questions)
Q16 : Monolithe vs Microservices
Question : Quand choisir monolithe vs microservices ?
Réponse :
- Monolithe : Petite équipe, MVP, startup, transactions complexes
- Microservices : Équipes multiples, scaling indépendant, cycles de release différents
- Stratégie : Commencer monolithe, extraire progressivement (Strangler Fig)
Q17 : SQL vs NoSQL
Question : Comment choisis-tu entre SQL et NoSQL ?
Réponse :
- SQL : Données relationnelles, ACID, requêtes complexes, reporting
- NoSQL document : Schéma flexible, dénormalisation, scalabilité horizontale
- NoSQL key-value : Cache, sessions, compteurs
- NoSQL graph : Relations complexes, recommandations, traversées
Q18 : REST vs GraphQL vs gRPC
Question : Quand utiliser REST, GraphQL, ou gRPC ?
Réponse :
- REST : API publiques, CRUD standard, cache HTTP, maturité
- GraphQL : UI complexes, multiple ressources, bande passante limitée, subscriptions
- gRPC : Inter-service, streaming, performance, polyglot
Q19 : Cache Strategy
Question : Quelle stratégie de cache choisis-tu selon le cas d'usage ?
Réponse :
- Cache-aside : Lazy loading, bon pour les lectures majoritaires
- Write-through : Cache toujours synchrone, écritures plus lentes
- Write-behind : Performant, risque de perte de données
- Refresh-ahead : Proactif, prédit les accès
- CDN : Statique, assets, contenu public
Q20 : Load Balancing
Question : Comment distribues-tu le trafic entre plusieurs instances ?
Réponse :
- DNS round-robin : Simple, pas de health check
- Reverse proxy (Nginx, HAProxy) : Health check, SSL termination, sticky sessions
- Cloud LB (ALB, GCLB) : Auto-scaling, multi-region
- Service mesh (Istio, Linkerd) : Traffic management, mTLS
Q21 : Database Indexing
Question : Comment indexer une table de 10M lignes avec des requêtes variées ?
Réponse :
- Analyser les requêtes (slow query log, pg_stat_statements)
- B-tree pour les colonnes de tri/where
- Composite index sur les colonnes filtrées ensemble
- Partial index pour les sous-ensembles
- Covering index pour éviter le table scan
- Éviter le sur-indexing (coût en écriture)
Q22 : Idempotence
Question : Comment garantir l'idempotence dans une API ?
Réponse :
- Clé d'idempotence dans le header (Idempotency-Key)
- Stockage en Redis avec TTL
- Vérifier si l'ID existe avant de traiter
- Réponse mise en cache pour la même clé
- Base de données unique constraint
Q23 : Transactions Distribuées
Question : Comment gères-tu les transactions qui traversent plusieurs services ?
Réponse :
- 2PC : Simple mais bloquant et lent
- Saga choreography : Chaque service publie des événements
- Saga orchestration : Orchestrateur central
- Outbox pattern : Événements stockés dans la même transaction DB
- Eventual consistency avec compensation si échec
Q24 : Cap Theorem
Question : Comment appliques-tu le théorème CAP en pratique ?
Réponse :
- Identifier le besoin : quelle propriété sacrifier en cas de partition ?
- AP : Préférer la disponibilité (DynamoDB, Cassandra, DNS)
- CP : Préférer la cohérence (ZooKeeper, etcd, base relationnelle)
- En pratique : trade-off pas binaire, spectre entre strong et eventual consistency
Q25 : Graceful Degradation
Question : Comment conçois-tu un système qui dégrade gracieusement ?
Réponse :
- Circuit breaker pour les dépendances externes
- Fallback values (cache stale, defaults)
- Bulkhead (thread pools séparés)
- Feature flags pour désactiver des fonctionnalités
- Cache toujours disponible même si obsolète
Q26 : Async Processing
Question : Quand utiliser du processing asynchrone vs synchrone ?
Réponse :
- Synchrone : Opérations en temps réel, feedback immédiat (< 100ms)
- Asynchrone : Traitements longs, batch, emails, notifications, découplage
- Pattern : API synchrone pour la validation, enqueue pour le processing
Q27 : Database Migration
Question : Comment gères-tu les migrations de base de données sans downtime ?
Réponse :
- Expand-Migrate-Contract pattern
- Les anciennes et nouvelles colonnes coexistent
- Déploiement en phases : add column → backfill → read new → drop old
- Versioned migrations avec rollback
- Feature flag pour basculer en lecture/écriture
Q28 : Data Consistency
Question : Comment maintiens-tu la cohérence des données entre services ?
Réponse :
- Event-driven avec outbox pattern
- Idempotent consumers
- Saga pour transactions longues
- Reconciliation jobs (batch fix)
- Data contracts (Avro/Protobuf schema registry)
Q29 : Observabilité
Question : Comment rends-tu un système observable ?
Réponse :
- Logs : Structurés, corrélés (trace ID), centralisés (ELK/Loki)
- Métriques : RED (Rate, Errors, Duration) pour chaque service
- Traces : OpenTelemetry, Distributed tracing (Jaeger/Zipkin)
- Dashboards : Grafana pour les tendances
- Alerting : Basé sur SLO, pas sur symptômes
Q30 : Security
Question : Quelles sont les pratiques de sécurité essentielles pour une API backend ?
Réponse :
- HTTPS everywhere (HSTS, TLS 1.3)
- Auth : JWT + refresh token rotation
- Rate limiting par API key et par IP
- Input validation (Zod/Joi)
- SQL injection prevention (parameterized queries)
- CORS configuré restrictivement
- Security headers (CSP, X-Frame-Options)
- Dépendances à jour (npm audit, Dependabot)
- Secrets management (Vault, environment variables)
Partie 3 : Coding Challenges (10 problèmes)
Challenge 1 : Rate Limiter
Problème : Implémenter un Token Bucket rate limiter.
class TokenBucket {
private tokens: number;
private lastRefill: number;
constructor(
private capacity: number,
private refillRate: number, // tokens per second
) {
this.tokens = capacity;
this.lastRefill = Date.now();
}
allow(): boolean {
this.refill();
if (this.tokens > 0) {
this.tokens--;
return true;
}
return false;
}
private refill(): void {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
}
Challenge 2 : LRU Cache
Problème : Implémenter un Least Recently Used cache.
class LRUCache<K, V> {
private capacity: number;
private cache = new Map<K, V>();
constructor(capacity: number) {
this.capacity = capacity;
}
get(key: K): V | undefined {
if (!this.cache.has(key)) return undefined;
const value = this.cache.get(key)!;
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
put(key: K, value: V): void {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
}
Challenge 3 : Distributed Counter
Problème : Implémenter un compteur distribué avec Redis.
async function incrementCounter(key: string, redis: Redis): Promise<number> {
const script = `
local current = redis.call('GET', KEYS[1])
if not current then
redis.call('SET', KEYS[1], 1, 'EX', ARGV[1])
return 1
end
return redis.call('INCR', KEYS[1])
`;
return redis.eval(script, 1, key, 3600);
}
Challenge 4 : JWT Middleware
Problème : Implémenter un middleware d'authentification JWT.
function authenticate(req: Request, res: Response, next: NextFunction): void {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
throw new UnauthorizedError('Missing or invalid token');
}
try {
const token = authHeader.slice(7);
const payload = jwt.verify(token, process.env.JWT_SECRET!, {
algorithms: ['RS256'],
issuer: 'auth.example.com',
}) as JwtPayload;
req.user = { id: payload.sub!, role: payload.role };
next();
} catch (error) {
if (error instanceof jwt.TokenExpiredError) {
throw new UnauthorizedError('Token expired');
}
throw new UnauthorizedError('Invalid token');
}
}
Challenge 5 : Pagination avec Cursor
Problème : Implémenter une pagination par curseur.
async function paginateWithCursor(
query: string,
params: any[],
cursor?: string,
limit: number = 20,
): Promise<{ data: any[]; nextCursor?: string }> {
const actualLimit = limit + 1;
let results;
if (cursor) {
const decoded = Buffer.from(cursor, 'base64').toString();
results = await pool.query(
`${query} AND created_at < $${params.length + 1} ORDER BY created_at DESC LIMIT $${params.length + 2}`,
[...params, decoded, actualLimit],
);
} else {
results = await pool.query(
`${query} ORDER BY created_at DESC LIMIT $${params.length + 1}`,
[...params, actualLimit],
);
}
const hasMore = results.rows.length > limit;
const data = hasMore ? results.rows.slice(0, limit) : results.rows;
const nextCursor = hasMore
? Buffer.from(data[data.length - 1].created_at.toISOString()).toString('base64')
: undefined;
return { data, nextCursor };
}
Challenge 6 : Circuit Breaker
Problème : Implémenter un circuit breaker.
class CircuitBreaker {
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
private failureCount = 0;
private lastFailureTime = 0;
constructor(
private threshold: number = 5,
private cooldownMs: number = 30000,
) {}
async call<T>(fn: () => Promise<T>, fallback: T): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime >= this.cooldownMs) {
this.state = 'HALF_OPEN';
} else {
return fallback;
}
}
try {
const result = await fn();
this.reset();
return result;
} catch (error) {
this.recordFailure();
throw error;
}
}
private recordFailure(): void {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.threshold) {
this.state = 'OPEN';
}
}
private reset(): void {
this.state = 'CLOSED';
this.failureCount = 0;
}
}
Challenge 7 : Debounce
Problème : Implémenter debounce pour éviter les appels répétés.
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number,
): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
Challenge 8 : Throttle
Problème : Implémenter throttle pour limiter le taux d'appels.
function throttle<T extends (...args: any[]) => any>(
fn: T,
limitMs: number,
): (...args: Parameters<T>) => void {
let lastCall = 0;
let timeoutId: ReturnType<typeof setTimeout> | null = null;
return (...args: Parameters<T>) => {
const now = Date.now();
const remaining = limitMs - (now - lastCall);
if (remaining <= 0) {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
lastCall = now;
fn(...args);
} else if (!timeoutId) {
timeoutId = setTimeout(() => {
lastCall = Date.now();
timeoutId = null;
fn(...args);
}, remaining);
}
};
}
Challenge 9 : Saga Orchestrator
Problème : Implémenter un orchestrateur de saga.
interface SagaStep<T> {
name: string;
execute: (ctx: T) => Promise<void>;
compensate: (ctx: T) => Promise<void>;
}
class SagaOrchestrator<T> {
private steps: SagaStep<T>[] = [];
private executedSteps: SagaStep<T>[] = [];
addStep(step: SagaStep<T>): this {
this.steps.push(step);
return this;
}
async execute(ctx: T): Promise<void> {
for (const step of this.steps) {
try {
await step.execute(ctx);
this.executedSteps.push(step);
} catch (error) {
await this.compensate();
throw new Error(`Saga failed at step ${step.name}: ${error}`);
}
}
}
private async compensate(): Promise<void> {
for (const step of [...this.executedSteps].reverse()) {
try {
await step.compensate(step as unknown as T);
} catch (error) {
console.error(`Compensation failed for ${step.name}:`, error);
}
}
}
}
Challenge 10 : Consistent Hashing
Problème : Implémenter un consistent hash ring.
class ConsistentHashRing {
private ring: Map<number, string> = new Map();
private sortedKeys: number[] = [];
private virtualNodes = 100;
constructor(private nodes: string[] = []) {
for (const node of nodes) {
this.addNode(node);
}
}
addNode(node: string): void {
for (let i = 0; i < this.virtualNodes; i++) {
const hash = this.hash(`${node}:${i}`);
this.ring.set(hash, node);
this.sortedKeys.push(hash);
}
this.sortedKeys.sort((a, b) => a - b);
}
removeNode(node: string): void {
for (let i = 0; i < this.virtualNodes; i++) {
const hash = this.hash(`${node}:${i}`);
this.ring.delete(hash);
}
this.sortedKeys = this.sortedKeys.filter(k => this.ring.has(k));
}
getNode(key: string): string {
if (this.ring.size === 0) throw new Error('No nodes available');
const hash = this.hash(key);
for (const ringKey of this.sortedKeys) {
if (hash <= ringKey) {
return this.ring.get(ringKey)!;
}
}
return this.ring.get(this.sortedKeys[0])!;
}
private hash(key: string): number {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash * 31 + key.charCodeAt(i)) | 0;
}
return hash >>> 0;
}
}
Partie 4 : Cas Complets de System Design
Cas 1 : Design d'Uber
Exigences :
- Passagers et conducteurs
- Matching en temps réel
- Pricing dynamique (surge)
- Navigation et tracking
- Paiement
Architecture :
Passenger App → API Gateway → Ride Service → Matching Engine
Conductor App → API Gateway → Driver Service → GPS Service
│
Kafka / WebSocket
│
ETA Service (ML)
Database :
- PostgreSQL : Comptes, rides, payments
- Redis : GPS locations, driver availability
- Cassandra : Ride history (time-series)
Key challenges :
- Geospatial index (PostGIS, Google S2)
- Driver matching dans un rayon donné
- Surge pricing (supply/demand par zone)
- Reliable real-time GPS with WebSocket
Cas 2 : Design de Netflix
Exigences :
- Streaming vidéo
- Recommandations personnalisées
- Multi-device
- Offline download
- CDN global
Architecture :
Upload → Transcode Pipeline → CDN
│
▼
Content DB → Recommendation Engine
│
▼
User Service → API Gateway → Client
Storage :
- S3 pour les vidéos originales
- CloudFront/CDN pour la diffusion
- Cassandra pour l'historique de visionnage
- Elasticsearch pour le catalogue
Key challenges :
- Transcodage (resolution ladder : 360p → 4K)
- Adaptive bitrate (HLS/DASH)
- Personalized recommendations (collaborative filtering + DL)
- CDN edge caching
Cas 3 : Design de Twitter/X
Exigences :
- Tweets, retweets, likes
- Timeline (home + trending)
- Search
- Notifications
- 500M MAU
Architecture :
Tweet Service → Fan-out (Kafka) → Timeline Service → Timeline Cache (Redis)
│
API Gateway ← Client
Fan-out :
- Célébrités : Pull (on read)
- Amis : Push (on write)
Storage :
- PostgreSQL/MySQL : Users, tweets
- Redis : Timeline cache
- Elasticsearch : Search index
- Cassandra : Social graph (followers)
Cas 4 : Design de Dropbox/Google Drive
Exigences :
- Upload/download fichiers
- Sync multi-device
- Partage et permissions
- Versioning
- Collaboration
Architecture :
Client → API Gateway → File Service → Metadata DB (PostgreSQL)
│
Block Store (S3)
│
Sync Engine (WebSocket)
Key challenges :
- Block-level sync (diviser en chunks de 4MB)
- Delta sync (ne transférer que les changements)
- Conflict resolution (last-writer-wins ou merge)
- Deduplication (content-addressable storage)
Cas 5 : Design de Slack/Discord
Exigences :
- Channels et DMs
- Real-time messaging
- File sharing
- Search
- 10M+ concurrent users
Architecture :
Client → WebSocket Gateway → Message Service → Message Store (Cassandra)
│
Search Index (Elasticsearch)
│
File Service (S3 + CDN)
Key challenges :
- WebSocket connection management (gateway)
- Message ordering per channel (sequence numbers)
- Read state tracking (last read message per user per channel)
- Full-text search with sharding
- Rate limiting per channel