home Homemenu_book Guides
search

Apex Fundamentals

Q: What is bulkification and why does it matter?

A: Bulkification means writing Apex that handles multiple records at once efficiently — moving SOQL and DML out of loops. Salesforce governor limits apply per transaction, not per record. A trigger processing 200 records with SOQL inside a loop burns 200 queries out of the 100 allowed, causing an immediate failure. Bulkified code queries all records once into a collection, processes in memory, then writes once.

Q: What is the difference between Trigger.new and Trigger.newMap?

A: Trigger.new is a List<SObject> of the new record values in the order they were processed. Trigger.newMap is a Map<Id, SObject> keyed by record Id — only available in update, delete, and undelete contexts where records already have Ids. Use Trigger.newMap when you need to look up records by Id or when building a Map for related-record queries.

Q: When would you use @future vs Queueable vs Batch Apex?

Answer
@future          → Simple async fire-and-forget. Can make callouts.
                   Cannot chain, cannot accept sObjects as parameters.

Queueable        → Chainable async jobs. Supports sObject parameters.
                   Can make callouts (implements Database.AllowsCallouts).
                   Better than @future for most modern async patterns.

Batch Apex       → Processing millions of records in chunks.
                   Implements Database.Batchable. Has start/execute/finish.
                   Use when data volume exceeds what a single transaction can handle.

Q: What is the order of execution when a record is saved?

A: System validation → Before-save Record-Triggered Flows → Before Apex Triggers → Custom Validation Rules → Duplicate Rules → Save to DB (uncommitted) → After Apex Triggers → Assignment/Auto-response/Workflow/Escalation Rules → After-save Record-Triggered Flows → Roll-up summaries → Sharing rules → Commit → Post-commit (email, async Apex, platform events).

SOQL

Q: What is SOQL injection and how do you prevent it?

A: SOQL injection occurs when user-supplied input is concatenated directly into a SOQL string, allowing an attacker to modify the query. Prevent it by always using bind variables (:variableName syntax) instead of string concatenation. For dynamic SOQL that must use strings, use String.escapeSingleQuotes() on any user input.

Apex
// Vulnerable — never do this
String query = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';

// Safe — bind variable
List<Account> accs = [SELECT Id FROM Account WHERE Name = :userInput];

// Safe — dynamic SOQL with escaping
String safeInput = String.escapeSingleQuotes(userInput);
String query = 'SELECT Id FROM Account WHERE Name = \'' + safeInput + '\'';

Q: What is the difference between SOQL and SOSL?

A: SOQL queries a single object (or its relationships) with structured field-level filters — like SQL SELECT. SOSL is a full-text search across multiple objects and fields simultaneously, returning results grouped by object. Use SOQL when you know exactly which object and fields to filter on. Use SOSL when searching across multiple objects for a keyword — like a global search.

Lightning Web Components

Q: What is the difference between @api, @track, and @wire?

Answer
@api     → Makes a property or method PUBLIC. Parent can pass values in.
           Also used to expose methods callable by parent via querySelector.

@track   → Historically made properties reactive. Since Summer '19,
           all properties are reactive by default. @track is now only
           needed to detect deep changes in objects and arrays (nested
           property changes on objects, or changes to array elements).

@wire    → Connects a property or function to a Salesforce data service —
           either an Apex method annotated @AuraEnabled(cacheable=true)
           or a wire adapter (like getRecord, getPicklistValues).
           Data is read-only and automatically refreshes when reactive
           properties change.

Q: How do you call Apex from LWC? Wire vs imperative — when to use each?

A: Wire calls the Apex method automatically when the component loads and when reactive properties change. The result is read-only and cached. Use wire for read-only data that should refresh automatically.

Imperative calls the Apex method explicitly (usually inside an event handler or connectedCallback). The result is mutable, so you can manipulate it. Required for DML operations and when you need to control exactly when the call fires.

JavaScript
import { LightningElement, wire } from 'lwc';
import getAccount from '@salesforce/apex/AccountService.getAccount';
import updateAccount from '@salesforce/apex/AccountService.updateAccount';

export default class Example extends LightningElement {
    accountId = '001XXXX';

    // Wire — auto-fetches when accountId changes
    @wire(getAccount, { recordId: '$accountId' })
    account;

    // Imperative — called on button click
    async handleSave() {
        try {
            await updateAccount({ recordId: this.accountId, name: 'New Name' });
        } catch (error) {
            console.error(error);
        }
    }
}

Flow

Q: When should you use a before-save Flow vs after-save Flow?

A: Before-save flows run before the record is written to the database and can modify fields on the triggering record without an extra DML statement — far more efficient. Use before-save for field updates on the triggering record. After-save flows run after the record is committed, making the record's Id available, and can perform DML on related records or send notifications. Use after-save for everything else.

Q: What replaced Process Builder and Workflow Rules?

A: Both are deprecated as of December 31, 2025. Record-Triggered Flows replace both. They have equivalent and expanded functionality — before/after save options, complex branching, subflows, Apex actions, and better bulk support — and are the only supported declarative automation path going forward.

Governor Limits

Q: How do you avoid Too Many SOQL Queries (101)?

A: Move all SOQL outside of loops. Use Map<Id, SObject> patterns to look up related records in memory after a single bulk query. Use relationship queries (subqueries in SOQL) to fetch parent/child data in one query. For complex scenarios, offload work to asynchronous Apex where the limit resets to 200.

Q: What is the Mixed DML error and how do you fix it?

A: Mixed DML occurs when a transaction tries to modify both setup objects (User, Profile, PermissionSet, etc.) and non-setup objects in the same transaction. Fix by separating the setup object DML into a @future method, which runs in its own transaction context.

Scenario Questions

Q: An Apex trigger is running correctly in sandbox but failing in production on bulk data loads. What do you check?

A: First, check for SOQL or DML inside loops — sandbox tests rarely use enough records to hit limits but production loads can send 200 records in one batch. Check debug logs for the exact error and which governor limit was hit. Review the trigger for non-bulkified patterns (queries inside loops, DML inside loops). Check if other automation (Flows, other triggers) on the same object is contributing to the limit count — all share the same transaction budget.

Q: How do you prevent trigger recursion?

A: Use a static Boolean flag in a utility class. Static variables persist for the lifetime of the transaction. Set the flag to false at the start of the handler, check it before running, and set it to true after the first execution so subsequent trigger fires from DML inside the handler return immediately without re-executing the logic.