Apex Governor Limits Explained: Write Scalable Salesforce Code

Salesforce is a multitenant platform, so many organisations share underlying resources. Apex governor limits protect that shared environment by restricting resource use within a transaction. Code that works for one record can fail when a bulk import, integration or automation processes many records together.

Exact limits vary by context and platform release, so developers should consult current Salesforce documentation. The durable skill is designing code that performs bounded, bulk-safe work.

Think in transactions

A transaction can include a user action, trigger, flow, validation rule and other automation. Their resource use can accumulate. A query inside a trigger is not isolated from queries performed by invoked logic.

Map the complete automation path. Fixing one class while ignoring recursive updates or another flow may not solve the failure.

Avoid SOQL inside loops

Query all required records before iterating. Collect identifiers in a Set, perform a selective query and store results in a Map for fast lookup.

Set<Id> accountIds = new Set<Id>();
for (Contact contactRecord : Trigger.new) {
    if (contactRecord.AccountId != null) {
        accountIds.add(contactRecord.AccountId);
    }
}

Map<Id, Account> accountsById = new Map<Id, Account>([
    SELECT Id, Industry
    FROM Account
    WHERE Id IN :accountIds
]);

This snippet issues one query each time it runs, rather than one query per contact. If other automation invokes the logic again in the same transaction, that work still consumes limits. Skip the query when there are no account IDs to look up.

Avoid DML inside loops

Create a list of records to insert, update or delete, then perform one data operation after the loop. Besides conserving limits, this makes partial failure and error handling easier to reason about.

Check whether an update is actually required. Writing unchanged records can trigger unnecessary automation and consume resources.

Use collections deliberately

Lists preserve ordered groups, sets keep unique values and maps connect a key to a record or value. Good collection design prevents repeated searches and queries.

Guard against null IDs and empty collections. A query with an empty filter or a map lookup without a key check can create logic errors even when it stays within limits.

Write selective queries

Return only required fields and records. Filter on suitable indexed or selective conditions for large datasets. Review query plans and data distribution when performance matters.

Do not solve a large-data problem only by moving the query. Processing millions of records may need Batch Apex, scheduled processing or a different architecture.

Use asynchronous Apex appropriately

Queueable, batch and scheduled processing can move work outside an interactive transaction, but they have their own limits and operational behaviour. Use them for long-running, callout or large-volume work where the business process allows delay.

Make asynchronous jobs idempotent where possible. Retries should not create duplicate records or external actions.

Prevent recursion and automation collisions

A record update can cause another update and re-enter logic. Use change detection, clear trigger architecture and carefully scoped recursion protection. A static Boolean can be too simplistic for transactions that contain multiple record groups.

Review interactions among Apex, Flow, managed packages and integrations. Consolidate ownership of a business event rather than letting several automations compete.

Test with realistic volume

Unit tests should create bulk record sets and invoke the code once with the collection. Assert business outcomes and use limit inspection selectively for diagnosis. Test error paths, missing relationships and mixed record conditions.

Diagnose a governor-limit failure before refactoring

  1. Identify the failing resource. Read the exception and debug log. A query-count problem, CPU-time problem and excessive query-row problem need different investigations.
  2. Trace the transaction. List the trigger handlers, flows and updates involved. Look for repeated execution, queries inside helpers called from loops and records being updated without a meaningful change.
  3. Reproduce with a controlled dataset. Use a development or test environment with several parent accounts, repeated parent IDs and contacts without an account. Compare the expected records and totals before and after the change.
  4. Check the complete result. Assert the business outcome as well as the resource behaviour. Moving work into asynchronous Apex changes transaction boundaries and timing; it does not remove the need for bulk design or error handling.

For the underlying collection-based patterns, review Salesforce Trailhead on bulk Apex triggers. Use the current linked platform limits for your execution context rather than treating every limit as one universal number.

Practice project

Build an opportunity update handler that recalculates account-level information for many records. Write a deliberately inefficient version, inspect its behaviour, then refactor with sets, maps, one query and one update operation.

Strengthen Apex and platform skills through the Salesforce Developer Training in Vizag. Use a clean Salesforce data model and then progress to the Lightning Web Components roadmap.

Final takeaway

Governor limits reward efficient application design. Bulkify every entry point, query and write outside loops, select only needed data and test realistic transactions. Scalable Apex begins with assuming that every operation may receive many records.