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.
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.
// 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 loopNot 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.
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.
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.
Nimbus tracks nothing. Useful for tests of code that intentionally exceeds limits (to verify exception handling), or when debugging an unrelated issue.
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.
# 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# 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 strictNimbus 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.
| Property | Default | Description |
|---|---|---|
| nimbus.governor.soql-queries | 100 | SOQL queries per transaction |
| nimbus.governor.dml-statements | 150 | DML statements per transaction |
| nimbus.governor.heap-size | 12000000 | Heap 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 |
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.
@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);
}The usual governor violation: one query per record in Trigger.new hits the limit at 101 records.
// ❌ 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);
}One DML per record is the most common bulkification mistake. Collect records and issue a single DML statement.
// ❌ 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;Nimbus enforces the same limits, counts and exceptions locally, before the code reaches an org.