Retour aux InsightsBack to Insights

Salesforce Governor Limits : comprendre, anticiper et contourner Salesforce Governor Limits: understand, anticipate and work around them

Pierre Frin Mai 2026May 2026 10 min de lecture10 min read
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.

Governor limits are one of the first things that surprise developers coming to Salesforce from other platforms. They impose strict ceilings on resources consumed by each Apex transaction — and exceeding them immediately raises an exception that rolls back execution. Understanding these limits means understanding how Salesforce really works.

Why governor limits exist

Salesforce is a multi-tenant platform — thousands of organisations share the same infrastructure. To ensure one client's code doesn't monopolise resources at others' expense, Salesforce enforces per-transaction limits on each resource type. This isn't a bug or artificial limitation — it's the mechanism that keeps the platform stable at scale.

Every time an Apex trigger fires, a Flow calls Apex code, or a batch runs, a resource counter starts. When a limit is reached, Salesforce raises a System.LimitException and rolls back the entire transaction.

The essential limits to know

SOQL Queries

100
Maximum SOQL queries per synchronous transaction. 200 in async context (batch, future, queueable).

SOQL Query Rows

50,000
Maximum records returned by all combined SOQL queries.

DML Statements

150
DML operations (insert, update, delete, upsert) maximum per transaction.

DML Rows

10,000
Maximum records modified by all combined DML operations.

CPU Time

10s / 60s
10 seconds synchronous, 60 seconds asynchronous. Pure Apex CPU time, excluding I/O.

Heap Size

6 MB / 12 MB
Memory allocated to the transaction: 6 MB synchronous, 12 MB asynchronous.

💡 Check limits in real time: the Limits class exposes current counters. Limits.getQueries() returns SOQL consumed, Limits.getLimitQueries() the maximum limit. Useful for instrumenting code and detecting overruns before they hit production.

The classic mistake — SOQL inside a loop

This is the most frequent mistake on Salesforce, and it catches even experienced developers coming from other languages. Putting a SOQL query inside a for loop consumes one query per iteration — and at 101 records, it's over.

❌ Pattern to avoid — SOQL inside a loop
// SOQL in loop = LimitException from 101 records
for (Account acc : trigger.new) {
    List<Contact> contacts = [
        SELECT Id, Email
        FROM Contact
        WHERE AccountId = :acc.Id  // ← one query per Account!
    ];
    // processing...
}
✅ Correct pattern — bulkified SOQL
// A single query for all records in the 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  // ← a single query
]) {
    if (!contactsByAccount.containsKey(c.AccountId)) {
        contactsByAccount.put(c.AccountId, new List<Contact>());
    }
    contactsByAccount.get(c.AccountId).add(c);
}

DML inside a loop — same problem

The same mistake exists on the DML side. Doing an update or insert inside a loop consumes one DML operation per iteration. At 151 records, the limit is reached.

❌ DML inside the loop
for (Account acc : accountsToUpdate) {
    acc.Description = 'Updated';
    update acc;  // ← one DML per Account!
}
✅ Bulkified DML — a single operation
List<Account> toUpdate = new List<Account>();
for (Account acc : accountsToUpdate) {
    acc.Description = 'Updated';
    toUpdate.add(acc);
}
update toUpdate;  // ← a single DML for all

CPU Time — the intensive processing trap

The CPU time limit (10 seconds synchronous) only counts pure Apex execution time — not SOQL query wait time or HTTP call wait time. In practice, it's rarely hit on simple code, but becomes critical with:

The main workaround: go asynchronous. A @future, Queueable or Batchable has 60 seconds of CPU instead of 10.

Heap Size — memory and large collections

The heap size limit (6 MB synchronous) is often hit when loading large collections into memory — for example retrieving all fields with SELECT * or storing large lists in static variables.

Reference table

LimitSynchronousAsynchronousClassic mistake
SOQL Queries100200SOQL in loop
SOQL Rows50,00050,000SELECT without filter
DML Statements150150DML in loop
DML Rows10,00010,000Batch too large
CPU Time10s60sNested loops
Heap Size6 MB12 MBSELECT * on large object
HTTP Callouts100100Callout in trigger
Future methods50@future in loop

Strategies for working with limits

1. Always code in bulkified mode

Each Apex trigger can receive up to 200 records in the same batch. All code must be written to process a collection, never a single record. This is rule #1 of Apex development.

2. Go async for heavy processing

When processing risks exceeding synchronous limits, move it asynchronous. Salesforce offers several patterns:

3. Monitor limits with the Limits class

In development, instrumenting code with System.debug(Limits.getQueries()) allows monitoring real consumption and identifying hotspots before they reach production.

🚨 Watch out for cascading triggers: a trigger on object A that updates object B can fire a trigger on B, which updates C… Governor limits apply to the entire transaction, not each trigger individually. An uncontrolled cascade can exceed limits even with perfectly bulkified individual code.

Conclusion

Governor limits aren't an obstacle — they're guardrails that force writing performant, scalable code. A Salesforce developer who masters limits naturally writes bulkified code, thinks in terms of collections rather than individual records, and chooses the right async pattern for the context. This mastery is what distinguishes code that holds in production from code that breaks as soon as volume increases.

Pierre Frin
Fondateur Grokium — Consultant Salesforce Sales Cloud · 8 ans d'expérienceFounder Grokium — Salesforce Sales Cloud Consultant · 8 years experience

Un projet Salesforce à optimiser ?A Salesforce project to optimise?

Architecture, développement Apex, revue de code — je peux vous accompagner. Réponse sous 24h.Architecture, Apex development, code review — I can support you. Reply within 24 hours.

Parlons de votre projet →Let's talk →