Synchrone vs Asynchrone, ou la réponse dont je n'ai pas besoinSynchronous vs Asynchronous, or the answer I don't need
Enchaîner quatre appels en synchrone les traite comme s'ils comptaient pareil : leurs latences s'additionnent, leurs disponibilités se multiplient, et un service périphérique en panne fait échouer un paiement réussi. Or un seul appel gate la réponse ; le reste doit seulement être fait. Couper le fil après le gate découple dans le temps — mais l'async n'est pas « mieux » : c'est un échange, et on doit reconstruire (file durable, retry, idempotence) la garantie qu'offrait le synchrone.Chaining four calls synchronously treats them as if they counted the same: their latencies add up, their availabilities multiply, and one peripheral outage fails a successful payment. Yet only one call gates the response; the rest merely have to happen. Cutting the thread after the gate decouples in time — but async isn't 'better': it's a trade, and you must rebuild (durable queue, retry, idempotence) the guarantee synchronous gave for free.
AudienceAudience
Dev qui hésite à passer un appel en async sans mesurer ce qu'il échangeDev weighing whether to make a call async without measuring the trade
Quatre appels en file. De combien ai-je besoin de la réponse, maintenant ?Four calls in a row. How many of their answers do I need, now?
Au #2 du Vol 3, Observer a rendu l'émetteur aveugle à son public — mais ses réactions restaient dans le même fil. Voici la suite : checkout enchaîne quatre appels comme s'ils étaient de même nature. Ils ne le sont pas. Le paiement gate la réponse : sans lui, pas de commande. Le mail, le PDF, l'analytics doivent seulement être faits — un jour. Ce qui varie, ce n'est pas l'appel : c'est si j'ai besoin de sa réponse maintenant. Le réflexe : ne fais attendre l'utilisateur que pour ce qui décide de la suite.In Vol 3 #2, Observer made the emitter blind to its audience — but its reactions stayed in the same thread. Here's the sequel: checkout chains four calls as if they were the same kind. They aren't. The payment gates the response: without it, no order. The email, the PDF, analytics only need to happen — someday. What varies isn't the call: it's whether I need its answer now. The reflex: make the user wait only for what decides what comes next.
Le code naïf — tout attendu, comme si tout comptait pareilThe naive code — everything awaited, as if all of it counted the same
def checkout(order):
pay = PaymentGateway().charge(order) # ← j'ai BESOIN de la réponse : refusé ?if not pay.ok: return"paiement refusé"
EmailService().send_receipt(order) # … le client n'attend pas le mail
InvoiceService().generate_pdf(order) # … ni la génération du PDF
Analytics().track("purchase", order) # … ni l'analyticsreturn"commande confirmée"# le client attend TOUT ça pour ça
La réponse vs le simple « fait »The answer vs the mere 'done'
charge() → ok ?charge() → ok?
réponse · maintenantanswer · now
|
vsvs
mail · pdf · analyticsemail · pdf · analytics
fait · un jourdone · someday
Une seule de ces lignes décide de ce qu'on répond au client. Les trois autres ne font que devoir arriver. Tant qu'elles sont dans le même fil, le client attend leur somme — et la commande dépend de leur disponibilité à toutes.Only one of these lines decides what we answer the customer. The other three merely have to happen. As long as they're in the same thread, the customer waits for their sum — and the order depends on all of them being available.
Le réflexe du volumeThe volume's reflex
« Ai-je besoin d'une réponse maintenant, ou seulement que ce soit fait un jour ? » Synchrone répond à la première : j'appelle, j'attends, je décide avec le résultat. Asynchrone répond à la seconde : j'émets le travail et je rends la main, la suite se fait sans moi. Tout le volume tient dans ce tri — et le piège, c'est de croire que l'un est « moderne » et l'autre « vieux ». Ce sont deux régimes de couplage, chacun avec sa facture.'Do I need an answer now, or only that it gets done someday?' Synchronous answers the first: I call, I wait, I decide with the result. Asynchronous answers the second: I emit the work and return, the rest happens without me. The whole volume lives in that sorting — and the trap is believing one is 'modern' and the other 'old'. They're two coupling regimes, each with its own bill.
Le pont depuis Observer.The bridge from Observer.
Observer (Vol 3 #2) a coupé le savoir — qui réagit — mais laissait le temps soudé : les réactions s'exécutaient dans le fil de l'émetteur. L'asynchrone coupe le temps. Une fois les réactions derrière une file, le découplage temporel qu'Observer rendait facultatif devient structurel. C'est exactement le pas que prend ce volume : du « émets un fait » en mémoire au « émets un fait » à travers le temps et le réseau.Observer (Vol 3 #2) cut the knowledge — who reacts — but left time welded: reactions ran in the emitter's thread. Async cuts time. Once reactions sit behind a queue, the temporal decoupling Observer made optional becomes structural. That's exactly the step this volume takes: from 'emit a fact' in memory to 'emit a fact' across time and network.
№ 02La douleurThe pain4 min
Les latences s'additionnent, les disponibilités se multiplient.Latencies add up, availabilities multiply.
Tout enchaîner en synchrone a un coût qui n'apparaît pas sur un poste de dev, où chaque service répond en 5 ms et personne n'est en panne. En production, deux lois s'installent. Les latences s'additionnent : le client attend la somme de tous les appels. Les disponibilités se multiplient : il suffit qu'un seul service périphérique soit lent ou tombé pour que le paiement réussi… échoue quand même à se conclure en commande.Chaining everything synchronously has a cost invisible on a dev machine, where each service answers in 5 ms and nothing is down. In production, two laws kick in. Latencies add up: the customer waits for the sum of every call. Availabilities multiply: a single peripheral service being slow or down is enough for a successful payment to… still fail to become an order.
№ 1 · La latence est une sommeLatency is a sum
Le client attend charge + mail + pdf + analytics, bout à bout. Le PDF rame deux secondes ? La page « commande confirmée » met deux secondes de plus — pour un document que personne ne regarde sur le moment.The customer waits charge + email + pdf + analytics, end to end. The PDF lags two seconds? The 'order confirmed' page takes two seconds longer — for a document nobody looks at right then.
№ 2 · La disponibilité est un produitAvailability is a product
Quatre services à 99,9 % chacun, appelés en série, donnent un chemin à ~99,6 %. Chaque dépendance synchrone qu'on ajoute au chemin critique le rend strictement moins fiable. On hérite des pannes de tout le monde.Four services at 99.9% each, called in series, give a path at ~99.6%. Every synchronous dependency added to the critical path makes it strictly less reliable. You inherit everyone's outages.
№ 3 · Le périphérique fait tomber le critiqueThe peripheral fells the critical
Le fournisseur d'e-mail est en panne ? send_receipt lève, checkout échoue — alors que le paiement, lui, a réussi. On a encaissé sans confirmer la commande. Le moins important a fait tomber le plus important.The email provider is down? send_receipt throws, checkout fails — even though the payment succeeded. You charged the card without confirming the order. The least important brought down the most important.
Le paiement réussi, la commande perdueThe payment succeeded, the order lost
pay = PaymentGateway().charge(order) # ✓ carte débitée
EmailService().send_receipt(order) # 💥 fournisseur d'e-mail HS → lève# checkout échoue. Le client est débité, voit une erreur, et n'a pas de commande.
Le diagnostic.The diagnosis.
Le détecteur, appel par appel : « ai-je besoin de sa réponse pour décider de la suite ? ». Si oui, il reste synchrone, sur le chemin critique, et j'accepte sa latence et sa panne. Si non, il n'a rien à y faire : je le passe en asynchrone derrière une file, et le chemin critique rétrécit à ce qui le mérite. Le réflexe n'est pas « tout passer en async » — c'est trier, ligne par ligne, ce qui gate la réponse de ce qui doit seulement arriver.The detector, call by call: 'do I need its answer to decide what comes next?'. If yes, it stays synchronous, on the critical path, and I accept its latency and its failure. If no, it has no business there: I move it to async behind a queue, and the critical path shrinks to what deserves it. The reflex isn't 'make everything async' — it's sorting, line by line, what gates the response from what merely has to happen.
№ 03Le fil coupéThe thread cut5 min
On coupe le fil après ce qui gate la réponse — le reste draine derrière.Cut the thread after what gates the response — the rest drains behind.
L'asynchrone n'est pas une vitesse, c'est une coupure. On garde synchrone le seul appel dont la réponse décide de la suite, et on rend la main tout de suite après ; tout ce qui doit seulement arriver part dans une file et s'exécute en arrière-plan. L'animation suit le fil d'exécution du client : d'abord bloqué sur les quatre appels, puis libéré au gate — puis montre que ce gain a un prix, et lequel.Async isn't a speed, it's a cut. Keep synchronous the only call whose answer decides what comes next, and return right after; everything that merely has to happen goes to a queue and runs in the background. The animation follows the client's thread of execution: first blocked across all four calls, then freed at the gate — then shows that this gain has a price, and which one.
Animation — le fil d'exécution du clientAnimation — the client's thread of execution
Départ — tout synchroneStart — all synchronous · 1/4
Le client est bloqué sur les quatre appels bout à bout. Sa latence est leur somme ; sa réussite, leur produit. Il attend le PDF pour voir « confirmée ».The client is blocked across all four calls end to end. Its latency is their sum; its success, their product. It waits for the PDF to see «confirmed».
Le même flux, deux régimesThe same flow, two regimes
# Synchrone — j'ai besoin de la réponse pour décider
pay = gateway.charge(order) # bloque, retourne ok/refuséif not pay.ok: return"refusé"# Asynchrone — il suffit que ce soit fait ; je rends la main
queue.publish("OrderPaid", order.id) # dépose, ne bloque pasreturn"commande confirmée"# le reste draine derrière# un worker, plus tard : mail, PDF, analytics — chacun à son rythme
Le motif transférable.The transferable pattern.
« N'attends que ce qui décide de la suite. » Le même tri se rejoue partout : un appel HTTP qu'on pourrait remplacer par un message déposé (Vol 4 #2), une écriture qu'on confirme avant de propager ses effets, une requête utilisateur qu'on accuse réception avant de traiter. À chaque fois, la question est la même : la réponse gate-t-elle une décision, ou seulement un accusé que « c'est parti » ?'Wait only for what decides what comes next.' The same sorting replays everywhere: an HTTP call you could replace with a dropped message (Vol 4 #2), a write you confirm before propagating its effects, a user request you acknowledge before processing. Each time, the question is the same: does the answer gate a decision, or merely acknowledge that 'it's underway'?
Modèle mentalMental model
L'imageThe image
Un appel synchrone, c'est un coup de fil : tu restes en ligne jusqu'à la réponse. L'asynchrone, c'est déposer un pli dans une boîte aux lettres : tu raccroches, quelqu'un le traitera plus tard.A synchronous call is a phone call: you stay on the line until the answer. Async is dropping a letter in a mailbox: you hang up, someone handles it later.
La règleThe rule
N'attends en ligne que l'appel dont la réponse décide de la suite ; dépose le reste dans un tampon durable et rends la main.Stay on the line only for the call whose answer decides what comes next; drop the rest into a durable buffer and return.
Où l'analogie casseWhere the analogy breaks
La boîte aux lettres a la Poste derrière : un pli déposé est garanti d'arriver, même si tu meurs. Un thread lancé « en mémoire » n'a personne derrière — pas de persistance, pas de retry. L'async ne t'offre la garantie de la boîte que si la file est vraiment durable ; sinon tu as juste jeté le pli par la fenêtre.The mailbox has the postal system behind it: a dropped letter is guaranteed to arrive, even if you die. A thread spawned 'in memory' has no one behind it — no persistence, no retry. Async only grants you the mailbox's guarantee if the queue is genuinely durable; otherwise you merely tossed the letter out the window.
№ 04Le piègeThe trap4 min
Rendre la main ne suffit pas. L'« async » qui a juste supprimé la garantie.Returning early isn't enough. The 'async' that merely deleted the guarantee.
Le piège n'est pas d'oublier de découpler — c'est de croire que ne plus attendre, c'est avoir découplé. Le synchrone offrait une garantie gratuite : quand l'appel revient, le travail est fait. L'asynchrone la perd par défaut. Si on se contente de lancer un thread et d'oublier, un redémarrage avale le travail en silence : pas de persistance, pas de retry, pas d'accusé. On n'a pas déplacé la garantie vers une file — on l'a supprimée. Bascule le toggle : cherche le « et si le process meurt maintenant ? ».The trap isn't forgetting to decouple — it's believing that not waiting means having decoupled. Synchronous gave a free guarantee: when the call returns, the work is done. Async loses it by default. If you just spawn a thread and forget, a restart swallows the work silently: no persistence, no retry, no acknowledgement. You didn't move the guarantee to a queue — you deleted it. Flip the toggle: hunt the 'what if the process dies now?'.
# async = on déplace la garantie vers une file DURABLEdef checkout(order):
pay = gateway.charge(order)
if not pay.ok: return"refusé"
queue.publish("OrderPaid", {"order_id": order.id}) # persisté avant le retourreturn"commande confirmée"# un worker, idempotent, retenté par la file si ça cassedef on_order_paid(msg):
if already_done(msg.id): return# rejouer ne fait pas de doublon
EmailService().send_receipt(msg.order_id)
mark_done(msg.id) # APRÈS l'effet : un crash entre les deux = un renvoi, pas une perte
Pourquoi c'est justeWhy it's right
Le fait est persisté dans la file avant le retour : si tout brûle juste après, le message est toujours là et sera rejoué. Le worker est idempotent — rejouer n'envoie pas deux reçus — et la file retente jusqu'au succès. On a vraiment déplacé la garantie de l'appel synchrone vers l'infrastructure : découplé dans le temps, mais sans rien perdre de « ça finira par être fait ».The fact is persisted in the queue before the return: if everything burns right after, the message is still there and will be replayed. The worker is idempotent — replaying doesn't send two receipts — and the queue retries until success. You genuinely moved the guarantee from the synchronous call to the infrastructure: decoupled in time, but losing nothing of 'it will eventually be done'.
La règle qui tranche.The rule that settles it.
Asynchrone ne veut pas dire « lance et oublie » : il déplace la garantie de l'appel vers une file. Le test : si le process meurt juste après le retour, le travail se fait-il quand même ? Si non, tu n'as pas découplé, tu as supprimé la garantie. Trois pièces obligatoires dès qu'on coupe le fil : la persistance (le message survit), le retry (la file réessaie), l'idempotence (rejouer ne duplique pas). Sans elles, le « async » est un pari.Async doesn't mean 'fire and forget': it moves the guarantee from the call to a queue. The test: if the process dies right after the return, does the work still happen? If not, you didn't decouple, you deleted the guarantee. Three mandatory pieces the moment you cut the thread: persistence (the message survives), retry (the queue tries again), idempotence (replaying doesn't duplicate). Without them, the 'async' is a gamble.
Le piège inverse : mentir à l'utilisateur.The opposite trap: lying to the user.
L'autre excès : passer en async un appel qui gate vraiment la décision. Si tu réponds « commande confirmée » avant que le paiement ait répondu, tu promets un fait qui peut encore échouer — tu as choisi la cohérence à terme là où l'utilisateur exige une cohérence immédiate. Le gate, lui, reste synchrone. Async pour ce qui doit seulement arriver ; sync pour ce qui doit être vrai au moment où on le dit.The other excess: making async a call that genuinely gates the decision. If you answer 'order confirmed' before the payment has responded, you promise a fact that can still fail — you chose eventual consistency where the user demands immediate consistency. The gate stays synchronous. Async for what merely has to happen; sync for what must be true the moment you say it.
№ 05Bilan & éditoWrap-up & editorial2 min
Attendre la réponse, ou déposer le travail et rendre la main.Await the answer, or drop the work and return.
Les deux régimes, côte à côteThe two regimes, side by side
synchronesynchronous
asynchroneasynchronous
La réponseThe answer
tout de suite, on décide avecright away, you decide with it
plus tard, ou jamais luelater, or never read
Latence perçuePerceived latency
la somme des appelsthe sum of the calls
le gate seul, le reste drainethe gate alone, the rest drains
DisponibilitéAvailability
le produit de tousthe product of all
le critique isolé des pannesthe critical path shielded
Quand ne pas passer en asynchroneWhen not to go asynchronous
L'asynchrone a un loyer : une file à exploiter, un travail à suivre sur deux temps, une garantie à reconstruire. On reste synchrone — et c'est correct — quand :Async has rent: a queue to operate, work to track across two timelines, a guarantee to rebuild. Stay synchronous — and that's fine — when:
tu as besoin de la réponse pour décider de la suite : un paiement refusé annule la commande. Rendre ça asynchrone, c'est répondre avant de savoir — et parfois mentir à l'utilisateur.you need the answer to decide what comes next: a declined payment cancels the order. Making that async means answering before you know — and sometimes lying to the user.
l'appel est instantané et la dépendance toujours disponible : une lecture en cache local, un calcul en mémoire. La file n'absorberait aucun pic et n'éviterait aucune panne ; elle n'ajouterait que du suivi — de la cérémonie (YAGNI, Vol 0 #3).the call is instant and the dependency always available: a local cache read, an in-memory computation. The queue would absorb no spike and avoid no outage; it would only add tracking — ceremony (YAGNI, Vol 0 #3).
tu ne peux pas tolérer la cohérence à terme : quand le résultat doit être vrai au moment exact où tu le renvoies, le « ce sera fait un jour » de l'async est précisément la mauvaise sémantique.you can't tolerate eventual consistency: when the result must be true at the exact moment you return it, async's 'it'll be done someday' is precisely the wrong semantics.
Le réflexe n'est donc pas « passe tout en async, c'est plus scalable ». C'est, appel par appel : « ai-je besoin de cette réponse pour décider, ou seulement que ce soit fait un jour ? Si la réponse décide, garde-le synchrone ; si elle ne fait qu'accuser, coupe le fil — mais alors reconstruis la garantie, ne la supprime pas. »So the reflex isn't 'make everything async, it scales better'. It's, call by call: 'do I need this answer to decide, or only that it gets done someday? If the answer decides, keep it synchronous; if it merely acknowledges, cut the thread — but then rebuild the guarantee, don't delete it.'
Mot de l'éditeurFrom the editor
« Asynchrone » est devenu un mot de prestige, comme si attendre était une faute de goût. Mais le synchrone n'est pas lent : il est honnête. Quand l'appel revient, le travail est fait — et cette garantie est gratuite. L'asynchrone ne la rend pas obsolète, il l'échange : on gagne du découplage temporel et on rachète, à la main, ce qui était offert — la certitude que le travail finit par arriver. L'architecte ne demande pas « est-ce async ? » mais « qu'est-ce que j'ai accepté de ne plus savoir tout de suite, et l'ai-je reconstruit ? ».'Asynchronous' has become a prestige word, as if waiting were a lapse of taste. But synchronous isn't slow: it's honest. When the call returns, the work is done — and that guarantee is free. Async doesn't make it obsolete, it trades it: you gain temporal decoupling and buy back, by hand, what was given — the certainty that the work eventually happens. The architect doesn't ask 'is it async?' but 'what did I agree to stop knowing right away, and did I rebuild it?'.
Ce numéro ouvre le volume de la communication avec sa clé : le couplage temporel est un choix, pas un défaut. Observer (Vol 3) avait coupé le savoir ; ici on coupe le temps. Les deux prochains poussent l'idée : la file de messages (#2) fait de la garantie durable la vedette — persistance, pics, pannes ; chorégraphie vs orchestration (#3) demande qui tient le fil de la transaction quand plus personne n'attend en ligne droite.This issue opens the communication volume with its key: temporal coupling is a choice, not a defect. Observer (Vol 3) cut knowledge; here we cut time. The next two push the idea: the message queue (#2) makes the durable guarantee the star — persistence, spikes, outages; choreography vs orchestration (#3) asks who holds the thread of the transaction when no one waits in a straight line anymore.
Les voisins que ce numéro débloqueThe neighbors this issue unlocks
Request/Response — le synchrone nommé : j'envoie, je bloque, je reçois la réponse qui décide de la suite. Le régime par défaut, et le bon tant que la réponse gate une décision.synchronous, named: I send, I block, I receive the answer that decides what comes next. The default regime, and the right one as long as the answer gates a decision.
Fire-and-forget — l'asynchrone dans sa forme la plus pure : j'émets, je ne lis jamais le résultat. Puissant pour ce qui doit seulement arriver — dangereux dès qu'on confond « je n'attends pas » avec « je ne garantis plus ».async in its purest form: I emit, I never read the result. Powerful for what merely has to happen — dangerous the moment you confuse 'I don't wait' with 'I no longer guarantee'.
Outbox pattern — la réponse au piège de ce numéro : écrire le message dans la même transaction que la donnée métier, pour qu'il soit impossible de confirmer sans avoir enregistré le travail à faire. Le pont direct vers la file durable du #2.the answer to this issue's trap: write the message in the same transaction as the business data, so it's impossible to confirm without having recorded the work to do. The direct bridge to #2's durable queue.
Le kata de décision — trace le gateThe decision kata — trace the gate
Pas de machine à écrire ici : le geste qui ancre, c'est de trancher. Voici un checkout réel, six appels sur un seul fil. Pour chaque groupe, parie avant de dérouler — synchrone (la réponse gate une décision) ou asynchrone (ça doit seulement finir par arriver) ? La dernière question est la sournoise : elle sépare « gate la réponse au client » de « gate un fait que la réponse promet ».No machine to type here: the anchoring gesture is to decide. Here's a real checkout, six calls on one thread. For each group, bet before you scroll — synchronous (the answer gates a decision) or asynchronous (it must merely happen eventually)? The last question is the sneaky one: it separates 'gates the answer to the client' from 'gates a fact the answer promises'.
# handle_checkout(order) — six gestes, un seul fil synchrone aujourd'hui.# Pour CHACUN, une seule question : sa réponse décide-t-elle de la suite,# ou doit-il seulement finir par arriver ? C'est ça, tracer le gate.
charge_payment(order) # 1. débite la carte
reserve_stock(order) # 2. décrémente le stock disponible
send_confirmation_email(order) # 3. accuse réception au client
generate_invoice_pdf(order) # 4. produit la facture
push_analytics(order) # 5. incrémente les compteurs produit
refresh_reco_model(order) # 6. réentraîne les recommandations
Chez toi, cette semaine.In your codebase, this week.
Ouvre ton handler le plus chargé — celui qui répond à l'utilisateur après une pile d'appels. Un point de départ : grep -rn "await \|\.send(\|requests\.\|http" src/ | grep -i "handler\|controller\|checkout\|view". Pour chaque appel de la pile, pose l'unique question du kata : « sa réponse gate-t-elle une décision (ou un fait que je promets), ou doit-il seulement finir par arriver ? » Tout ce qui tombe dans la seconde catégorie est un candidat à sortir du fil critique — à condition de reconstruire la garantie, pas de la supprimer.Open your busiest handler — the one that answers the user after a stack of calls. A starting point: grep -rn "await \|\.send(\|requests\.\|http" src/ | grep -i "handler\|controller\|checkout\|view". For each call in the stack, ask the kata's single question: 'does its answer gate a decision (or a fact I promise), or must it merely happen eventually?' Everything in the second category is a candidate to move off the critical thread — provided you rebuild the guarantee, not delete it.