Governor Limits

Governor limits enforced
before the deploy

Salesforce governor limits are invisible until you hit them in production. Nimbus enforces the same limits, with the same counts and the same errors, on your machine, so you find out during development rather than during a release.

The problem with governor limits

Salesforce enforces resource limits per transaction: 100 SOQL queries, 150 DML statements, 12 MB of heap space. Exceed any of them and Salesforce throws a runtime exception that rolls back the whole transaction, with no warning and no partial commit. Often that happens in production, after a data volume increase your tests never exercised.

Limits do not show up in unit tests at low data volumes. A trigger that runs 3 SOQL queries per record is fine in a test with 10 records. In production with 200 records in a single batch, it passes the limit on the first bulkified operation. 95% test coverage did not catch it because the tests never reached the boundary.

Without local execution there is no way to observe limit consumption during development; you deploy and find out. With Nimbus you find out locally.

apex
// This trigger looks fine in a single-record test
trigger AccountTrigger on Account (before insert) {
    for (Account acc : Trigger.new) {
        // ❌ SOQL inside a loop - 1 query per record
        List<Contact> contacts = [
            SELECT Id FROM Contact WHERE AccountId = :acc.Id
        ];
        acc.Contact_Count__c = contacts.size();
    }
}

// With 10 records: 10 SOQL queries - passes
// With 101 records: 101 SOQL queries - System.LimitException
// nimbus test (strict mode): fails immediately, points at the loop

Three enforcement modes

Not every context needs the same enforcement. Local development wants fast feedback without blocking on every limit. CI is the quality gate and should be strict. The nimbus.governor.mode setting selects the mode.

strict

The test fails

Exceeding a limit fails the test immediately with a System.LimitException, exactly as Salesforce would. Use this in CI to enforce limits as a quality gate before every merge.

warn

A warning is logged

The test continues but a warning is printed. You see where the violation happened without the test failing. Good for local development: you see the limits without being blocked.

off

Limits are ignored

Nimbus tracks nothing. Useful for tests of code that intentionally exceeds limits (to verify exception handling), or when debugging an unrelated issue.

Different modes for different contexts

The usual configuration is warn locally, so you see violations without being blocked, and strict in CI, so violations do not reach main.

Define this once with a profile in the committed nimbus.properties file and set NIMBUS_PROFILE=ci in the pipeline. There are no wrapper scripts and no per-developer configuration.

The off mode is also useful in a dedicated profile for exception-handling paths that expect a limit to be hit: NIMBUS_PROFILE=limits-off nimbus test ExceptionTest.

properties
# nimbus.properties

# Local: warn on violations, stay unblocked
nimbus.governor.mode=warn

# CI: strict enforcement, fail on any violation
%ci.nimbus.governor.mode=strict

# Optionally tune the limits themselves
# (defaults match Salesforce's synchronous limits)
nimbus.governor.soql-queries=100
nimbus.governor.dml-statements=150
nimbus.governor.heap-size=12000000
bash
# Local development - violations warn, don't fail
nimbus test

# CI - violations fail the test
NIMBUS_PROFILE=ci nimbus test

# One-off: run with strict mode for a check
nimbus test --governor-mode strict

What gets tracked

Nimbus tracks governor limit consumption per transaction and resets counters between tests. Each test method runs in isolation, so limits from one test do not carry into the next.

PropertyDefaultDescription
nimbus.governor.soql-queries100SOQL queries per transaction
nimbus.governor.dml-statements150DML statements per transaction
nimbus.governor.heap-size12000000Heap size in bytes (12 MB)
Limits.getLimitQueries()-Read the current limit in Apex, the same as in an org
Limits.getQueries()-Read current consumption, for testing governor-aware code

Testing your own governor-aware code

Code that checks Limits.getQueries() and adjusts its behavior is testable locally too. Nimbus tracks consumption the same way, so assertions on limit values behave as they would in an org.

apex
@isTest
static void testGovernorAwareBulkOperation() {
    // Verify your service respects query limits
    List<Account> accounts = new List<Account>();
    for (Integer i = 0; i < 50; i++) {
        accounts.add(new Account(Name = 'Test ' + i));
    }
    insert accounts;

    Integer queriesBefore = Limits.getQueries();
    BulkAccountService.processAll(accounts);
    Integer queriesUsed = Limits.getQueries() - queriesBefore;

    // Assert bulkification: should be 1 query for 50 records, not 50
    System.assert(queriesUsed <= 3,
        'Expected bulkified queries, got ' + queriesUsed);
}

Common patterns that hit limits

SOQL in a loop

The usual governor violation: one query per record in Trigger.new hits the limit at 101 records.

apex
// ❌ 1 query per record
for (Account acc : Trigger.new) {
    List<Contact> c = [SELECT Id FROM Contact
                        WHERE AccountId = :acc.Id];
}

// ✓ 1 query total
Set<Id> ids = new Map<Id, Account>(Trigger.new).keySet();
Map<Id, List<Contact>> cMap = new Map<Id, List<Contact>>();
for (Contact c : [SELECT Id, AccountId FROM Contact
                  WHERE AccountId IN :ids]) {
    if (!cMap.containsKey(c.AccountId))
        cMap.put(c.AccountId, new List<Contact>());
    cMap.get(c.AccountId).add(c);
}

DML in a loop

One DML per record is the most common bulkification mistake. Collect records and issue a single DML statement.

apex
// ❌ 1 DML per record
for (Account acc : accounts) {
    acc.Status__c = 'Active';
    update acc;
}

// ✓ 1 DML total
for (Account acc : accounts) {
    acc.Status__c = 'Active';
}
update accounts;

Limit violations found in development

Nimbus enforces the same limits, counts and exceptions locally, before the code reaches an org.