home Home build Tools bug_report Errors menu_book Guides lightbulb Tips smart_toy Prompts extension Extensions folder_open Resources info About
search
Shortcuts

Salesforce Keyboard Shortcuts

Navigate faster with these essential shortcuts.

Ctrl /

Global Search

Open Salesforce global search from anywhere

Ctrl E

Execute Anonymous

Run Apex in Developer Console

Ctrl Shift C

Open Console

Launch Developer Console quickly

Ctrl G

Go to Line

Jump to specific line in code editor

Ctrl S

Save & Compile

Save file and compile Apex in Developer Console

Ctrl F

Find in File

Search within the current file in Developer Console

Alt

Navigate Back

Go back to previous cursor location in VS Code

Ctrl P

Quick Open File

Fuzzy-search and open any file in VS Code instantly

VS Code

VS Code Tips for Salesforce

Essential extensions and configurations for Salesforce DX development.

extension

Salesforce Extension Pack

Install the official extension pack for Apex language support, SOQL highlighting, Lightning Web Components, and org management.

auto_fix

Apex PMD Linting

Install Apex PMD for static code analysis. Catch governor limit issues, unused variables, and code smells before deployment.

snippet_folder

Custom Snippets

Create Apex snippets for common patterns: trigger handlers, test methods, SOQL templates, and batch class boilerplate.

palette

Theme: One Dark Pro

Recommended dark theme with excellent Apex syntax highlighting for long coding sessions with reduced eye strain.

sync

Settings Sync

Use VS Code's built-in settings sync to keep your Salesforce development environment consistent across machines.

bug_report

Replay Debugger

Use the Apex Replay Debugger to step through code execution from debug logs — set breakpoints and inspect variables.

terminal

Integrated Terminal

Use the built-in terminal for SFDX commands without switching windows. Split terminals to run deploys and watch logs simultaneously.

manage_search

Multi-Cursor Editing

Hold Alt and click to place multiple cursors and edit several lines at once — great for bulk field renaming.

format_align_left

Prettier for LWC

Add Prettier with the Salesforce plugin to auto-format HTML, JS, and CSS files on save. Keeps LWC files consistent across your team.

Debugging

Debugging Techniques

Master Salesforce debugging to resolve issues faster.

Use Trace Flags Effectively

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

Strategic System.debug()

Don't scatter debug statements randomly. Use labels and JSON serialization for complex objects so logs stay readable.

Apex
System.debug('=== AccountTrigger ===');
System.debug('Records: ' + 
    JSON.serializePretty(newAccounts));
System.debug('SOQL used: ' + 
    Limits.getQueries() + '/' + 
    Limits.getLimitQueries());

Checkpoint Inspector

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.

Debug with Limits Monitoring

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.

Filter Logs with Keywords

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.

Anonymous Apex for Quick Tests

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.

Apex
// Quick limit check
System.debug(Limits.getCpuTime());
System.debug(Limits.getHeapSize());
System.debug(Limits.getQueries());
SOQL

SOQL Best Practices

Write queries that are fast, selective, and governor-limit safe.

Never Query Inside Loops

This is the #1 governor limit mistake. Move all queries outside of loops and use collections to process results in bulk.

Apex
// ❌ 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);
}

Use Bind Variables

Always use Apex bind variables (:varName) instead of string concatenation. They prevent SOQL injection and are automatically optimized by the query engine.

Apex
// ❌ 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];

Query Only What You Need

Avoid SELECT * patterns. Only include fields you actually use — this reduces heap consumption and makes your query more selective on indexed fields.

Use Relationship Queries

Fetch parent and child data in one query instead of separate round trips. Child relationships use a subquery; parent fields use dot notation.

SOQL
SELECT Id, Name, Account.Name,
    (SELECT Id, Subject
     FROM Cases)
FROM Contact
WHERE Account.Industry = 'Technology'

Count Without Fetching Records

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.

Apex
Integer total = [SELECT COUNT() 
    FROM Opportunity 
    WHERE StageName = 'Closed Won' 
    AND CloseDate = THIS_YEAR];

Index-Friendly WHERE Clauses

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%'.

Apex

Apex Coding Patterns

Write cleaner, more maintainable Apex with these proven patterns.

1

One Trigger Per Object

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.

2

Bulkify Everything

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.

3

Use Custom Metadata for Config

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.

4

Avoid Hardcoded IDs

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.

5

Use Database Methods for Partial Success

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.

6

Leverage Static Variables for Recursion Control

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.

7

Prefer Maps Over Nested Loops

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).

8

Always Write Test Setup with @TestSetup

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.

Governor Limits

Staying Within Governor Limits

Know the limits, respect the limits, stay out of production incidents.

database

100 SOQL Queries

The synchronous limit. Use relationship queries, aggregate results, and bulkification to stay under. Check Limits.getQueries() in debug logs.

Hard Limit
speed

10s CPU Time

Synchronous Apex gets 10,000ms. Move heavy computation to async (Queueable/Batch). Avoid string manipulation inside large loops — it's expensive.

Hard Limit
memory

6MB Heap Size

Avoid storing large collections in memory. Process records in batches, clear variables when done, and never serialize entire objects unless necessary.

Hard Limit
table_rows

50,000 Rows Returned

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.

Workaround Available
cloud_upload

150 DML Statements

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.

Hard Limit
http

100 Callouts

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.

Hard Limit
Security

Apex Security Best Practices

Write secure code that respects Salesforce's sharing and access model.

with sharing vs. without sharing

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.

Apex
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];
        }
    }
}

Strip Inaccessible Fields

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.

Apex
SObjectAccessDecision decision = 
    Security.stripInaccessible(
        AccessType.CREATABLE, records);

insert decision.getRecords();

Validate All User Input

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.

Avoid SOQL Injection

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.

Apex
// Last resort — always escape
String safe = String.escapeSingleQuotes(input);
String q = 'SELECT Id FROM Account WHERE Name = \''
    + safe + '\'';
CLI

SFDX CLI Productivity

Speed up your local development workflow with these CLI commands and habits.

1

Alias Your Orgs

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
2

Deploy Only Changed Metadata

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
3

Run Specific Tests Only

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
4

Tail Logs in Real Time

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
5

Pull Metadata from Org

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
6

Run Anonymous Apex from CLI

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
Deployment

Deployment Best Practices

Ship with confidence using these deployment habits.

check_circle

Validate Before Deploying

Always run a validation-only deployment first to catch errors without impacting the org.

schedule

Deploy During Off-Hours

Schedule deployments during low-usage windows to minimise user impact and reduce lock conflicts.

inventory

Use Packages Wisely

Organise related metadata into unlocked packages for modular, repeatable deployments across sandboxes.

history

Maintain a Rollback Plan

Always have a documented rollback strategy with previous metadata snapshots ready to restore.

science

Test in Full-Copy Sandbox

Run full regression tests in a full-copy sandbox before any production deployment, not just Developer sandboxes.

build_circle

CI/CD Pipeline

Set up GitHub Actions with SFDX for automated test runs and deployment on every pull request merge.

rule

Quick Deploy After Validation

A successful validation within 10 days unlocks Quick Deploy — skip re-running all tests and deploy in seconds.

checklist

Component-Level Deployment

Deploy only the components that changed. Deploying everything every time slows you down and increases rollback risk.

lock

Lock the Org During Deployment

For critical production deployments, use the "Prevent Backups" option and communicate a change window to users beforehand.