Retour aux Insights

Salesforce Governor Limits : comprendre, anticiper et contourner

Pierre Frin Mai 2026 10 min de lecture
SOQL Queries 80% DML Statements 95% CPU Time 50% 100 SOQL max 150 DML max 10k SOQL rows 10s CPU Apex sync System.LimitException: Too many SOQL queries: 101 SALESFORCE · GOVERNOR LIMITS · APEX

Les governor limits sont l'une des premières choses qui surprennent les développeurs qui arrivent sur Salesforce depuis d'autres plateformes. Elles imposent des plafonds stricts sur les ressources consommées par chaque transaction Apex, et les dépasser génère immédiatement une exception qui bloque l'exécution. Comprendre ces limites, c'est comprendre comment Salesforce fonctionne vraiment.

Pourquoi les governor limits existent

Salesforce est une plateforme multi-tenant, des milliers d'organisations partagent la même infrastructure. Pour garantir que le code d'un client ne monopolise pas les ressources au détriment des autres, Salesforce impose des limites par transaction sur chaque type de ressource. Ce n'est pas un bug ni une limitation artificielle, c'est le mécanisme qui rend la plateforme stable à grande échelle.

Chaque fois qu'un trigger Apex se déclenche, qu'un Flow appelle du code Apex ou qu'un batch s'exécute, un compteur de ressources démarre. Quand une limite est atteinte, Salesforce lève une System.LimitException et rollback la transaction entière.

Les limites essentielles à connaître

SOQL Queries

100
Requêtes SOQL maximum par transaction synchrone. 200 en asynchrone (batch, future, queueable).

SOQL Query Rows

50 000
Nombre maximum d'enregistrements retournés par toutes les requêtes SOQL combinées.

DML Statements

150
Opérations DML (insert, update, delete, upsert) maximum par transaction.

DML Rows

10 000
Nombre maximum d'enregistrements modifiés par toutes les opérations DML combinées.

CPU Time

10s / 60s
10 secondes en synchrone, 60 secondes en asynchrone. Temps CPU Apex pur, hors I/O.

Heap Size

6 MB / 12 MB
Mémoire allouée à la transaction : 6 MB en synchrone, 12 MB en asynchrone.

💡 Vérifier les limites en temps réel : la classe Limits expose les compteurs courants. Limits.getQueries() retourne le nombre de SOQL consommés, Limits.getLimitQueries() la limite maximale. Utile pour instrumenter du code et détecter des dépassements avant qu'ils arrivent en production.

L'erreur classique : SOQL dans une boucle

C'est l'erreur la plus fréquente sur Salesforce, et elle touche même des développeurs expérimentés qui arrivent d'autres langages. Mettre une requête SOQL à l'intérieur d'une boucle for consomme une requête par itération, et sur 101 enregistrements, c'est terminé.

❌ Pattern à éviter, SOQL dans une boucle
// SOQL dans la boucle = LimitException dès 101 enregistrements
for (Account acc : trigger.new) {
    List<Contact> contacts = [
        SELECT Id, Email
        FROM Contact
        WHERE AccountId = :acc.Id  // ← une requête par Account !
    ];
    // traitement...
}
✅ Pattern correct, SOQL bulkifié
// Une seule requête pour tous les enregistrements du batch
Set<Id> accountIds = new Set<Id>();
for (Account acc : trigger.new) {
    accountIds.add(acc.Id);
}

Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for (Contact c : [
    SELECT Id, Email, AccountId
    FROM Contact
    WHERE AccountId IN :accountIds  // ← une seule requête
]) {
    if (!contactsByAccount.containsKey(c.AccountId)) {
        contactsByAccount.put(c.AccountId, new List<Contact>());
    }
    contactsByAccount.get(c.AccountId).add(c);
}

DML dans une boucle : même problème

La même erreur existe côté DML. Faire un update ou un insert à l'intérieur d'une boucle consomme une opération DML par itération. Sur 151 enregistrements, la limite est atteinte.

❌ DML dans la boucle
for (Account acc : accountsToUpdate) {
    acc.Description = 'Mis à jour';
    update acc;  // ← un DML par Account !
}
✅ DML bulkifié, une seule opération
List<Account> toUpdate = new List<Account>();
for (Account acc : accountsToUpdate) {
    acc.Description = 'Mis à jour';
    toUpdate.add(acc);
}
update toUpdate;  // ← un seul DML pour tous

CPU Time : le piège des traitements intensifs

La limite CPU time (10 secondes en synchrone) ne prend en compte que le temps d'exécution Apex pur, pas le temps d'attente des requêtes SOQL ou des appels HTTP. En pratique, elle est rarement atteinte sur du code simple, mais elle devient critique sur :

Le contournement principal : passer en asynchrone. Un @future, un Queueable ou un Batchable dispose de 60 secondes de CPU au lieu de 10.

Heap Size : mémoire et grandes collections

La limite heap size (6 MB en synchrone) est souvent atteinte quand on charge de grandes collections en mémoire, par exemple en récupérant tous les champs d'un objet avec SELECT * ou en stockant des listes volumineuses dans des variables statiques.

Tableau de référence

LimiteSynchroneAsynchroneErreur classique
SOQL Queries100200SOQL dans boucle
SOQL Rows50 00050 000SELECT sans filtre
DML Statements150150DML dans boucle
DML Rows10 00010 000Batch trop large
CPU Time10s60sBoucles imbriquées
Heap Size6 MB12 MBSELECT * sur gros objet
Callouts HTTP100100Callout dans trigger
Future methods50—@future en boucle

Stratégies pour travailler avec les limits

1. Toujours coder en mode bulkifié

Chaque trigger Apex peut recevoir jusqu'à 200 enregistrements dans un même batch. Tout le code doit être écrit pour traiter une collection, jamais un seul enregistrement. C'est la règle n°1 du développement Apex.

2. Passer en asynchrone pour les traitements lourds

Quand un traitement risque de dépasser les limites synchrones, il faut le déplacer en asynchrone. Salesforce propose plusieurs patterns :

3. Surveiller les limits avec la classe Limits

En développement, instrumenter son code avec System.debug(Limits.getQueries()) permet de surveiller la consommation réelle et d'identifier les points chauds avant qu'ils arrivent en production.

🚨 Attention aux triggers en cascade : un trigger sur l'objet A qui met à jour l'objet B peut déclencher un trigger sur B, qui met à jour C… Les governor limits s'appliquent à la transaction entière, pas à chaque trigger individuellement. Une cascade mal maîtrisée peut dépasser les limites même avec du code individuel parfaitement bulkifié.

Conclusion

Les governor limits ne sont pas un obstacle, ce sont des garde-fous qui forcent à écrire du code performant et scalable. Un développeur Salesforce qui maîtrise les limits écrit naturellement du code bulkifié, pense en termes de collections plutôt que d'enregistrements unitaires, et choisit le bon pattern asynchrone selon le contexte. C'est cette maîtrise qui fait la différence entre un code qui tient en production et un code qui plante dès que le volume augmente.

Pierre Frin
Fondateur Grokium · Consultant Salesforce Sales Cloud · 10 ans d'expérience

Un projet Salesforce à optimiser ?

Architecture, développement Apex, revue de code, je peux vous accompagner. Réponse sous 24h.

Parlons de votre projet →
Aller plus loin
Mes prestations CRM Tous les articles techniques Me contacter