Salesforce enforces hard caps per transaction. The big ones:
| Limit | Sync | Async | What hits it | |---|---|---|---| | SOQL queries | 100 | 200 | Loops with SOQL | | SOQL rows | 50,000 | 50,000 | Massive single query | | DML statements | 150 | 150 | Loops with DML | | DML records | 10,000 | 10,000 | Single bulk DML | | CPU time | 10s | 60s | Heavy computation, recursive flow | | Heap size | 6 MB | 12 MB | Large collections | | Callouts | 100 | 100 | API integrations | | Future calls / queueable | 50 | 50 | Triggering many async jobs |
Patterns to stay under:
- Bulkify everything. Never SOQL or DML inside loops. Build collections, do one query/DML.
- Use Maps for lookups, not SOQL. If you need parent records: query parents into a Map by Id, then look up in-memory.
- Selective queries. WHERE clause must hit indexed fields. Without selectivity, queries against large tables fail or time out (separate from row limits).
- Don't load more than you need.
SELECT Id, Name FROM AccountnotSELECT * FROM Account. Reduces heap and SOQL row count.
- Use QueryLocator in Batch for >50k rows.
Database.getQueryLocator()returns iterator, not a static list.
- Async for heavy work. Move CPU/heap-heavy logic into Queueable or Batch — they get more headroom.
- Limit governor-limit counter awareness.
Limits.getQueries(),Limits.getDmlStatements(), etc. — read at runtime to make conditional decisions.
- Static caching across method calls in the same transaction — store results of expensive queries in a static Map.
- Avoid recursion. Use static flags. Each re-entry adds to limits.
Common mistakes:
- A trigger handler that re-queries
[SELECT ... FROM Contact WHERE AccountId IN :accIds]from inside another method, when the parent already has it. - A flow that loops over Get Records results and does Update inside the loop.
- An LWC
@wireApex method that does heavy work for every page render. - A managed package that adds 30 SOQL queries per save without coordinating.
The discipline: every method that does SOQL or DML should accept and return a list, not a single record. That's the fundamental shift.
