Passez à AWS Step Functions dès que vos fonctions Lambda commencent à s'orchestrer entre elles : appels synchrones de fonction à fonction, avancement suivi à la main dans DynamoDB, boucles de retry copiées-collées dans chaque handler. Une machine à états déplace les retries, les branchements, les timeouts et les attentes humaines dans une définition déclarative que l'on peut lire, tester et rejouer. Et depuis Distributed Map, le même service couvre le fan-out de traitements par lots à grande échelle, jusqu'à 10 000 exécutions enfants en parallèle, un travail qui exigeait auparavant une flotte de consommateurs SQS.
Quand l'orchestration Lambda a-t-elle besoin d'une machine à états ?
Le signe le plus fiable : une fonction Lambda dont le rôle principal est d'appeler d'autres fonctions Lambda. Quand les handlers s'invoquent en synchrone, consignent l'avancement dans une table DynamoDB maison et embarquent chacun sa copie de la boucle de retry, vous exploitez déjà une machine à états : invisible, et éparpillée dans le code.
Le chaînage synchrone a des coûts concrets. L'appelant paie toute la durée de l'appelé, les timeouts s'empilent vers le plafond des 15 minutes de Lambda, et un crash en milieu de chaîne laisse une colonne status que personne ne peut rejouer sereinement. Le comportement de retry dérive, chaque handler l'implémentant à sa façon. Dans les bases de code serverless que nous auditons, c'est là que se cachent les échecs partiels : une commande marquée PAID dont l'étape d'expédition n'a jamais tourné.
Qu'apporte une machine à états que vos handlers ne peuvent pas offrir ?
Un flux de contrôle déclaratif : Retry par état avec backoff exponentiel et jitter, Catch vers des états de récupération, timeouts et heartbeats par état, et un historique d'exécution visuel qui montre l'entrée et la sortie de chaque état. Les handlers redeviennent de la pure logique métier ; l'orchestration vit dans un document JSON relisible.
Deux capacités vont plus loin qu'une plomberie mieux rangée. Avec un task token (.waitForTaskToken), un workflow Standard se met en pause, des heures ou des mois si nécessaire, jusqu'au rappel d'un humain ou d'un système externe : des étapes d'approbation sans boucle de polling ni cron. Et les intégrations SDK permettent à un état d'appeler directement DynamoDB, SQS ou Bedrock, sur plus de deux cents services AWS (documentation AWS, 2026), en supprimant des fonctions de glu qu'il faudrait sinon écrire, maintenir et payer.
Des fonctions Lambda qui s'orchestrent entre elles en production ? Décrivez votre système : diagnostic d'une page sous 48 h.
Recevoir mon diagnostic →Standard ou Express : quel type de workflow choisir ?
Choisissez Standard pour les processus métier : sémantique exactly-once, exécutions jusqu'à un an, task tokens, 90 jours d'historique interrogeable. Choisissez Express pour l'événementiel court, massif et idempotent : plafond de cinq minutes, sémantique at-least-once, historique via CloudWatch Logs. Le type est immuable après création : décidez machine par machine.
| Critère | Chaînage Lambda maison | Step Functions Standard | Step Functions Express |
|---|---|---|---|
| Retries | Écrits à la main par handler, dérivent avec le temps | Déclaratifs par état : backoff, jitter, tentatives max | Même modèle déclaratif |
| Observabilité | Logs CloudWatch épars, IDs de corrélation maison | Historique complet par état, débogage visuel, rétention 90 jours | Historique via CloudWatch Logs, selon le niveau de log |
| Limites de durée | 15 min par fonction, les chaînes empilent les timeouts | Jusqu'à un an | Jusqu'à cinq minutes |
| Forme du coût | Invocations, plus l'attente payée pendant les appels | Par transition d'état | Par nombre d'exécutions, durée et mémoire |
| Cas adaptés | Glu en deux étapes, scripts jetables | Processus métier, approbations humaines, étapes non idempotentes | Événementiel court, massif et idempotent |
Les formes de coût diffèrent plus que le modèle affiché ne le laisse penser. Standard facture par transition d'état : une machine bavarde, faite de nombreux petits états, coûte plus cher qu'une machine compacte. Express facture au nombre d'exécutions, à la durée et à la mémoire, ce qui reste raisonnable en rafales courtes mais mérite un calcul avant d'atterrir sur un chemin de requête chaud. La comparaison officielle détaille tous les compromis.
Que change Distributed Map pour les traitements par lots ?
Distributed Map transforme un simple état Map en moteur de fan-out : il lit les éléments directement depuis un préfixe S3, un fichier JSON ou CSV, ou un inventaire S3, puis exécute chaque élément ou lot comme une exécution enfant à part entière, jusqu'à 10 000 en parallèle (quota documenté, 2026).
Avant son arrivée, la recette classique pour « traiter un million d'objets » était une file SQS et une flotte de consommateurs Lambda, avec batching, retries et suivi d'avancement faits main. Distributed Map remplace cette plomberie pour le batch : chaque exécution enfant a sa politique de retry et son historique, ItemBatcher regroupe les éléments pour amortir le coût d'invocation, ToleratedFailurePercentage évite qu'un élément toxique fasse échouer tout le lot, et un ResultWriter agrège les résultats vers S3. Les enfants peuvent tourner en Express pour le coût, pendant que le parent Standard conserve la piste d'audit :
{
"Comment": "Pipeline de commande : retry avec backoff, compensation en cas d'échec, fan-out distribué",
"StartAt": "ValidateOrder",
"States": {
"ValidateOrder": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": { "FunctionName": "validate-order" },
"Retry": [{
"ErrorEquals": ["Lambda.ServiceException", "States.Timeout"],
"IntervalSeconds": 2, "MaxAttempts": 4,
"BackoffRate": 2.0, "JitterStrategy": "FULL"
}],
"Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "CompensateOrder" }],
"Next": "ProcessItems"
},
"ProcessItems": {
"Comment": "Distributed Map : une exécution enfant Express par lot de 100 objets",
"Type": "Map",
"ItemReader": {
"Resource": "arn:aws:states:::s3:listObjectsV2",
"Parameters": { "Bucket": "orders-inbox", "Prefix": "2026/08/" }
},
"ItemBatcher": { "MaxItemsPerBatch": 100 },
"MaxConcurrency": 1000,
"ToleratedFailurePercentage": 1,
"ItemProcessor": {
"ProcessorConfig": { "Mode": "DISTRIBUTED", "ExecutionType": "EXPRESS" },
"StartAt": "HandleBatch",
"States": {
"HandleBatch": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": { "FunctionName": "process-order-batch" },
"End": true
}
}
},
"End": true
},
"CompensateOrder": {
"Comment": "Annuler les effets de bord, mettre l'entrée de côté pour replay, puis échouer explicitement",
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": { "FunctionName": "cancel-order" },
"Next": "OrderFailed"
},
"OrderFailed": { "Type": "Fail", "Error": "OrderProcessingFailed" }
}
}
Quand Step Functions est-il le mauvais outil ?
Passez votre chemin dans trois cas. Un flux linéaire simple (une fonction, un appel d'API, une écriture) ne gagne rien à la machine à états, sinon de la latence et un artefact de déploiement de plus. Un chemin synchrone à fort volume mérite un calcul : même la facturation Express à chaque exécution peut perdre face à une Lambda unique bien conçue. Et les événements qui traversent les frontières d'équipes relèvent de la chorégraphie, pas de l'orchestration.
Notre règle : orchestrez un processus que vous possédez de bout en bout ; chorégraphiez les événements entre domaines. Si l'équipe commande veut seulement annoncer OrderPlaced et laisser facturation et expédition réagir, c'est une topologie EventBridge, détaillée dans notre article sur le trio EventBridge, SQS et Lambda. Les deux se combinent bien : un événement déclenche une machine à états qui possède un processus borné.
Comment concevoir les chemins d'échec ?
Donnez à chaque machine une branche d'échec explicite, l'équivalent d'une file de lettres mortes. Routez States.ALL depuis un Catch vers un état de compensation qui annule les effets de bord (relâcher le stock, annuler l'autorisation), puis persistez l'entrée d'origine dans un endroit rejouable, S3 ou SQS, avant de terminer sur un état Fail nommé.
Ce que nous refusons de livrer : une machine dont la seule gestion d'échec est l'abandon par défaut, car elle recrée le problème de la « commande bloquée dans DynamoDB » que la migration devait résoudre. Posez une alarme sur la métrique ExecutionsFailed, gardez les états de compensation idempotents (eux aussi seront rejoués) et répétez un replay depuis les entrées mises de côté. Cette revue des chemins d'échec est souvent le point de départ de nos missions d'architecture AWS.
Check-list d'adoption
- Recensez les fonctions Lambda dont le rôle principal est d'invoquer d'autres fonctions : ce sont vos premières candidates.
- Choisissez le type de workflow avant de créer chaque machine ; il est immuable ensuite.
- Déplacez chaque retry écrit à la main vers des blocs
Retrydéclaratifs avec backoff et jitter, puis supprimez les copies côté handler. - Remplacez les fonctions de glu par des intégrations SDK directes dès qu'un état ne fait qu'appeler une API AWS.
- Pour le batch, prototypez Distributed Map avec
ItemBatcheravant de construire une nouvelle file et sa flotte de consommateurs. - Donnez à chaque
Catchun chemin de compensation qui met l'entrée en échec de côté, dans un endroit rejouable. - Laissez les événements inter-domaines sur EventBridge ; n'orchestrez que les processus que vous possédez de bout en bout.