Bite-sized tips to speed up your Salesforce development workflow every day.
Navigate faster with these essential shortcuts.
Open Salesforce global search from anywhere
Run Apex in Developer Console
Launch Developer Console quickly
Jump to specific line in code editor
Save file and compile Apex in Developer Console
Search within the current file in Developer Console
Go back to previous cursor location in VS Code
Fuzzy-search and open any file in VS Code instantly
Essential extensions and configurations for Salesforce DX development.
Install the official extension pack for Apex language support, SOQL highlighting, Lightning Web Components, and org management.
Install Apex PMD for static code analysis. Catch governor limit issues, unused variables, and code smells before deployment.
Create Apex snippets for common patterns: trigger handlers, test methods, SOQL templates, and batch class boilerplate.
Recommended dark theme with excellent Apex syntax highlighting for long coding sessions with reduced eye strain.
Use VS Code's built-in settings sync to keep your Salesforce development environment consistent across machines.
Use the Apex Replay Debugger to step through code execution from debug logs — set breakpoints and inspect variables.
Use the built-in terminal for SFDX commands without switching windows. Split terminals to run deploys and watch logs simultaneously.
Hold Alt and click to place multiple cursors and edit several lines at once — great for bulk field renaming.
Add Prettier with the Salesforce plugin to auto-format HTML, JS, and CSS files on save. Keeps LWC files consistent across your team.
Master Salesforce debugging to resolve issues faster.
Set trace flags on specific users with appropriate log levels. Use FINEST for Apex but INFO for system events to reduce log noise and stay within the 20 active log limit.
// Recommended log levels
Apex Code: FINEST
System: INFO
Validation: INFO
Database: FINE
Workflow: INFO
Don't scatter debug statements randomly. Use labels and JSON serialization for complex objects so logs stay readable.
System.debug('=== AccountTrigger ===');
System.debug('Records: ' +
JSON.serializePretty(newAccounts));
System.debug('SOQL used: ' +
Limits.getQueries() + '/' +
Limits.getLimitQueries());
Use checkpoints in the Developer Console to capture the heap state at specific lines. This shows variable values and object references without adding debug statements to your code.
Always check governor limit consumption in your logs. Look for the LIMIT_USAGE_FOR_NS line at the end of every transaction to identify issues before they hit production.
In the Developer Console log viewer, filter by USER_DEBUG to see only your debug statements, or by SOQL_EXECUTE_BEGIN to audit all queries in a transaction.
Use Execute Anonymous to test logic in isolation before adding it to a class. Great for verifying SOQL output, testing callouts in sandbox, or quickly checking a governor limit.
// Quick limit check
System.debug(Limits.getCpuTime());
System.debug(Limits.getHeapSize());
System.debug(Limits.getQueries());
Write queries that are fast, selective, and governor-limit safe.
This is the #1 governor limit mistake. Move all queries outside of loops and use collections to process results in bulk.
// ❌ Wrong — hits limit fast
for (Account a : accounts) {
List<Contact> c = [SELECT Id FROM
Contact WHERE AccountId = :a.Id];
}
// ✅ Right — one query for all
Map<Id, List<Contact>> cMap = new Map<>();
for (Contact c : [SELECT Id, AccountId
FROM Contact WHERE AccountId IN :accounts]) {
if (!cMap.containsKey(c.AccountId))
cMap.put(c.AccountId, new List<Contact>());
cMap.get(c.AccountId).add(c);
}
Always use Apex bind variables (:varName) instead of string concatenation. They prevent SOQL injection and are automatically optimized by the query engine.
// ❌ Injection risk
String q = 'SELECT Id FROM Account WHERE Name = \''
+ userInput + '\'';
// ✅ Safe bind variable
String name = userInput;
List<Account> accs = [SELECT Id FROM Account
WHERE Name = :name];
Avoid SELECT * patterns. Only include fields you actually use — this reduces heap consumption and makes your query more selective on indexed fields.
Fetch parent and child data in one query instead of separate round trips. Child relationships use a subquery; parent fields use dot notation.
SELECT Id, Name, Account.Name,
(SELECT Id, Subject
FROM Cases)
FROM Contact
WHERE Account.Industry = 'Technology'
Use SELECT COUNT() when you only need a number — it returns an integer immediately without transferring any records and doesn't count against your 50,000 row query limit.
Integer total = [SELECT COUNT()
FROM Opportunity
WHERE StageName = 'Closed Won'
AND CloseDate = THIS_YEAR];
Fields like Id, Name, OwnerId, and custom fields marked as External ID or Unique are indexed. Leading wildcards like LIKE '%value' bypass indexes completely — use trailing wildcards instead: LIKE 'value%'.
Write cleaner, more maintainable Apex with these proven patterns.
Never have multiple triggers on the same object — execution order is non-deterministic. Use a single trigger that delegates to a handler class. This gives you full control over execution order.
Always write code assuming 200 records will be processed at once. If your method takes a single SObject, change it to take a List<SObject>. Triggers always fire in bulk.
Store threshold values, feature flags, and org-specific settings in Custom Metadata Types (CMDT) instead of hardcoding them. CMDTs are deployable, queryable, and don't count against your SOQL limits.
Record IDs change between orgs — sandbox to production, scratch org to scratch org. Use SOQL to look up records by unique names, record types by DeveloperName, or store them in CMDT.
Use Database.insert(records, false) instead of insert records when you want partial success. Check SaveResult for errors per record instead of rolling back the whole transaction.
Use a static Boolean flag in your handler class to prevent trigger recursion. Set it to true after the first execution — subsequent DML operations in the same transaction will skip re-processing.
Nested loops are O(n²) — fine for 5 records, a disaster for 200. Build a Map<Id, SObject> first, then do single lookups inside the loop to keep it O(n).
Use @TestSetup to create shared test data once per test class. It runs before each test method with a rollback, which is faster than recreating data in every individual test.
Know the limits, respect the limits, stay out of production incidents.
The synchronous limit. Use relationship queries, aggregate results, and bulkification to stay under. Check Limits.getQueries() in debug logs.
Synchronous Apex gets 10,000ms. Move heavy computation to async (Queueable/Batch). Avoid string manipulation inside large loops — it's expensive.
Avoid storing large collections in memory. Process records in batches, clear variables when done, and never serialize entire objects unless necessary.
A single transaction can't hold more than 50k records in memory. Use Database.QueryLocator in Batch Apex to bypass this for large data volumes.
Batch all inserts, updates, and deletes into lists. Never call DML inside a loop. One update myList counts as one DML regardless of list size.
Callouts can't be made after DML in the same transaction. Use @future(callout=true), Queueable with Database.AllowsCallouts, or Platform Events to decouple them.
Write secure code that respects Salesforce's sharing and access model.
Always declare with sharing on service and controller classes unless you explicitly need to bypass sharing rules. When you do bypass, isolate it in a dedicated inner class to minimise scope.
public with sharing class AccountService {
// Respects sharing rules ✅
public static List<Account> getAccounts() {
return [SELECT Id FROM Account];
}
// Isolated bypass for internal use only
private without sharing class Admin {
List<Account> getAllAccounts() {
return [SELECT Id FROM Account];
}
}
}
Use Security.stripInaccessible() before DML to automatically remove fields the running user can't read or write — far safer than manually checking FLS on every field.
SObjectAccessDecision decision =
Security.stripInaccessible(
AccessType.CREATABLE, records);
insert decision.getRecords();
Any value arriving from an LWC, Aura component, or API can be tampered with. Never trust @AuraEnabled method parameters — validate type, length, and format on the server side before processing.
Dynamic SOQL with user input is a critical vulnerability. Use String.escapeSingleQuotes() when dynamic SOQL is unavoidable, and prefer static SOQL with bind variables wherever possible.
// Last resort — always escape
String safe = String.escapeSingleQuotes(input);
String q = 'SELECT Id FROM Account WHERE Name = \''
+ safe + '\'';
Speed up your local development workflow with these CLI commands and habits.
Always set an alias when authorising an org. It saves you from copying 18-character usernames into every command.
sf org login web --alias mydev --set-default
Use --source-dir to deploy a specific folder instead of the entire project. Much faster for iterative development.
sf project deploy start \
--source-dir force-app/main/default/classes/AccountService.cls \
--target-org mydev
Don't run your entire test suite every deploy during development. Target the test class that covers your changes.
sf project deploy start \
--source-dir force-app/main/default/classes \
--test-level RunSpecifiedTests \
--tests AccountServiceTest \
--target-org mydev
Stream debug logs directly to your terminal without opening Developer Console. Pipe through grep to filter noise.
sf apex tail log --target-org mydev | grep USER_DEBUG
Retrieve any metadata type from an org back to your local project. Useful when changes are made declaratively in Setup.
sf project retrieve start \
--metadata ApexClass:AccountService \
--target-org mydev
Execute a local script file against any org directly from the terminal — great for data fixes, seeding records, and quick tests.
sf apex run --file scripts/fix-accounts.apex \
--target-org mydev
Ship with confidence using these deployment habits.
Always run a validation-only deployment first to catch errors without impacting the org.
Schedule deployments during low-usage windows to minimise user impact and reduce lock conflicts.
Organise related metadata into unlocked packages for modular, repeatable deployments across sandboxes.
Always have a documented rollback strategy with previous metadata snapshots ready to restore.
Run full regression tests in a full-copy sandbox before any production deployment, not just Developer sandboxes.
Set up GitHub Actions with SFDX for automated test runs and deployment on every pull request merge.
A successful validation within 10 days unlocks Quick Deploy — skip re-running all tests and deploy in seconds.
Deploy only the components that changed. Deploying everything every time slows you down and increases rollback risk.
For critical production deployments, use the "Prevent Backups" option and communicate a change window to users beforehand.