Chapitre 22
Chapitre 22 : Préparation aux Entretiens Techniques
> **60 questions d'entretien** (30 comportementales + 30 techniques) **+ 3 études de system design** pour réussir vos entretiens Python.
Chapitre 22 — Préparation aux Entretiens Techniques
Version : 1.0.0
30 questions comportementales + 30 questions techniques + 3 system designs.
Table des matières
- Introduction
- 30 Questions Comportementales (STAR)
- 30 Questions Techniques Python
- 3 Études de System Design
- Préparation finale
Introduction
L'entretien technique Python se décompose généralement en 4 parties :
- Présentation et questions comportementales (15-30 min)
- Question technique Python (30-45 min) — codage en direct
- System Design (30-45 min) — architecture
- Q&A (10-15 min) — vos questions à l'entreprise
Ce que les recruteurs évaluent
| Critère | Pondération | Détail |
|---|---|---|
| Compétences techniques | 40 % | Python, algorithmes, architecture |
| Communication | 25 % | Clarté, écoute, pédagogie |
| Expérience | 20 % | Projets, réalisations, échecs |
| Culture fit | 15 % | Valeurs, collaboration, autonomie |
1. 30 Questions Comportementales (STAR)
Questions générales
Q01. Parlez-moi de vous.
STAR : Structurez votre réponse en 3 minutes :
- Présent : votre poste actuel et vos responsabilités
- Passé : votre parcours (2-3 étapes clés)
- Futur : pourquoi ce poste vous attire
Exemple de réponse :
"Actuellement, je suis ingénieur backend chez [Company], où je maintiens une API FastAPI qui sert 10M de requêtes/jour. Avant cela, j'ai travaillé 3 ans chez [Startup] sur des pipelines de données avec pandas et Airflow. Je souhaite rejoindre [Target] parce que votre stack technique (async, event-driven) correspond exactement à ce que je veux approfondir."
Q02. Quelle est votre plus grande réussite technique ?
STAR :
- S : Projet de migration d'une API monolithique vers microservices
- T : Migrer 50 endpoints avec 0 downtime
- A : Création d'un plan de migration en 3 phases, tests de charge, blue-green deployment, rollback automatisé
- R : Migration réussie en 2 mois, 0 incident, 40 % de réduction du temps de réponse
Q03. Parlez-moi d'un projet qui a échoué.
STAR :
- S : Projet de refonte d'un data pipeline
- T : Migrer de pandas vers Spark pour scaler à 10 To
- A : Sous-estimation de la complexité de la migration, manque de tests de non-régression
- R : Retard de 3 mois, dépassement budget de 30 %
- Leçon : Ajout d'une phase de POC systématique avant toute migration
Q04. Comment gérez-vous un désaccord avec votre manager ?
STAR :
- Écouter son point de vue
- Proposer des données objectives (métriques, benchmarks)
- Suggérer un A/B test ou un POC
- Accepter la décision finale si le désaccord persiste
Q05. Décrivez une situation où vous avez dû apprendre une nouvelle technologie rapidement.
STAR : Passage de Flask à FastAPI en 2 semaines pour un projet client.
- A : Lecture de la doc officielle, réalisation d'un POC, revue de code croisée avec un collègue expert
- R : Livraison dans les temps, 30 % de réduction du code boilerplate
Q06. Comment priorisez-vous votre travail quand vous avez plusieurs deadlines ?
- Matrice Eisenhower (urgent/important)
- Communication transparente avec les stakeholders
- Utilisation de tickets Jira avec priorisation
- Négociation des délais si nécessaire
Q07. Décrivez un projet où vous avez collaboré avec une équipe non technique.
STAR : Projet avec l'équipe marketing pour un dashboard analytics.
- Traduction des besoins métier en spécifications techniques
- Itérations courtes avec démos hebdomadaires
- Documentation claire (pas de jargon technique)
Q08. Comment gérez-vous le stress en production (incident) ?
- Rester calme, diagnostiquer méthodiquement
- Communiquer sur le canal d'incident
- Prioriser le rétablissement du service (rollback > fix)
- Post-mortem sans blame
Q09. Quelle est votre plus grande faiblesse ?
Choisissez une vraie faiblesse et montrez comment vous l'améliorez : "J'ai tendance à vouloir perfectionner le code avant de le partager. Je travaille à faire des PR plus tôt, même imparfaites, pour itérer plus rapidement."
Q10. Pourquoi voulez-vous quitter votre poste actuel ?
Restez positif : parlez de ce que vous cherchez (nouveaux défis, stack technique, impact), pas de ce que vous fuyez.
Q11-Q20 : Questions métier
Q11. Comment définissez-vous un code "propre" ? Q12. Quelle est votre expérience avec la revue de code ? Q13. Comment testez-vous votre code ? (pyramid of tests) Q14. Parlez-moi d'un incident de production que vous avez résolu. Q15. Comment documentez-vous votre code ? Q16. Quelle est votre expérience avec CI/CD ? Q17. Comment gérez-vous la dette technique ? Q18. Décrivez un projet open source auquel vous avez contribué. Q19. Comment restez-vous à jour avec les nouvelles technologies ? Q20. Que feriez-vous différemment dans votre dernier projet ?
Q21-Q30 : Questions situationnelles
Q21. Un collègue ne répond pas à vos messages. Vous faites quoi ? Q22. Vous découvrez un bug critique en production un vendredi soir. Q23. Votre estimation d'une tâche était trop optimiste. Q24. Un développeur junior vous demande de l'aide sur un sujet que vous maîtrisez peu. Q25. On vous demande de livrer une fonctionnalité sans tests. Q26. Un membre de l'équipe propose une solution moins bonne que la vôtre. Q27. Vous devez donner un feedback négatif à un collègue. Q28. On vous impose une technologie que vous n'aimez pas. Q29. Vous travaillez sur 3 projets en parallèle. Comment vous organisez-vous ? Q30. Que feriez-vous si votre architecte proposait une solution que vous jugez sous-optimale ?
2. 30 Questions Techniques Python
Questions fondamentales (Q01-Q10)
Q01. Quelle est la différence entre une liste et un tuple ?
# Liste : mutable, peut être modifiée après création
lst = [1, 2, 3]
lst[0] = 10 # OK
# Tuple : immuable, ne peut pas être modifié
tpl = (1, 2, 3)
tpl[0] = 10 # TypeError: 'tuple' object does not support item assignment
Utilisation : tuple pour les données fixes (coordonnées, clés de dict), liste pour les collections dynamiques.
Q02. Comment fonctionne le GIL (Global Interpreter Lock) ?
Le GIL est un mutex qui protège l'accès aux objets Python, empêchant plusieurs threads natifs d'exécuter du bytecode simultanément.
- Avantage : simplifie la gestion mémoire (pas de race conditions)
- Inconvénient : limite le parallélisme CPU-bound avec threads
- Solutions :
multiprocessing, asyncio (I/O-bound), C extensions (numpy)
import threading
# Les threads ne parallélisent pas le CPU
def count(n):
while n > 0:
n -= 1
t1 = threading.Thread(target=count, args=(10**7,))
t2 = threading.Thread(target=count, args=(10**7,))
# Pas plus rapide que séquentiel !
Q03. Quelle est la différence entre __str__ et __repr__ ?
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __str__(self):
return f"({self.x}, {self.y})" # Pour l'utilisateur
def __repr__(self):
return f"Point({self.x}, {self.y})" # Pour le développeur
p = Point(3, 4)
print(p) # (3, 4) → __str__
repr(p) # "Point(3, 4)" → __repr__
Q04. Comment fonctionne un décorateur ?
Un décorateur est une fonction qui prend une fonction en argument et retourne une fonction modifiée.
from functools import wraps
import time
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
slow_function() # slow_function took 1.0001s
Q05. Qu'est-ce qu'un générateur et pourquoi l'utiliser ?
Un générateur produit des valeurs à la demande (lazy evaluation) sans stocker toute la séquence en mémoire.
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
# vs liste équivalente
def fibonacci_list(n):
result = []
a, b = 0, 1
for _ in range(n):
result.append(a)
a, b = b, a + b
return result
# Avantages :
# 1. Mémoire : générateur = O(1), liste = O(n)
# 2. Composition : chaînage de générateurs
# 3. Infini : possible avec yield, pas avec liste
Q06. Comment fonctionne la gestion mémoire en Python ?
Python utilise :
- Reference counting : chaque objet compte ses références. Quand le compte atteint 0, il est libéré immédiatement.
- Garbage collector (gc) : détecte et collecte les cycles de références.
- Memory pools : petites allocations gérées par des arenas.
import sys
import gc
obj = []
print(sys.getrefcount(obj)) # 2 (dont l'argument temporaire)
# Forcer le garbage collector
gc.collect()
Q07. Expliquez le MRO (Method Resolution Order).
Le MRO détermine l'ordre dans lequel Python cherche les méthodes dans l'héritage multiple. Il utilise l'algorithme C3 linearization.
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
Q08. Quelle est la différence entre deepcopy et shallow copy ?
import copy
original = [[1, 2, 3], [4, 5, 6]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
shallow[0][0] = 99 # Modifie aussi original[0][0]
deep[0][0] = 42 # Ne modifie pas original
- Shallow copy : copie les références (1 niveau)
- Deep copy : copie récursivement tous les objets
Q09. Comment fonctionne asyncio.gather vs asyncio.create_task ?
import asyncio
async def fetch(id):
await asyncio.sleep(id)
return id
# gather : exécute plusieurs coroutines en concurrence
async def with_gather():
results = await asyncio.gather(fetch(1), fetch(2), fetch(3))
return results # [1, 2, 3]
# create_task : crée une tâche qui s'exécute en arrière-plan
async def with_tasks():
tasks = [asyncio.create_task(fetch(i)) for i in range(3)]
results = [await t for t in tasks]
return results # [1, 2, 3]
Q10. Qu'est-ce qu'un context manager et comment l'implémenter ?
# Avec une classe
class ManagedFile:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.file = open(self.filename, 'w')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
# Avec contextlib
from contextlib import contextmanager
@contextmanager
def managed_file(filename):
f = open(filename, 'w')
try:
yield f
finally:
f.close()
Questions intermédiaires (Q11-Q20)
Q11. Comment fonctionne super() dans l'héritage multiple ?
Q12. Quelle est la différence entre @staticmethod, @classmethod, et une méthode d'instance ?
Q13. Expliquez le duck typing et son rapport avec les Protocols.
Q14. Comment fonctionne le mécanisme d'import en Python ?
Q15. Quelle est la différence entre __getattr__ et __getattribute__ ?
Q16. Comment fonctionnent les métaclasses ? Donnez un exemple.
Q17. Qu'est-ce que le "Global Interpreter Lock" et comment le contourner ?
Q18. Expliquez les différentes façons de paralléliser en Python.
Q19. Comment fonctionne le packaging en Python (pyproject.toml) ?
Q20. Quelle est la différence entre __slots__ et un dictionnaire d'instance ?
# Q13: Duck typing + Protocol
from typing import Protocol
class Quackable(Protocol):
def quack(self) -> str: ...
class Duck:
def quack(self) -> str:
return "Quack!"
class Person:
def quack(self) -> str:
return "I can quack!"
def make_it_quack(obj: Quackable):
print(obj.quack())
make_it_quack(Duck()) # OK
make_it_quack(Person()) # OK (duck typing)
Questions avancées (Q21-Q30)
Q21. Comment fonctionne l'instruction yield from ?
Q22. Expliquez le concept de "descriptor" en Python.
Q23. Comment fonctionne l'AST (Abstract Syntax Tree) en Python ?
Q24. Qu'est-ce que l'import "lazy" et comment l'implémenter ?
Q25. Comment fonctionne le buffer protocol (PEP 3118) ?
Q26. Expliquez le "trio" pattern de concurrence.
Q27. Comment fonctionnent les "coroutines" au niveau bas (PEP 492) ?
Q28. Qu'est-ce que le "Global Object" pattern et pourquoi l'éviter ?
Q29. Comment le JIT (niveau 3.13) change-t-il la donne ?
Q30. Comment fonctionne le free-threaded mode de Python 3.13 ?
# Q22: Descripteur
class PositiveNumber:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
return obj.__dict__.get(self.name)
def __set__(self, obj, value):
if value <= 0:
raise ValueError("Must be positive")
obj.__dict__[self.name] = value
class Order:
quantity = PositiveNumber()
order = Order()
order.quantity = 10 # OK
order.quantity = -1 # ValueError
3. 3 Études de System Design
Design 1 : Data Pipeline (ETL)
Contexte : Concevoir un pipeline de données qui ingère 50 Go de logs par jour, les transforme, et les charge dans un entrepôt de données.
Contraintes :
- 50 Go/jour de logs (format JSON)
- Latence max : 1 heure (batch)
- Interrogation ad-hoc possible
- Coût maîtrisé
Solution proposée :
┌─────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐
│ Sources │───▶│ Kafka │───▶│ Spark │───▶│ClickHouse│
│ (APIs) │ │ (Buffer) │ │ (Stream) │ │ (OLAP) │
└─────────┘ └──────────┘ └───────────┘ └──────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ S3 │ │ Grafana │
│ (Archive)│ │ (Dash) │
└──────────┘ └──────────┘
Composants :
- Ingestion : FastAPI → Kafka (partitionné par date)
- Traitement : PySpark (batch horaire) ou Flink (streaming)
- Transformation : validation, déduplication, enrichissement
- Stockage : ClickHouse (analytics) + S3 (cold storage, 90 jours)
- Orchestration : Airflow (planification + monitoring)
- Monitoring : Prometheus + Grafana
Dimensions à considérer :
- Schema evolution : Avro ou Protobuf avec registry
- Exactly-once semantics : Kafka + Spark checkpointing
- Backpressure : Kafka consumer lag monitoring
- Cost : S3 lifecycle policies, spot instances Spark
Questions d'entretien possibles :
- Comment garantir l'ordre des événements ?
- Que faire en cas d'échec d'un job Spark ?
- Comment backfiller des données historiques ?
Design 2 : ML API (Real-time Inference)
Contexte : Concevoir une API de prédiction en temps réel pour un modèle de recommandation produit.
Contraintes :
- Latence p99 < 200 ms
- 10 000 requêtes/seconde
- Modèle de 500 Mo (neural network)
- Mise à jour du modèle sans downtime
Solution proposée :
┌──────────┐
│ Load │
┌─────▶│ Balancer │──────┐
│ │ (nginx) │ │
│ └──────────┘ │
│ ▼
┌──────┐ │ ┌──────────────────┐
│Users │───┤ │ FastAPI (uvicorn)│
└──────┘ │ │ - Model loading │
│ │ - Pre/post proc │
│ │ - Feature store │
│ └──────────────────┘
│ │
│ ▼
│ ┌──────────────────┐
│ │ Redis (cache) │
│ │ + Feature Store │
│ └──────────────────┘
Composants :
- API : FastAPI asynchrone (workers uvicorn)
- Load balancing : nginx + round-robin
- Inference : ONNX Runtime (optimisé CPU/GPU)
- Cache : Redis (LRU, 5 min TTL, features les plus demandées)
- Batch prediction : Celery + Redis pour les lots
- Model registry : MLflow (versioning, rollback)
Gestion des modèles :
class ModelRegistry:
def __init__(self):
self.models: dict[str, InferenceModel] = {}
self._lock = asyncio.Lock()
async def load_model(self, name: str, version: str) -> None:
async with self._lock:
new_model = await self._download_model(name, version)
self.models[name] = new_model # Atomic swap
async def predict(self, name: str, features: Feature) -> Prediction:
model = self.models.get(name)
if not model:
raise ModelNotFoundError(name)
return model.predict(features)
Monitoring :
- Predictions : Prometheus (latency, throughput, error rate)
- Drift : Evidently AI (data drift, model drift)
- Alerts : PagerDuty (p99 > 200 ms, error rate > 1%)
Questions d'entretien possibles :
- Comment gérer le cache invalidé ?
- Comment faire un A/B test de modèles ?
- Que faire si le modèle est trop lent ?
Design 3 : Real-time Processing System
Contexte : Concevoir un système de traitement en temps réel pour des événements IoT (10 000 capteurs, 1M messages/minute).
Contraintes :
- Traitement en temps réel (sous 100 ms)
- 1 million de messages/minute
- Fenêtres de temps (1 min, 5 min, 1h)
- Alertes en cas d'anomalie
Solution proposée :
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│Capteurs │──▶│ MQTT │──▶│ Kafka │──▶│ Flink │
│(10 000) │ │ Broker │ │(Stream) │ │(Process) │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│
┌─────────────────────────────┤
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ InfluxDB │ │ Alert │ │ Kafka │
│ (TSDB) │ │ Manager │ │ (Down) │
└──────────┘ └──────────┘ └──────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Grafana │ │ PagerDuty│
│ (Dash) │ │ (Alert) │
└──────────┘ └──────────┘
Composants :
- Ingestion : MQTT (protocole IoT standard) → Kafka (buffering + replay)
- Stream processing : Apache Flink (ou Kafka Streams)
- Time windows : tumbling windows (1min, 5min, 1h)
- Storage : InfluxDB (time series) + Cassandra (long-term)
- Alerting : Rule engine → PagerDuty/Slack
- Dashboard : Grafana (temps réel)
Traitement Flink (pseudo-code Python avec PyFlink) :
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.datastream.window import TumblingProcessingTimeWindows
env = StreamExecutionEnvironment.get_execution_environment()
stream = env.add_source(kafka_source)
# Fenêtre de 1 minute
windowed = (
stream
.key_by(lambda e: e["sensor_id"])
.window(TumblingProcessingTimeWindows.of(Time.minutes(1)))
.aggregate(AverageAggregator())
)
# Détection d'anomalie (Z-score)
def detect_anomaly(reading):
if abs(reading["value"] - reading["mean"]) > 3 * reading["std"]:
return alert_event(reading)
alerts = windowed.map(detect_anomaly)
alerts.add_sink(alert_sink)
Considérations :
- Exactly-once : Kafka + checkpointing Flink
- Watermarks : pour gérer les événements en retard
- State management : RocksDB pour états larges
- Scaling : partitionnement Kafka + parallelisme Flink
Questions d'entretien possibles :
- Comment gérer un pic de trafic 10x ?
- Que faire si Kafka est down ?
- Comment gérer les données en retard (> 1 heure) ?
4. Préparation finale
Checklist pré-entretien
- Relire les 30 questions comportementales (STAR préparé)
- Coder les 40 exercices du chapitre 17 (au moins les 20 premiers)
- Faire les 100 QCM du chapitre 18 (score > 70 %)
- Préparer 3 questions à poser au recruteur
- Vérifier votre setup technique (IDE, Python, venv)
- Tester votre micro/casque/connexion
Questions à poser au recruteur
Techniques :
- Quelle est votre stack technique actuelle ?
- Comment gérez-vous les migrations de code ?
- Quelle est votre politique de tests ?
- Comment est organisée la revue de code ?
Process :
- À quoi ressemble une semaine typique ?
- Comment sont priorisées les features ?
- Quel est le processus on-call ?
Équipe :
- Combien de personnes dans l'équipe Python ?
- Quelle est l'ancienneté moyenne ?
- Comment sont répartis les rôles ?
Le jour J
- Arrivez 5 minutes en avance (ou testez la connexion 10 min avant)
- Respirez : l'entretien est aussi un échange
- Pensez à voix haute : le recruteur veut voir votre raisonnement
- Si bloqué : dites "Je ne suis pas sûr, mais voici comment j'aborderais..."
- Codez proprement : nommage, types, tests
- Prenez le feedback : remerciez, même si négatif
Ressources
- Cracking the Coding Interview (McDowell) — Algorithmes
- System Design Interview (Alex Xu) — Architecture
- Designing Data-Intensive Applications (Kleppmann) — Distribué
Prochain chapitre : 23-Annexes →
Glossaire, cheat sheets, idioms et pièges à éviter.