1. Comment concevoir un système de raccourcissement d'URL (type bit.ly) ?
Requirements : générer des URL courtes, rediriger rapidement, analytics optionnels. Base de données : table avec colonnes (short_code, original_url, created_at, click_count). Le short_code est généré en encodant un ID auto-increment en base62 (a-zA-Z0-9), donnant des codes courts (7 caractères = 3.5 trillions de combinaisons). Architecture : API REST (POST pour créer, GET pour rediriger avec 301/302). Cache Redis pour les URL les plus accédées (pattern read-through). Load balancer devant plusieurs serveurs applicatifs. Base de données : sharding par hash du short_code pour la scalabilité. Considerations : expiration des URLs, détection de spam, rate limiting, analytics avec des compteurs asynchrones (Kafka + batch processing).
2. Expliquez les concepts de scalabilité horizontale vs verticale.
Scalabilité verticale (scale up) : augmenter les ressources d'une machine (CPU, RAM, stockage). Simple mais limitee par le hardware maximal, single point of failure, downtime pour upgrade. Scalabilité horizontale (scale out) : ajouter des machines. Plus complexe mais theoriquement illimitee, resiliente, zero-downtime. Pour scaler horizontalement : application stateless (session externalisee dans Redis), load balancer pour distribuer le trafic, base de données réplication/sharding. Les applications modernes privilegient le scale out. Pattern : microservices indépendamment scalables, conteneurs orchestres par Kubernetes, auto-scaling base sur les métriques. Le scaling horizontal nécessite de gérer la cohérence des données distribuees.
3. Comment concevoir un système de chat en temps réel ?
Protocole : WebSocket pour la communication bidirectionnelle persistante. Fallback : Server-Sent Events ou long polling. Architecture : serveurs WebSocket derrière un load balancer avec session sticky. Quand un utilisateur envoie un message, le serveur identifie le destinataire et route vers le bon serveur WebSocket. Message broker (Redis Pub/Sub ou Kafka) pour la communication inter-serveurs. Stockage : messages dans Cassandra ou DynamoDB (write-heavy, partitionne par conversation). Présence : heartbeats WebSocket + Redis pour le statut en ligne. Notifications push pour les utilisateurs offline. Scalabilité : sharding des conversations, groupes limites en taille, messages anciens archives dans un stockage froid.
4. Qu'est-ce que le CAP theorem et ses implications pratiques ?
Le theoreme CAP (Brewer) stipule qu'un système distribue ne peut garantir simultanément que deux des trois propriétés : Consistency (tous les nœuds voient la même donnée au même moment), Availability (chaque requête reçoit une réponse), Partition tolérance (le système fonctionne malgré des pertes de communication réseau). Les partitions réseau sont inevitables, donc le choix réel est entre CP (consistance + partition tolérance, ex: ZooKeeper, HBase) et AP (disponibilité + partition tolérance, ex: DynamoDB, Cassandra). En pratique, PACELC affine : en cas de Partition choisir A ou C, sinon choisir Latency ou Consistency. La plupart des systèmes modernes offrent une eventual consistency tunable.
5. Comment concevoir un système de notification (push, email, SMS) ?
Architecture event-driven : les services emettent des événements (commande validee, message reçu) dans une file de messages (Kafka/SQS). Un service de notification consomme les événements et orchestre l'envoi. Template engine pour les contenus (titre, body, variables). Routage : le service determine le canal (push, email, SMS) selon les préférences utilisateur et le type de notification. Files séparées par canal et priorité (urgente vs marketing). Rate limiting pour éviter le spam. Idempotence : deduplication par event_id pour éviter les doublons. Tracking : enregistrer les envois, deliveries, opens, clicks. Retry avec backoff exponentiel pour les échecs. Stocker les préférences et l'historique dans une base dédiée.
6. Expliquez les patterns de communication inter-services (synchrone vs asynchrone).
Synchrone (requete-reponse) : REST ou gRPC. Simple, résultat immédiat, mais couplage fort et risque de cascade de pannes. Asynchrone (messaging) : message queues (RabbitMQ, SQS) pour le point-to-point, event streaming (Kafka) pour le publish-subscribe. Decouplage, résilience, mais complexité accrue et eventual consistency. Patterns : Saga pour les transactions distribuees (choreography ou orchestration), CQRS pour séparer lectures et écritures, Event Sourcing pour stocker les événements plutôt que l'état. Circuit Breaker (Hystrix, Resilience4j) pour isoler les pannes. En pratique, combiner les deux : synchrone pour les requêtes utilisateur nécessitant une réponse immédiate, asynchrone pour les traitements en arriere-plan.
7. Comment concevoir un système de cache distribue ?
Stratégies de caching : Cache-aside (l'application gere le cache explicitement), Read-through (le cache charge depuis la source automatiquement), Write-through (écriture synchrone cache + source), Write-behind (écriture asynchrone vers la source). Eviction : LRU (Least Recently Used), LFU (Least Frequently Used), TTL (Time To Live). Architecture : Redis Cluster ou Memcached distribue. Consistent hashing pour distribuer les clés entre les nœuds sans redistribution massive lors de l'ajout/suppression. Cache stampede : lock ou probabilistic early expiration pour éviter les requêtes simultanées à l'expiration. Invalidation : la plus grande difficulté en informatique. Stratégies : TTL, event-driven invalidation, version-based keys.
8. Qu'est-ce que le sharding de base de données et comment le mettre en place ?
Le sharding (partitionnement horizontal) distribue les données sur plusieurs serveurs de base de données. Stratégies : range-based (par date, par ID range), hash-based (hash de la cle mod N), directory-based (table de lookup). Cle de sharding : choix critique, doit distribuer uniformement les données et les requêtes. Mauvais choix = hotspots. Problèmes : jointures cross-shard complexes, transactions distribuees, rebalancing lors de l'ajout de shards. Solutions : denormaliser pour éviter les jointures, utiliser des ID globaux (snowflake), consistent hashing pour le rebalancing minimal. Alternatives au sharding manuel : bases de données nativement distribuees (CockroachDB, Vitess pour MySQL, Citus pour PostgreSQL).
9. Comment concevoir un moteur de recherche simplifie ?
Indexation : crawler parcourt les pages, extracteur parse le HTML, index inverse mappe chaque mot aux documents le contenant (mot -> liste de doc_ids avec positions et frequences). Stockage dans une structure optimisée (B-tree ou hash). Recherche : tokenisation de la requête, lookup dans l'index inverse, intersection des résultats pour les requêtes multi-mots. Ranking : TF-IDF (fréquence du terme * rarete dans le corpus) comme base, PageRank pour la popularité, signaux de fraicheur, proximité des mots. Architecture : index partitionne (sharded) par documents, réplicas pour la disponibilité. Elasticsearch implemente tout cela avec Lucene : index inverse, analyseurs linguistiques, scoring BM25, aggregations, suggestions autocomplete.
10. Comment aborder un entretien de system design ?
Méthodologie en 4 étapes : 1) Clarifier les requirements (5 min) : utilisateurs, fonctionnalités clés, contraintes (traffic, stockage, latence). Estimer les chiffres (QPS, stockage). 2) High-level design (15 min) : composants principaux (client, API, services, base de données, cache), diagramme d'architecture, flux de données. 3) Deep dive (15 min) : approfondir 2-3 composants critiques, discuter les trade-offs, gérer les edge cases. 4) Wrap-up (5 min) : bottlenecks, monitoring, évolution future. Principes transversaux : estimer avant de concevoir, privilegier la simplicité, nommer les trade-offs explicitement, considérer les pannes. Les recruteurs evaluent la pensée structurée et la capacité à naviguer les compromis, pas la solution parfaite.