PD1 Cram Deck Pass 68% · 60 Q · 105 min

Overview & Strategy

Current 4-section blueprint (the old "46% Logic" scheme is Winter '19 — ignore it). All four sections now carry near-equal weight, so this is a broad, balanced sweep — with the deepest time on the highest-frequency tested topics.

68%To pass (~41/60)
60Scored Q (+5 unscored)
105Minutes (pace 90s/Q)
~19You can miss

Section weighting

SectionWeightCore content
Process Automation & Logic30%Apex, triggers, SOQL/DML, governor limits, order of execution, async, exceptions, Flow-vs-Apex
User Interface25%Visualforce controllers, LWC (events), Aura, UI security
Developer Fundamentals23%Multitenancy, MVC, declarative-vs-code, data model & relationships, data import/export
Testing, Debugging & Deployment22%Test classes, 75% coverage, SFDX/CLI, debug logs, change sets, sandboxes
Winning moveThe knowledge here is the understanding layer. Your readiness gate is still 80%+ on timed practice exams (Focus on Force) with every miss understood.
Style watch60–70% of questions are scenario-based with code you must read. Multi-select is all-or-nothing. People bomb their home-turf topics from careless reading — slow down on the "easy" ones.

Governor Limits

High-yield

Memorize the numbers cold — then recognize violations in code. Async is the "bigger sibling."

LimitSyncAsync
Total SOQL queries100200
SOQL rows retrieved50,00050,000
getQueryLocator rows10,00010,000
SOSL queries / rows per query20 / 2,00020 / 2,000
DML statements150150
DML rows per statement10,00010,000
Callouts / timeout100 / 120s100 / 120s
@future / enqueueJob50 / 5050 / 1*
Heap size6 MB12 MB
CPU time10 s60 s
sendEmail / recursion depth10 / 1610 / 16

*Only 1 chained job from inside a running Queueable/Batch.

#1 code trapSOQL or DML inside a for loop blows the 100/150 limit under bulk load. People also swap "50,000 rows" (SOQL) with "10,000 rows" (DML). CPU time excludes callout + query wait. LimitException is uncatchable.

Memory hook: async doubles/6× the sync figure — SOQL 100→200, CPU 10→60, heap 6→12. Anchors: 50,000 SOQL rows · 10,000 DML rows · 150 DML statements · 100 SOQL & callouts · 20 SOSL / 2,000 rows.

Order of Execution

High-yield

Learn the 4 buckets first, then the order within each. Blank-sheet it until you can write it in under 2 minutes.

① Before Save

  • Load old record; run system validation (required, type, length)
  • Before-save record-triggered flows
  • Before triggers
  • System validation again + custom validation rules
  • Duplicate rules (block → stop)

② Save

  • Record written to DB — not committed

③ After Save

  • After triggers
  • Assignment → auto-response → workflow → escalation
  • Workflow field update → update triggers fire once more
  • Processes (PB) → after-save flows
  • Roll-up / cross-object → parent re-saves
  • Criteria-based sharing recalc

④ Commit → Post-commit

  • Commit all DML → send email → async Apex
TrapsValidation rules run after before-triggers, not before. Roll-ups & workflow field updates happen after the initial save. Before-save flows run first. Async is always last. Limits reset only when async begins.

Triggers

Automation & Logic

Context variable availability

Variableinsertupdatedeleteundelete
Trigger.new✔ editable*✔ editable*✗ null✔ read-only
Trigger.old✗ null✔ read-only✔ read-only✗ null
Trigger.newMap✗ null✗ null
Trigger.oldMap✗ null✗ null

*editable in before triggers only.

Use BEFORE for…

  • Validation
  • Setting/defaulting fields on the same record (no DML)
  • Blocking with addError()
  • Note: before insert has no record Id yet

Use AFTER for…

  • Anything needing the record Id
  • Creating child/related records
  • Roll-ups, email, async callouts
  • Trigger.new is read-only here
Best practiceOne trigger per object → logic in a handler class → fully bulkified → guard against recursion.

Asynchronous Apex

High-yield
TypeUse whenKey facts
@futureSimple fire-and-forget; callout from triggerPrimitives only (no sObjects). No chain/monitor. 50/txn. @future(callout=true)
QueueableChaining, sObject members, monitoringReturns monitorable Job Id. Chain 1 (depth 5 Dev Edition). Database.AllowsCallouts
BatchMillions of recordsstart/execute/finish. Size 200 (1–2000). Fresh limits per chunk. Database.Stateful
ScheduledRun at a time/intervalSystem.schedule(name, cron, obj). Max 100 jobs. Often launches a Batch
Look-alike trapNeeds an sObject param / monitoring / chaining → Queueable, not @future.

Testing

Testing & Deploy · 22%#1 Skipped

The single most common failure cause is underestimating this section. Don't.

  • 75% org-wide coverage to deploy to production; every trigger >0%; all tests pass. Excludes test lines, comments, System.debug.
  • Test.startTest()/stopTest()fresh governor limits + runs enqueued async synchronously so you can assert.
  • @testSetup → test data once per class, refreshed per method.
  • System.runAs(user) → test sharing/permissions + fixes MIXED_DML. Doesn't change limits.
  • Test.loadData() → records from a CSV static resource.
  • SeeAllData=false is the default and correct — tests must be self-contained.
  • Callouts → implement HttpCalloutMock + Test.setMock(). Real callouts throw in tests.
  • Cover positive + negative + single + bulk (200) + null. Assert with System.assertEquals(expected, actual, msg).

Data Model & Sharing

Fundamentals

Master-Detail vs Lookup

FeatureMaster-DetailLookup
Required on childAlwaysOptional
Owner & sharingInherits parentOwn
Cascade deleteYesNo (default)
Roll-up summaryYesNo
ReparentingNo (default)Yes
Max per object240
RememberJunction object = 2 master-detail fields (first = primary master). Formula traverses child→parent only. External ID enables upsert/integration matching.

Sharing keywords (Apex)

  • with sharing → enforces record sharing — NOT FLS/CRUD
  • without sharing → system mode, sees all records
  • inherited sharing → mode of caller (defaults to with at entry point)
  • Default (no keyword) = system mode
TrapApex ignores FLS/CRUD by default even with sharing. Enforce via WITH SECURITY_ENFORCED, Security.stripInaccessible, or Schema checks.

SOQL / DML / Exceptions

Automation & Logic

Relationship queries

  • Child→Parent (up): Account.Name; custom Custom__r.Name
  • Parent→Child (down): subquery (SELECT LastName FROM Contacts); custom Line_Items__r
  • Aggregates → AggregateResult[], ar.get('alias'), GROUP BY
  • SOSL = text search across objects → List<List<sObject>>

DML partial success & errors

  • insert list; = all-or-none (throws DmlException)
  • Database.insert(list, false) = partial → SaveResult[]isSuccess()/getErrors()
  • Custom exception must end in Exception and extend Exception
  • addError() in a trigger blocks DML with a user error

User Interface

UI · 25%

Visualforce controllers

TypeBehavior
StandardstandardController="Account" — built-in CRUD, respects user perms/sharing
CustomApex class, system mode by default (bypasses sharing/FLS unless with sharing)
ExtensionAdds to a controller; ctor takes ApexPages.StandardController; multiple allowed
SecurityVF auto-enforces FLS with apex:inputField; custom controllers don't. Prevent injection with String.escapeSingleQuotes().

LWC vs Aura

  • LWC files: .html, .js, .js-meta.xml (+ optional .css)
  • Decorators: @api (public), @track (reactive), @wire (data service)
  • Events: child→parent via dispatchEvent(new CustomEvent('name',{detail})); cross-DOM via LMS
  • Aura can contain LWC; LWC cannot contain Aura. LWC is faster/lighter

Fundamentals & Deployment

Fundamentals + Deploy

Platform

  • MVC: Model = objects/DB · View = VF/LWC/layouts · Controller = Apex
  • Multitenancy → shared infra → why governor limits exist
  • Declarative first; Apex only when too complex / high bulk / callout parsing / transactional control

Data & deployment

  • Import: Wizard ≤50k (dedupe) · Data Loader >50k
  • Change Sets = related orgs, metadata only (no records)
  • SFDX + CLI + VS Code = modern tooling (replaced Force.com IDE)
  • Debug levels: ERROR < WARN < INFO < DEBUG < FINE < FINER < FINEST

Sandbox types

TypeDataRefresh
DeveloperNone1 day
Developer ProNone (more storage)1 day
Partial CopySample (template)5 days
FullAll production data29 days

Code Patterns to Know Cold

Code-reading

Bulkified trigger + handler

trigger AccountTrigger on Account (before update) {
  AccountTriggerHandler.handleBeforeUpdate(Trigger.new, Trigger.oldMap);
}
public class AccountTriggerHandler {
  public static void handleBeforeUpdate(List<Account> newList, Map<Id,Account> oldMap) {
    Set<Id> parentIds = new Set<Id>();
    for (Account a : newList) parentIds.add(a.ParentId);        // collect
    Map<Id,Account> parents = new Map<Id,Account>(              // ONE query
      [SELECT Id, Name FROM Account WHERE Id IN :parentIds]);
    for (Account a : newList)                                   // loop, no SOQL/DML inside
      if (parents.containsKey(a.ParentId)) a.Description = parents.get(a.ParentId).Name;
  }
}

Test: startTest / stopTest + bulk

@isTest private class AcctTest {
  @testSetup static void seed() { insert new Account(Name='Seed'); }
  @isTest static void bulk() {
    List<Account> a = new List<Account>();
    for (Integer i=0;i<200;i++) a.add(new Account(Name='A'+i));
    Test.startTest();  insert a;  Test.stopTest();
    System.assertEquals(201, [SELECT COUNT() FROM Account]);
  }
}

HttpCalloutMock

@isTest global class MyMock implements HttpCalloutMock {
  global HttpResponse respond(HttpRequest req) {
    HttpResponse res = new HttpResponse();
    res.setStatusCode(200);
    res.setBody('{"ok":true}');
    return res;
  }
}
// Test.setMock(HttpCalloutMock.class, new MyMock());

Queueable (chainable)

public class MyJob implements Queueable,
                              Database.AllowsCallouts {
  public void execute(QueueableContext c) {
    // work...
    if (more) System.enqueueJob(new MyJob()); // chain
  }
}
// System.enqueueJob(new MyJob()); -> Job Id

Batch skeleton

public class MyBatch implements
    Database.Batchable<sObject>, Database.Stateful {
  public Database.QueryLocator start(Database.BatchableContext bc){
    return Database.getQueryLocator('SELECT Id FROM Account');
  }
  public void execute(Database.BatchableContext bc,
                      List<sObject> scope){ /* fresh limits */ }
  public void finish(Database.BatchableContext bc){ /* email */ }
}
// Database.executeBatch(new MyBatch(), 200);

Flashcards

Active recall

Tap a question, answer in your head first, then reveal. Re-test with spaced gaps.

Exam-Day Tactics

Pacing & triage

  • Two-pass: answer knowns, flag code-tracing, return later (~90s/Q)
  • Read the last line first on long stems
  • Never let one question eat 5 minutes

Answering

  • Multi-select = all-or-nothing — obey "choose two/three"
  • Eliminate obvious wrongs first
  • "bulkified / efficient / governor-safe" → the no-SOQL-in-loop answer
  • Slow down on home-turf topics; trust first instinct
MindsetYou can miss ~19 and still pass. Box-breathe if you stall, warm up on 2–3 easy questions, keep moving.