← Study Vault
Field manual · one file · progress saves in this browser

Salesforce Developer
The 30-Day Sprint

Sales Cloud · Service Cloud · Financial Services Cloud — from admin basics to interview-ready. Theory, assignments, drills, and the job hunt, day by day.

Overall progress
0%
of all tasks
Tasks done
0/0
across 30 days + reference
Days complete
0/30
a day counts when every box is ticked
You are on
Day 1
first unfinished day
Start date

Week 1 — Apex Core

Goal: never lose an interview on fundamentals. Everything this month — integrations, LWC controllers, FSC customizations — stands on collections, SOQL, triggers, and tests. Your admin knowledge already covers the data model and declarative side; this week converts it into code.

  • Repo #1: create GitHub repo apex-fundamentals Day 1; commit every kata (small commits, clear messages).
  • Rule of the month: type every line yourself. No copy-paste from tutorials, no video-only sessions. 70% of study time is in VS Code or the org.
  • Exit gate (Day 7): write a bulkified rollup trigger from a blank file in 30 minutes, and recite the order of execution.
DAY01

Apex language & collections

Theory 3h · Build 3.5h · Drill 1h · Comm 45m
0/6

By tonight: you write fluent Apex with Lists, Sets and Maps, and you understand why every Salesforce interview circles back to the Map pattern.

  • Open theory — the language & the runtime
    • What Apex is: a Java-like, strongly-typed language that runs on Salesforce servers in a multitenant runtime. Because thousands of orgs share hardware, every transaction is metered by governor limits — that single fact explains most Apex "best practices."
    • Case-insensitive (Account = ACCOUNT), compiled and stored on-platform, versioned per class (API version on each class).
    • Primitives: Integer, Long, Decimal, Double, String, Boolean, Id, Date, Datetime, Time, Blob. Currency fields arrive as Decimal. Everything can be null — guard with safe navigation acc?.Owner?.Name and null-coalescing x ?? 'default'.
    • Classes: public | private | global, with/without sharing (Day 7), static vs instance. Static variables live for one transaction — that's why they work as recursion guards (Day 5).
    • sObjects: every record is an SObject (Account a = new Account(Name='Acme');). Generic SObject + a.get('Name')/a.put(...) enable dynamic code.
    Run it — two ways
    • VS Code: Cmd+Shift+P → SFDX: Execute Anonymous Apex (select code first), output in the Debug console.
    • Dev org: Setup → Debug → Developer Console → Debug → Open Execute Anonymous.
    public class GreetingService {
        private static final String PREFIX = 'Hello, ';
        public static String greet(String name) {
            return PREFIX + (name ?? 'stranger');
        }
    }
    // Anonymous: System.debug(GreetingService.greet('Amardeep'));
  • Open theory — collections & the three golden idioms
    • List — ordered, allows duplicates, index access. add, get, size, sort, isEmpty, addAll, clear.
    • Set — unique values, no order, O(1) contains. Use for dedupe and "collect Ids" steps.
    • Map<K,V> — key → value. put, get, containsKey, keySet(), values(). Keys are compared by value (Ids, Strings are common keys).
    Idiom 1 — query straight into a Map
    Map<Id, Account> accById = new Map<Id, Account>(
        [SELECT Id, Name, Industry FROM Account WHERE Industry = 'Banking']
    );
    // accById.keySet() is now a Set<Id> you can bind in another query
    Idiom 2 — group children by parent (memorize)
    Map<Id, List<Contact>> contactsByAcc = new Map<Id, List<Contact>>();
    for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId != null]) {
        if (!contactsByAcc.containsKey(c.AccountId)) {
            contactsByAcc.put(c.AccountId, new List<Contact>());
        }
        contactsByAcc.get(c.AccountId).add(c);
    }
    Idiom 3 — collect, then act once (the bulk shape)
    Set<Id> accIds = new Set<Id>();
    for (Opportunity o : opps) { accIds.add(o.AccountId); }   // collect
    Map<Id, Account> accs = new Map<Id, Account>([
        SELECT Id, OwnerId FROM Account WHERE Id IN :accIds    // one query
    ]);
    for (Opportunity o : opps) {                               // act in memory
        o.OwnerId = accs.get(o.AccountId)?.OwnerId;
    }
  • SOQL-for loop for (Account a : [SELECT ...]) processes in 200-record chunks — heap-friendly iteration for big result sets.
  • Open the six katas + acceptance criteria
    1. Dedupe: given List<String> emails with duplicates, produce a deduped list preserving first-seen order. (Set + List.)
    2. Group: query Contacts, build Map<Id, List<Contact>> by AccountId (Idiom 2 from memory).
    3. Totals: from a List<Opportunity> (create 10 in-memory), build Map<Id, Decimal> AccountId → summed Amount.
    4. Domain split: from Contact emails, build Map<String, Integer> domain → count (hint: email.split('@')[1], guard nulls).
    5. Diff: two Set<Id> A and B — compute A∩B and A−B using retainAll/removeAll on clones.
    6. Reshape: turn Map<Id, List<Contact>> into Map<Id, Integer> (counts), then find the accountId with max count.

    Done when: each runs clean in Execute Anonymous with System.debug proof, no hardcoded Ids, and pushed to GitHub.

  • Questions + key points to hit
    • List vs Set vs Map — when each? (order/dupes vs uniqueness/contains vs lookup-by-key; give the trigger use case for each)
    • Why does Apex have governor limits? (multitenancy — shared resources; limits force bulk-safe design)
    • static vs instance? (per-transaction lifetime of statics; utility methods; recursion guards)
    • What is an sObject? (generic record type; concrete vs generic; dynamic get/put)
    • How do you run ad-hoc Apex? (Execute Anonymous — VS Code / Dev Console)

    Structure every answer: definition → when to use → gotcha → example from today's katas.

DAY02

SOQL & SOSL — querying like a senior

Theory 3h · Build 3h · Drill 1h · Comm 45m
0/7

Relationship queries in both directions, aggregates without flinching, and you can say what makes a query "selective."

  • Open theory — syntax, binds, filters
    • Shape: SELECT fields FROM object WHERE ... ORDER BY ... LIMIT n OFFSET m. No SELECT * — you name fields, and un-queried fields on a record throw SObjectException on access.
    • Bind variables: WHERE Id IN :accIds — binds any in-scope variable or collection; this is also your injection defense (never build WHERE by string concat; if you must be dynamic, use String.escapeSingleQuotes or bind maps with Database.queryWithBinds).
    • Date literals: LAST_N_DAYS:30, THIS_MONTH, TODAY, NEXT_N_DAYS:7 — no quoting.
    • Nulls: WHERE Email = null is valid; ORDER BY Amount DESC NULLS LAST.
    • FOR UPDATE locks rows for the transaction (prevents UNABLE_TO_LOCK_ROW races — Day 17).
    List<Opportunity> recent = [
        SELECT Id, Name, Amount, StageName
        FROM Opportunity
        WHERE CloseDate = LAST_N_DAYS:90 AND Amount > :minAmount
        ORDER BY Amount DESC NULLS LAST
        LIMIT 50
    ];
  • Open theory — both directions + aggregates
    Child → parent (dot notation, up to 5 levels)
    SELECT Id, LastName, Account.Name, Account.Owner.Name
    FROM Contact WHERE Account.Industry = 'Banking'
    Parent → child (subquery — child relationship name, 1 level)
    SELECT Id, Name, (SELECT Id, LastName FROM Contacts)
    FROM Account
    // custom objects: relationship name ends in __r, e.g. (SELECT ... FROM Invoices__r)

    Access in Apex: for (Contact c : acc.Contacts) { ... }. Custom lookup field Bank__c → relationship Bank__r.

    Aggregates
    List<AggregateResult> rows = [
        SELECT AccountId acc, SUM(Amount) total, COUNT(Id) n
        FROM Opportunity WHERE IsClosed = false
        GROUP BY AccountId HAVING SUM(Amount) > 100000
    ];
    for (AggregateResult ar : rows) {
        Id accId = (Id) ar.get('acc');          // alias, cast required
        Decimal total = (Decimal) ar.get('total');
    }
    • COUNT() returns Integer directly; COUNT(field)/SUM/AVG/MIN/MAX come back in AggregateResult.
    • Aggregate rows still count against the 50k row limit via records scanned — filter first.
  • Open theory — search vs query, and what "selective" means
    • SOSL = text search across objects/fields: FIND {loan*} IN ALL FIELDS RETURNING Account(Id,Name), Contact(Id,Email). Returns List<List<SObject>>. Use when: term search across many objects/fields (global-search-like). SOQL when: structured filters on known fields.
    • Selectivity: a WHERE clause is selective when it filters on an indexed field and returns a small slice. Standard indexes: Id, Name, OwnerId, CreatedDate, SystemModstamp, lookup/master-detail fields, External Id fields (and unique fields). Killers: leading-wildcard LIKE '%x', negatives (!=, NOT IN), formula fields.
    • Why interviewers ask: on large objects (>100k rows... millions in FSC banks) non-selective queries time out or blow the 50k limit. Mentioning "I filter on indexed fields and check with Query Plan" reads senior.
  • Seed script + the 10 queries

    First seed your org (Execute Anonymous):

    List<Account> accs = new List<Account>();
    for (Integer i = 1; i <= 20; i++) {
        accs.add(new Account(Name = 'Bank ' + i,
            Industry = Math.mod(i,2)==0 ? 'Banking' : 'Insurance',
            AnnualRevenue = i * 250000));
    }
    insert accs;
    List<Contact> cons = new List<Contact>();
    List<Opportunity> opps = new List<Opportunity>();
    for (Account a : accs) {
        for (Integer j = 0; j < 3; j++) {
            cons.add(new Contact(LastName = a.Name + '-C' + j, AccountId = a.Id,
                Email = 'c' + j + '@' + a.Name.deleteWhitespace().toLowerCase() + '.com'));
            opps.add(new Opportunity(Name = a.Name + '-Opp' + j, AccountId = a.Id,
                StageName = j == 0 ? 'Closed Won' : 'Prospecting',
                CloseDate = Date.today().addDays(30 - 10*j), Amount = 50000.0 * (j+1)));
        }
    }
    insert cons; insert opps;

    Then write (in a committed queries.soql file): ① all Banking accounts by revenue desc; ② contacts with their account name + owner name; ③ accounts with their contacts subquery; ④ accounts having >2 open opps (aggregate HAVING); ⑤ pipeline per account (SUM group by); ⑥ opps closing THIS_MONTH; ⑦ contacts whose email ends a given domain (bind var); ⑧ count of opps per stage; ⑨ SOSL finding 'Bank 1*' across Account/Contact; ⑩ accounts with no open opps (semi-join: WHERE Id NOT IN (SELECT AccountId FROM Opportunity WHERE IsClosed=false)).

    Done when: all 10 return sensible rows in your org; you can explain ④ and ⑩ without notes.

  • Questions + key points
    • Child-to-parent vs parent-to-child query? (dot vs subquery; __r for custom; levels 5-up/1-down)
    • When SOSL over SOQL? (text search, many objects, unknown field)
    • How do you handle AggregateResult? (alias + cast from Object)
    • What makes a query selective and why care? (indexed filters, LDV, Query Plan tool)
    • How do you prevent SOQL injection? (binds first; escapeSingleQuotes for dynamic SOQL)
DAY03

DML, Database methods, transactions & exceptions

Theory 2.5h · Build 3.5h · Drill 1h · Comm 45m
0/6

You know the difference between insert and Database.insert cold, and you build the error-logging utility you'll reuse all month.

  • Open theory — verbs, partial success, savepoints, upsert
    • Verbs: insert, update, upsert, delete, undelete, merge. Statement form (insert accs;) throws DmlException on the first failure and rolls back that statement.
    • Database methods add control: Database.insert(records, false)allOrNone=false = partial success; returns Database.SaveResult[] you must inspect:
    Database.SaveResult[] results = Database.insert(accs, false);
    for (Integer i = 0; i < results.size(); i++) {
        if (!results[i].isSuccess()) {
            for (Database.Error e : results[i].getErrors()) {
                System.debug(accs[i].Name + ' failed: ' + e.getMessage());
            }
        }
    }
    • Upsert: matches on Id by default, or an External Id field: upsert accs My_ERP_Id__c; — the integration-safe idempotent write (say this in interviews).
    • Transaction: one execution context = one transaction; everything commits together at the end or rolls back on unhandled exception. Savepoints: Savepoint sp = Database.setSavepoint(); ... Database.rollback(sp); — partial rollback within the transaction.
    • Mixed DML: setup objects (User, Group…) and normal records can't be written in the same transaction → MIXED_DML_OPERATION. Fix: do one side asynchronously (Day 8 demo).
    • Order matters: parents before children (you need the parent Id to set the lookup).
  • Open theory — types, custom exceptions, logging pattern
    • Common types: DmlException (has getNumDml(), getDmlMessage(i)), QueryException (single-row assignment got 0/2+ rows), NullPointerException, ListException, CalloutException. LimitException cannot be caught — you prevent it by design, not try/catch (interview favorite).
    • Custom: public class KycException extends Exception {} — throw with a business message; catch specific types before generic Exception.
    • In triggers prefer record.addError('msg') to block a save (turns into a friendly page/API error) — throwing raw exceptions from triggers gives users an ugly stack.
    • finally always runs — cleanup/logging.
    Pattern you'll reuse: catch → log to a custom object (async-safe, queryable, reportable) → rethrow or addError. Swallowed exceptions ("empty catch") are a red flag interviewers probe.
  • Spec + skeleton
    1. Object Error_Log__c (use your admin skills — Object Manager): fields Class_Name__c (Text 80), Method_Name__c (Text 80), Record_Id__c (Text 18), Message__c (Long Text), Stack_Trace__c (Long Text), Severity__c (Picklist: Info/Warning/Error).
    2. Class:
    public without sharing class ErrorLogger {
        public static void log(Exception ex, String cls, String method, Id recordId) {
            insert new Error_Log__c(
                Class_Name__c = cls, Method_Name__c = method,
                Record_Id__c = recordId != null ? String.valueOf(recordId) : null,
                Message__c = ex.getMessage(),
                Stack_Trace__c = ex.getStackTraceString(),
                Severity__c = 'Error');
        }
    }
    1. Resilient loader script: insert 5 accounts where 2 are invalid (e.g., add a validation rule requiring Industry, leave it blank on two) using Database.insert(..., false); log each failure via ErrorLogger; debug a summary "3 ok / 2 failed".
    2. Savepoint demo: script that inserts an Account, sets a savepoint, inserts a bad record in try/catch, rolls back to savepoint — prove the first Account survives.

    Done when: Error_Log__c rows appear with real stack traces; both scripts committed; you can explain allOrNone true vs false in one breath.

  • Questions + key points
    • insert vs Database.insert? (exception vs SaveResult; allOrNone flag; when each)
    • How does upsert decide insert vs update? (Id or External Id match; integration idempotency)
    • Can you catch a LimitException? (no — design around limits)
    • What is a savepoint? (partial rollback inside one transaction; released on rollback of earlier sp)
    • What is MIXED_DML and the fix? (setup vs non-setup objects; async split)
DAY04

Triggers I — events, context, order of execution

Theory 3h · Build 3h · Drill 1h · Comm 45m
0/7

The single most-tested topic in Salesforce interviews starts today. By tonight you can recite the order of execution and explain before vs after without hesitating.

  • Open theory — events, context, before vs after
    • Events: before/after × insert/update/delete, plus after undelete. One trigger can handle many: trigger ContactTrigger on Contact (before insert, before update, after update) { ... }
    • Context variables: Trigger.new (new versions, List), Trigger.old (prior versions), Trigger.newMap / oldMap (Id → record), Trigger.isBefore/isAfter/isInsert/isUpdate/isDelete/isUndelete, Trigger.operationType (enum for switch), Trigger.size.
    • Availability matters (full matrix in Reference): on insert there is no old; on delete there is no new; newMap is unavailable in before insert (no Ids yet); after records are read-only.
    • before = modify the very records being saved — assign fields directly on Trigger.new, no DML needed, no Id yet on before insert. Use for defaulting, validation (addError).
    • after = records saved (Ids exist) but not committed — use to touch other records (create tasks, roll up to parent) or anything needing the Id.
    • Detect real changes in update: compare Trigger.newMap.get(id).Field != Trigger.oldMap.get(id).Field.
    • No callouts in triggers (sync) — hand off to async (Day 8). Keep triggers logic-free: delegate to a handler class (Day 5).
    trigger ContactTrigger on Contact (before insert, before update) {
        for (Contact c : Trigger.new) {
            if (c.Email == null) {
                c.Email__Missing_Flag__c = true;         // before: just assign
            }
            if (Trigger.isUpdate) {
                Contact prior = Trigger.oldMap.get(c.Id);
                if (c.Email != prior.Email) { /* email changed */ }
            }
        }
    }
  • Open theory — the condensed spine + the traps
    1. Record loaded / system validation (required fields, formats)
    2. Before-save record-triggered flows
    3. Before triggers
    4. System validation again + custom validation rules → duplicate rules
    5. Record saved (gets Id) — not committed
    6. After triggers
    7. Assignment rules → auto-response → workflow rules (field update? → before/after update triggers run one more time) → escalation rules
    8. After-save record-triggered flows / processes
    9. Entitlement rules → roll-up summary recalc on parent (then grandparent) → criteria-based sharing eval
    10. Commit → post-commit (emails send, async jobs actually enqueue)
    The traps interviewers set
    • "Where do validation rules run relative to before triggers?" — after before triggers (your before-trigger field fixes are validated).
    • "Why did my update trigger run twice?" — a workflow/flow field update re-fires update triggers once.
    • "When does @future actually start?" — only after commit succeeds; if the transaction rolls back, nothing was enqueued.
    • "Record-triggered flow before-save vs before trigger — which first?" — the flow.
  • Spec

    Rule: two Contacts under the same Account must not share an Email. Before insert/update. Bulk-safe: collect candidate emails+accounts from Trigger.new, run one SOQL against existing contacts, also check duplicates within the incoming batch, and c.Email.addError('Duplicate email on this account') for offenders. Skip null emails. Test manually: single insert, then an Execute-Anonymous bulk insert of 10 where 3 collide.

    Done when: bad rows blocked with a clean message, good rows in the same batch still save (that proves addError beats throw).

  • Spec

    Before insert/update on Account: ① if AnnualRevenue > 1,000,000 set Rating = 'Hot'; ② if Shipping address blank, copy Billing→Shipping. Zero SOQL, zero DML (it's a before trigger — assignment only). Note in the README why this must be a before trigger, not a workflow or after trigger.

  • Questions + key points
    • Recite the order of execution. (the spine above — this exact request is asked constantly)
    • before vs after — when each? (same-record edits vs related records/Id needed)
    • Is Trigger.newMap available in before insert? (no — no Ids yet)
    • Why did an update trigger fire twice? (workflow/flow field update)
    • addError vs throwing an exception? (row-level friendly block vs transaction abort)
    • Can a trigger make a callout? (not synchronously — async handoff)
DAY05

Triggers II — bulkification & the handler framework

Theory 2.5h · Build 3.5h · Drill 1h · Comm 45m
0/7

"How do you bulkify a trigger?" — after today you answer with a pattern, a framework, and a repo link.

  • Open theory — bad vs good, side by side

    Triggers receive up to 200 records per chunk (Data Loader, API, flows). A SOQL or DML inside a loop multiplies by 200 → limit breach. The pattern is always: collect → query once → map → act once.

    // ❌ BAD — 200 queries + 200 DML
    for (Opportunity o : Trigger.new) {
        Account a = [SELECT OwnerId FROM Account WHERE Id = :o.AccountId]; // SOQL in loop
        a.Description = 'Has opps';
        update a;                                                          // DML in loop
    }
    
    // ✅ GOOD — 1 query + 1 DML
    Set<Id> accIds = new Set<Id>();
    for (Opportunity o : Trigger.new) { accIds.add(o.AccountId); }
    Map<Id, Account> accs = new Map<Id, Account>(
        [SELECT Id, OwnerId FROM Account WHERE Id IN :accIds]);
    List<Account> toUpdate = new List<Account>();
    for (Id accId : accs.keySet()) {
        toUpdate.add(new Account(Id = accId, Description = 'Has opps'));
    }
    update toUpdate;
    • Prefer aggregate SOQL over summing in loops when rolling up.
    • Never assume Trigger.new.size() == 1. Ever.
  • Open theory — the framework you'll defend in interviews
    • Why one trigger per object: Salesforce doesn't guarantee firing order between two triggers on the same object/event — one dispatching trigger makes order explicit, logic testable, and bypass possible.
    • Layout: trigger = 3 lines of routing; handler = static methods per event; helper/service classes for business logic.
    • Recursion: a workflow/flow update or your own DML can re-fire the trigger. Guard with a static Set<Id> of processed Ids (statics live per-transaction) — better than a boolean (a boolean skips the second chunk of a 400-record batch; a processed-Id set doesn't).
    • Bypass switch: a Custom Metadata / Custom Setting checkbox ("Bypass_Triggers__c") checked in the handler — lets admins mass-load data without automation. Mentioning this reads very senior.
    trigger OpportunityTrigger on Opportunity (
        after insert, after update, after delete, after undelete) {
        OpportunityTriggerHandler.run();
    }
    
    public with sharing class OpportunityTriggerHandler {
        private static Set<Id> processed = new Set<Id>();
    
        public static void run() {
            switch on Trigger.operationType {
                when AFTER_INSERT, AFTER_UPDATE, AFTER_UNDELETE {
                    rollup(collectAccountIds(Trigger.new));
                }
                when AFTER_DELETE {
                    rollup(collectAccountIds(Trigger.old));
                }
            }
        }
        private static Set<Id> collectAccountIds(List<Opportunity> opps) {
            Set<Id> ids = new Set<Id>();
            for (Opportunity o : opps) {
                if (o.AccountId != null && !processed.contains(o.Id)) {
                    ids.add(o.AccountId); processed.add(o.Id);
                }
            }
            return ids;
        }
        public static void rollup(Set<Id> accountIds) {
            if (accountIds.isEmpty()) return;
            Map<Id, Decimal> totals = new Map<Id, Decimal>();
            for (AggregateResult ar : [
                SELECT AccountId acc, SUM(Amount) total FROM Opportunity
                WHERE AccountId IN :accountIds AND IsClosed = false
                GROUP BY AccountId]) {
                totals.put((Id) ar.get('acc'), (Decimal) ar.get('total'));
            }
            List<Account> updates = new List<Account>();
            for (Id accId : accountIds) {
                updates.add(new Account(Id = accId,
                    Total_Open_Pipeline__c = totals.get(accId) ?? 0));
            }
            update updates;
        }
    }
  • Spec

    Create Total_Open_Pipeline__c (Currency) on Account. Implement the handler above yourself (type it, adapt it, break it, fix it). Cover all four after events. Why not a roll-up summary field? Opportunity→Account is lookup-ish here... actually Opp-Account supports RSF — your README answer: RSF works for master-detail/std opp-account but can't filter on complex criteria or roll up across lookups generally; the Apex version is the interview demonstration (and FSC uses config-driven RBL for the same job — Day 18 callback).

    Done when: bulk test via Execute Anonymous — insert 300 opps across 20 accounts in one go, totals correct, no limit errors (check debug log's LIMIT_USAGE lines).

  • Questions + key points
    • How do you bulkify a trigger? (collect→query once→map→act once; narrate your T3)
    • Why one trigger per object? (no order guarantee, testability, bypass)
    • How do you prevent trigger recursion? (static processed-Id set; why not a plain boolean)
    • How long does a static variable live? (one transaction)
    • How would you let admins bypass triggers during data load? (custom metadata/setting flag or custom permission check)
DAY06

Apex testing — write tests like you mean it

Theory 2.5h · Build 4h · Drill 1h · Comm 45m
0/6

75% is the law; behavior-asserting tests are the craft. Today T1–T4 get a proper test suite and a reusable TestDataFactory.

  • Open theory — @isTest, @testSetup, startTest/stopTest, runAs
    • @isTest on class + methods. Test code doesn't count against org code size; 75% overall coverage required to deploy to production, and every trigger needs some coverage.
    • Data isolation: tests see no org data (except User, Profile, etc.). @isTest(SeeAllData=true) re-opens org data — treat as a smell (flaky, env-dependent); the exception is objects you can't create in tests.
    • @testSetup: runs once per class; each test method gets a fresh rollback to that snapshot — fast shared data without cross-test bleed.
    • Test.startTest()/stopTest(): ① resets governor limits for the code between them (your setup DML doesn't eat the budget you're testing) ② stopTest() forces async work (future/queueable/batch) to run synchronously — that's how you test async.
    • System.runAs(user): switches user context — tests sharing and user-dependent logic (note: FLS/CRUD isn't auto-enforced in Apex regardless; runAs is about sharing + ownership).
    • Assertions: modern Assert.areEqual(expected, actual, msg), Assert.isTrue, Assert.fail — or classic System.assertEquals. A test without asserts is coverage theater; interviewers ask "what do you assert?"
    @isTest
    private class OpportunityTriggerHandlerTest {
        @testSetup static void seed() {
            insert TestDataFactory.accounts(5);
        }
        @isTest static void rollsUpOpenPipeline_bulk() {
            List<Account> accs = [SELECT Id FROM Account];
            List<Opportunity> opps = TestDataFactory.opps(accs, 40); // 200 total
            Test.startTest();
            insert opps;
            Test.stopTest();
            for (Account a : [SELECT Total_Open_Pipeline__c FROM Account]) {
                Assert.areEqual(40 * 10000, a.Total_Open_Pipeline__c,
                    'Rollup must sum open opps per account');
            }
        }
    }
  • Open theory — the four test types + asserting addError
    • Positive (happy path), negative (bad input blocked), bulk (200 records), restricted user (runAs a minimal-profile user). Name tests by behavior: blocksDuplicateEmail_withinBatch().
    • Asserting a blocked save:
    try {
        insert dupContact;
        Assert.fail('Expected duplicate email to be blocked');
    } catch (DmlException e) {
        Assert.isTrue(e.getDmlMessage(0).contains('Duplicate email'),
            'Wrong error surfaced: ' + e.getDmlMessage(0));
    }
    • TestDataFactory: one class, static builders (accounts(n), opps(accs, perAcc), contact(acc, email)) with sensible defaults + overridable fields. Every test file this month uses it — DRY test data is a real interview talking point.
    • Coverage is a byproduct: aim asserts at behavior; 90%+ falls out naturally on small classes.
  • Spec + how to run
    • Factory: accounts(n), contacts(accs, perAcc), opps(accs, perAcc) — no DML inside builders (caller inserts) for flexibility.
    • Suites: ContactTriggerHandlerTest (positive, negative-duplicate incl. within-batch, bulk 200), AccountTriggerHandlerTest (rating set, address copy, bulk), OpportunityTriggerHandlerTest (insert/update/delete/undelete paths + bulk).
    • Run: VS Code Testing sidebar, or sf apex run test --code-coverage --result-format human --wait 10. Screenshot the coverage table for the README.

    Done when: all green, handlers ≥ 95%, at least one bulk-200 test, one negative test asserting the message, one runAs test.

  • Questions + key points
    • What do startTest/stopTest actually do? (limit reset + async flush — give both)
    • @testSetup behavior? (once per class, rollback per method)
    • Why is SeeAllData=true bad? (flaky, env-coupled, breaks in prod deploys)
    • How do you test a trigger properly? (4 test types; assert field outcomes not just "no exception")
    • What does runAs change — and not change? (user context + sharing; not FLS in Apex)
DAY07

Security, config data & the Week-1 gate

Theory 2.5h · Build 2.5h · Gate 2h · Comm 45m
0/6

Close the week: sharing keywords, FLS enforcement, config-driven code — then prove Week 1 stuck with a timed rebuild and a recorded mock.

  • Open theory — who sees what, in code
    • Apex runs in system mode by default — it ignores record sharing and FLS unless you opt in. That's the headline fact.
    • with sharing = respect the running user's record access (sharing rules, OWD, role). without sharing = see everything (legit for rollups/logging). inherited sharing = take the caller's mode (safe default for service classes).
    • Sharing keywords do not enforce object CRUD or field-level security. For that:
    • Modern: SELECT Id FROM Account WITH USER_MODE and insert as user rec; — enforce FLS + CRUD + sharing in one keyword. (Older code: WITH SECURITY_ENFORCED on queries.)
    • Security.stripInaccessible(AccessType.READABLE, records) — silently removes fields the user can't see; the polite option for API responses.
    • Interview line: "Controllers exposed to UI run with sharing and use USER_MODE; internal engines that must see all data are without sharing by deliberate choice, documented."
    public with sharing class AccountReader {
        @AuraEnabled(cacheable=true)
        public static List<Account> recent() {
            return [SELECT Id, Name FROM Account
                    WITH USER_MODE ORDER BY LastViewedDate DESC LIMIT 10];
        }
    }
  • Open theory — config-driven code
    • Custom Metadata Types (CMDT): records deploy with your package/change set, visible in tests without SeeAllData, queried or via MyType__mdt.getAll(). Use for: endpoints, feature flags, trigger bypass switches, mapping tables. Default choice.
    • Custom Settings: hierarchy type gives per-profile/per-user overrides (MySetting__c.getInstance()) — use when the value must differ per user (e.g., bypass for the data-migration user only). List type mostly legacy.
    • Wire your Day-5 bypass flag to CMDT today: Trigger_Switch__mdt with a checkbox per object handler.
  • The 15-question gate (score yourself /15, pass ≥ 12)
    1. List vs Set vs Map with a trigger use case each
    2. Recite the order of execution
    3. before vs after triggers — two use cases each
    4. Bulkify this: "SOQL inside a for loop" — narrate the fix
    5. insert vs Database.insert(…, false)
    6. upsert with External Id — why integrations love it
    7. Can you catch LimitException? What do you do instead?
    8. startTest/stopTest — both jobs
    9. @testSetup semantics
    10. Assert a blocked save (narrate the try/catch)
    11. with vs without vs inherited sharing
    12. How to enforce FLS in Apex (two ways)
    13. Recursion guard — why Set<Id> beats a boolean
    14. CMDT vs Custom Settings
    15. What makes a SOQL query selective?

    Record the whole thing. Watch it. This recording is your Week-1 baseline.

WEEK 1 EXIT: repo #1 public with tests green · rollup rebuilt in 30 min · quiz ≥ 12/15 · 21 comm recordings exist. If all four hold, Week 2 will feel easy.

Week 2 — Async · Integration · LWC

This week covers the three question clusters that decide developer interviews: asynchronous Apex, calling and exposing APIs, and Lightning Web Components. And on Day 10 the job hunt goes live — from then on, applying is a daily task, because the interview pipeline takes 2–3 weeks on its own.

  • Repo #2: lwc-service-lite — all LWC + integration work lands here.
  • Buy tonight: a PD1 question bank (Focus on Force, ~$20). Nightly questions start Day 14.
  • Exit gate (Day 14): build a search LWC from scratch in 45 min; explain future vs Queueable vs Batch vs Schedulable without notes; resume live on LinkedIn + Naukri.
DAY08

Async Apex I — @future & Queueable

Theory 3h · Build 3h · Drill 1h · Comm 45m
0/6

"Future vs Queueable" is the single most-asked developer question set in Salesforce interviews. Today you earn the right to answer it with opinions.

  • Open theory — the three reasons, and future's rules
    Why go async at all (give all three in interviews)
    • Callouts: triggers can't call external APIs synchronously — hand off to async.
    • Higher limits: async transactions get 200 SOQL, 12MB heap, 60s CPU.
    • Mixed DML: setup objects (User…) and normal records in one flow → split one side into async.
    @future rules
    • Static, void, primitive parameters only (no sObjects — records could be stale by run time; pass Ids and re-query).
    • @future(callout=true) to allow callouts.
    • No chaining (future can't call future), no job object handle returned, 50 per transaction.
    • Enqueued work starts only after the transaction commits — rollback = never ran. No guaranteed order.
    public class UserProvisioner {
        @future
        public static void createUser(String alias, String email) {
            insert new User(Alias = alias, Email = email, /* ... */
                ProfileId = [SELECT Id FROM Profile WHERE Name='Standard User'].Id,
                Username = email, LastName = alias,
                EmailEncodingKey='UTF-8', LanguageLocaleKey='en_US',
                LocaleSidKey='en_US', TimeZoneSidKey='Asia/Kolkata');
        }
    }
  • Open theory — interface, chaining, finalizers, when-which
    • Implements Queueable (+ Database.AllowsCallouts for callouts). Instance class → complex state: pass sObjects, lists, anything via constructor.
    • Id jobId = System.enqueueJob(new KycCheckJob(ids)); returns an Id → monitor via AsyncApexJob (Status, NumberOfErrors) or Setup → Apex Jobs.
    • Chaining: inside execute() you may enqueue one child job. Unlimited depth in prod; capped at 5 in Developer/Trial orgs. In tests, chaining throws — guard with if (!Test.isRunningTest()).
    • Transaction Finalizers: System.attachFinalizer(this) with a class implementing Finalizer — its execute(FinalizerContext ctx) runs even if the queueable died on an uncatchable limit error. ctx.getResult() tells success/failure → retry or log. Mentioning finalizers = instant senior signal.
    public class KycCheckJob implements Queueable, Database.AllowsCallouts, Finalizer {
        private List<Id> accountIds;
        public KycCheckJob(List<Id> accountIds) { this.accountIds = accountIds; }
    
        public void execute(QueueableContext ctx) {
            System.attachFinalizer(this);
            // callout + DML here ...
            if (needsSecondPass && !Test.isRunningTest()) {
                System.enqueueJob(new KycCheckJob(remainingIds)); // one child max
            }
        }
        public void execute(FinalizerContext fc) {
            if (fc.getResult() == ParentJobResult.UNHANDLED_EXCEPTION) {
                ErrorLogger.log(fc.getException(), 'KycCheckJob', 'execute', null);
            }
        }
    }
    When which (the answer table lives in Reference → Async comparison)
    • Rule of thumb: Queueable is the default; @future only for trivial fire-and-forget/mixed-DML; Batch for >50k rows; Schedulable for timing.
  • Spec
    1. F1: script that inserts an Account AND a User in one transaction → capture the MIXED_DML error text; then fix via the @future UserProvisioner. Keep both versions in the repo with a comment explaining.
    2. Q1: AccountEnrichmentJob (Queueable): takes List<Id>, sets Enriched__c=true + timestamp, then chains EnrichmentLogJob writing a summary row to Error_Log__c (Severity=Info). Finalizer logs failures.
    3. Tests: Test.startTest(); System.enqueueJob(...); Test.stopTest(); then assert the field changes. Assert job ran via [SELECT Status FROM AsyncApexJob WHERE Id = :jobId]. Remember the chain-guard for tests.

    Done when: tests green; you've watched the jobs in Setup → Apex Jobs; you can say why Q1 couldn't be @future (complex params, chaining, monitoring).

  • Questions + key points
    • future vs Queueable — full comparison. (params, chaining, job Id, monitoring, when each — the headline answer of the week)
    • Why no sObject params in @future? (serialization + staleness; pass Ids)
    • How do you chain jobs and what are the limits? (1 child per execute; dev-org depth 5; test guard)
    • What is a Transaction Finalizer? (post-mortem hook, runs even on limit death, retry pattern)
    • When does async work actually start? (after commit; order not guaranteed)
DAY09

Async Apex II — Batch & Schedulable

Theory 2.5h · Build 3.5h · Drill 1h · Comm 45m
0/6

Millions of rows, nightly jobs, CRON strings — the "enterprise" half of async. Your Dormant Account Flagger built today goes straight into the capstone.

  • Open theory — the interface, statefulness, monitoring
    public class DormantAccountBatch implements
            Database.Batchable<SObject>, Database.Stateful {
        private Integer flagged = 0;
    
        public Database.QueryLocator start(Database.BatchableContext bc) {
            return Database.getQueryLocator([
                SELECT Id FROM Account
                WHERE LastActivityDate < :Date.today().addDays(-180)
                  AND Status__c != 'Dormant']);
        }
        public void execute(Database.BatchableContext bc, List<Account> scope) {
            for (Account a : scope) { a.Status__c = 'Dormant'; }
            update scope;
            flagged += scope.size();
        }
        public void finish(Database.BatchableContext bc) {
            System.debug('Flagged: ' + flagged);
            // email summary, or chain the next job here
        }
    }
    // run: Database.executeBatch(new DormantAccountBatch(), 200);
    • start returns a QueryLocator (up to 50M rows) or an Iterable (custom lists, but 50k limit applies).
    • execute runs per chunk — default scope 200, settable up to 2,000 via executeBatch(job, scope). Each execute = fresh governor limits; one chunk failing doesn't stop the others.
    • Database.Stateful preserves instance fields across executes (counters, error lists) — without it every chunk starts clean. Costs serialization time; use only when needed.
    • finish runs once at the end — summary email, chaining the next batch, enqueueing a queueable.
    • Limits: 5 concurrent batch jobs; 100 more can wait in the flex queue. Monitor: AsyncApexJob / Setup → Apex Jobs. Callouts: implement Database.AllowsCallouts; scope size × callouts-per-record must stay under 100 per execute — a real interview scenario ("how would you size scope for a callout batch?").
  • Open theory — scheduling, CRON format, the thin-shell rule
    public class NightlyDormantSweep implements Schedulable {
        public void execute(SchedulableContext sc) {
            Database.executeBatch(new DormantAccountBatch(), 200); // thin shell
        }
    }
    // 2:00 AM every day:
    System.schedule('Dormant sweep', '0 0 2 * * ?', new NightlyDormantSweep());
    • CRON fields: Seconds Minutes Hours Day-of-month Month Day-of-week [Year]. Examples: '0 0 2 * * ?' daily 2am · '0 30 18 ? * MON-FRI' weekdays 18:30 · '0 0 8 1 * ?' 1st of month 8am.
    • Thin-shell rule: keep logic out of the Schedulable — the scheduled class version gets pinned, complicating deploys; it should only launch a batch/queueable.
    • Manage: Setup → Scheduled Jobs; System.abortJob(cronTriggerId); query CronTrigger. Max 100 scheduled jobs.
    • Alternative worth name-dropping: Scheduled Flows for declarative nightly work; and scheduled batch via the UI (Apex Classes → Schedule Apex).
  • Spec
    1. Create Status__c picklist on Account (Active/Dormant) if not present.
    2. Batch per the skeleton, but: Stateful counters for flagged + skipped; collect failures with Database.update(scope,false) + ErrorLogger; finish() sends a summary email via Messaging.SingleEmailMessage to yourself.
    3. Schedulable thin shell + schedule it for 2 AM; verify it appears in Scheduled Jobs.
    4. Tests: seed old-date accounts (careful — LastActivityDate isn't writeable; filter on a custom Last_Touch__c Date field you control instead. Note this in README: test-data-friendly design is a real skill), Test.startTest(); Database.executeBatch(...); Test.stopTest(); assert statuses + counter via a public @TestVisible field. Test the Schedulable with System.schedule in-test + assert CronTrigger exists.

    Done when: green tests incl. batch + schedulable; summary email arrives in your inbox from a real run.

  • Questions + key points
    • Walk through Batchable's three methods. (roles + limits reset per execute)
    • QueryLocator vs Iterable? (50M vs 50k; when custom iteration)
    • What does Database.Stateful do? (fields survive across executes; serialization cost)
    • Write the CRON for weekdays 6:30 pm. ('0 30 18 ? * MON-FRI')
    • How do you size batch scope for callouts? (≤100 callouts per execute → scope accordingly)
    • All four async types — one-line when-to-use each. (the Reference table, from memory)
DAY10

Integration I — callouts, JSON, Named Credentials · JOB HUNT LAUNCH

Theory 2.5h · Build 2.5h · Job hunt 2h · Drill 45m · Comm 45m
0/8

Two launches: your first real API integration, and your candidacy. From today, 45 minutes of job-hunt work is a daily non-negotiable.

  • Open theory — Http classes + the rules
    HttpRequest req = new HttpRequest();
    req.setEndpoint('callout:Exchange_API/v6/latest/USD'); // Named Credential merge
    req.setMethod('GET');
    req.setTimeout(20000);                                  // ms, max 120000
    HttpResponse res = new Http().send(req);
    if (res.getStatusCode() == 200) {
        Map<String, Object> body =
            (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
    }
    • Limits: 100 callouts/transaction; cumulative timeout 120s.
    • No callout after DML in the same transaction — "uncommitted work pending." Order: callout first, DML after — or split into async.
    • Triggers → never call out synchronously; enqueue a Queueable with AllowsCallouts.
    • Design words that impress: idempotency (safe retries — pair with upsert + External Id), retry with backoff (finalizer or scheduled re-run), timeout budgets.
  • Open theory — where secrets live, how auth flows work
    • Named Credential = endpoint + auth config stored by the platform; code says callout:My_NC/path and never touches secrets. Modern setup splits External Credential (the auth: OAuth/JWT/Basic + principals) from the Named Credential (the URL). Legacy NCs bundle both — recognize either in an org.
    • Why it matters in interviews: "no hardcoded endpoints or passwords; rotation without deploys; per-user vs named-principal identity" — say exactly that.
    • Raw endpoint without NC → requires Remote Site Setting (the older allowlist).
    • OAuth flows, one line each (for "how does the external system authenticate?"): Authorization Code = a human logs in (web apps). Client Credentials = server-to-server with client id/secret. JWT Bearer = server-to-server with a certificate, no user interaction — the classic choice for middleware → Salesforce. Inbound callers hit a Connected App to get a token, then call your API with Authorization: Bearer ….
  • Open theory — wrappers, deserialization, the reserved-word trick
    // Typed — define a wrapper mirroring the payload
    public class RatePayload {
        public String base_code;
        public Map<String, Decimal> conversion_rates;
    }
    RatePayload p = (RatePayload) JSON.deserialize(res.getBody(), RatePayload.class);
    Decimal inr = p.conversion_rates.get('INR');
    
    // Untyped — spelunking with casts
    Map<String, Object> root =
        (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
    Map<String, Object> rates = (Map<String, Object>) root.get('conversion_rates');
    Decimal inr2 = (Decimal) rates.get('INR');
    • Typed = compile-safe, testable — default for stable APIs. Untyped = flexible for ragged payloads.
    • JSON key is an Apex reserved word (currency, case)? Wrapper can't declare it — either pre-process body.replace('"currency":', '"currency_x":') or read that field untyped.
    • JSON.serialize(obj) for outbound; JSON.serializePretty for debugging. Tools: JSON2Apex generates wrappers from a sample payload.
  • Spec
    1. Pick a free, keyless API — https://open.er-api.com/v6/latest/USD works. Create the Named Credential (anonymous auth) → name it Exchange_API.
    2. Design for testability: interface RateProvider { RatePayload fetch(String base); } + HttpRateProvider implementation. ExchangeRateService.refresh('USD') calls the provider, upserts Exchange_Rate__c rows (fields: Base__c, Target__c, Rate__c, unique External Id Pair_Key__c = "USD-INR") — your upsert + External Id story, live.
    3. Failures → ErrorLogger. Run from anonymous; verify rows.

    Done when: rates in the org, re-running updates instead of duplicating (External Id proof), zero hardcoded URLs.

  • Launch checklist
    1. Resume (1 page): headline "Salesforce Developer — Apex · LWC · Integrations · Sales/Service Cloud · FSC (learning) · PD1 (scheduled)". Projects section with GitHub links + 3 bullets each using the formula in Reference. Honest experience section — your real background, framed toward Salesforce.
    2. LinkedIn: same headline; About = 4 lines (who, stack, proof links, what you're looking for); Featured = repo links. Naukri: refresh profile daily before 9am (recency boosts ranking); keywords field stuffed honestly: Apex, LWC, SOQL, Triggers, REST API, Integration, Sales Cloud, Service Cloud, Financial Services Cloud, SFDX.
    3. Tracker (sheet): Company · Role · Source · Date · Contact · Status · Next action. Every touch gets logged.
    4. Apply: 10 today — LinkedIn + Naukri, filter "Salesforce developer" 0–5 yrs, include partner firms. DM: 5 referral messages (template in Reference) to devs at Salesforce partners.
  • Questions + key points
    • Why Named Credentials over hardcoded endpoints? (secrets, rotation, principals)
    • Which OAuth flow for server-to-server and why? (client credentials / JWT; no human)
    • "Uncommitted work pending" — what happened? (DML before callout; reorder or async)
    • Typed vs untyped JSON handling? (wrapper vs Map casts; when each)
    • How do you make an integration idempotent? (upsert + External Id; retry-safe)
DAY11

Integration II — expose REST, mock callouts, Platform Events

Theory 2.5h · Build 3h · Job 45m · Drill 45m · Comm 45m
0/9

Yesterday you called out; today the world calls you — plus the event-driven patterns (Platform Events, CDC) that separate 5-year answers from 2-year answers.

  • Open theory — @RestResource end to end
    @RestResource(urlMapping='/v1/leads/*')
    global with sharing class LeadIntakeApi {
        global class LeadRequest { global String firstName, lastName, email, product; }
        global class ApiResponse { global Boolean ok; global String message, id; }
    
        @HttpPost
        global static ApiResponse create() {
            RestRequest req = RestContext.request;
            ApiResponse out = new ApiResponse();
            try {
                LeadRequest body = (LeadRequest) JSON.deserialize(
                    req.requestBody.toString(), LeadRequest.class);
                // dedupe check + insert here...
                out.ok = true; out.id = newLead.Id;
            } catch (Exception e) {
                RestContext.response.statusCode = 400;
                out.ok = false; out.message = e.getMessage();
                ErrorLogger.log(e, 'LeadIntakeApi', 'create', null);
            }
            return out;                    // serialized to JSON automatically
        }
    }
    • URL: https://<mydomain>.my.salesforce.com/services/apexrest/v1/leads/. Wildcard path parsed from req.requestURI.
    • One class per urlMapping; one method per verb (@HttpGet/Post/Put/Patch/Delete).
    • Auth story for interviews: caller uses a Connected App → OAuth token (client credentials/JWT) → Bearer header → API call counts against daily API limits; give the API user a minimal permission set.
    • Return typed wrappers (never raw SObjects — you'll leak fields); set statusCode deliberately (201/400/404/409).
  • Open theory — no real callouts in tests, ever
    @isTest
    global class RateApiMock implements HttpCalloutMock {
        global HttpResponse respond(HttpRequest req) {
            HttpResponse res = new HttpResponse();
            res.setStatusCode(200);
            res.setBody('{"base_code":"USD","conversion_rates":{"INR":83.2}}');
            return res;
        }
    }
    // in the test:
    Test.setMock(HttpCalloutMock.class, new RateApiMock());
    Test.startTest();  ExchangeRateService.refresh('USD');  Test.stopTest();
    Assert.areEqual(83.2, [SELECT Rate__c FROM Exchange_Rate__c
                           WHERE Pair_Key__c = 'USD-INR'].Rate__c);
    • Variants: StaticResourceCalloutMock (body from a Static Resource), or one mock class that switches on req.getEndpoint() for multi-endpoint flows.
    • Testing inbound REST: build the context yourself — RestContext.request = new RestRequest(); request.requestBody = Blob.valueOf(jsonString); then call the method directly and assert the wrapper + statusCode.
  • Open theory — event-driven Salesforce
    • Platform Event = a custom pub/sub message (KYC_Result__e, define like an object). Publish: EventBus.publish(new KYC_Result__e(Status__c='CLEAR')); from Apex/Flow/API. Subscribe: an after-insert trigger on the event, a flow, or external systems (CometD/Pub-Sub API). Config per event: publish immediately (even if txn rolls back) vs after commit.
    • Change Data Capture = system-generated events on record changes (AccountChangeEvent) — enable per object; carries changed fields + header. For keeping external systems in sync without polling.
    • Decision one-liners: fire-and-forget notify → Platform Event · replicate record changes → CDC · declarative SOAP ping with retries → Outbound Message · external system pulls → REST API + updated-since query. Full table: Reference.
    • Test trick: events publish at Test.stopTest(), or force delivery with Test.getEventBus().deliver();.
  • Spec

    Implement LeadIntakeApi per the skeleton: dedupe by email against existing Leads/Contacts → return 409-style ok=false, message='duplicate'; happy path creates the Lead and returns its Id. Test class covers happy/duplicate/malformed-JSON. Then prove it live: Workbench (workbench.developerforce.com) → utilities → REST Explorer → POST /services/apexrest/v1/leads/ with a JSON body — screenshot for the README.

  • Spec
    1. C3: mock suite for ExchangeRateService — 200 path, 500 path (assert ErrorLogger row), garbage-JSON path. Coverage of the service ≥ 90%.
    2. PE1: define KYC_Result__e (fields: Account_Id__c Text, Status__c Text). After-insert trigger on the event updates Account.KYC_Status__c. Publish from anonymous Apex; verify. Test with Test.getEventBus().deliver(). This exact pipeline returns in the capstone Day 20.
  • Questions + key points
    • Expose an Apex REST service end-to-end, including auth. (annotation → URL → Connected App/OAuth → status codes)
    • How do you test callouts? (setMock; why tests forbid real callouts)
    • Platform Events vs CDC vs Outbound Message? (custom message vs record-sync vs declarative SOAP)
    • Publish immediately vs after commit? (audit/log events vs transactional consistency)
    • How would an external banking system push loan status into Salesforce? (your C2, narrated — or platform event via API)
DAY12

LWC I — JavaScript primer + component fundamentals

Theory 3h · Build 3h · Job 45m · Drill 45m · Comm 45m
0/8

LWC rounds fail on JavaScript, not on Salesforce. Today: modern JS survival kit, then your first two deployed components.

  • Open theory — the 12 things they actually ask
    const rates = [{ ccy: 'INR', rate: 83.2 }, { ccy: 'EUR', rate: 0.92 }];
    
    // arrow functions + array methods (map/filter/find/reduce/some)
    const codes  = rates.map(r => r.ccy);
    const high   = rates.filter(r => r.rate > 1);
    const inr    = rates.find(r => r.ccy === 'INR');
    const total  = rates.reduce((sum, r) => sum + r.rate, 0);
    
    // destructuring + spread + template literals
    const { ccy, rate } = inr;
    const copy  = { ...inr, updated: true };
    const label = `1 USD = ${rate} ${ccy}`;
    
    // optional chaining + nullish coalescing
    const name = response?.data?.account?.Name ?? 'Unknown';
    
    // promises & async/await
    async function loadRates() {
        try   { this.rows = await fetchRates({ base: 'USD' }); }
        catch (e) { this.error = e?.body?.message; }
    }
    • let/const (block scope) vs var (function scope, hoisted) — always const-first.
    • Arrow functions don't rebind this — why event handlers in LWC use them or class methods.
    • == vs === — always strict.
    • Promise states pending/fulfilled/rejected; async/await is syntax over promises; parallelize with Promise.all.
    • Modules: import { x } from 'y' — every LWC file is a module; export default class.
    • event.target (what was clicked) vs event.currentTarget (where the listener sits).
  • Open theory — how a component actually works
    • Bundle: myWidget/myWidget.html + .js + .js-meta.xml (+ optional .css, .svg). Name camelCase in code, kebab-case in markup: <c-my-widget>.
    • Reactivity: all class fields are reactive — reassignment re-renders. Mutating object/array internals is NOT tracked → reassign (this.rows = [...this.rows, newRow]) or decorate with @track (legacy deep-tracking). @api = public property/method (parent sets it). Getters compute derived display values.
    • Lifecycle order: constructor → @api props assigned → connectedCallback (fetch/subscribe) → renderrenderedCallback (DOM ready — guard it, it re-runs on every render!) → disconnectedCallback (cleanup: intervals, subscriptions). errorCallback = boundary for child errors.
    • Directives: lwc:if={x} / lwc:elseif / lwc:else (modern; you'll still see legacy if:true), for:each={rows} for:item="row" + mandatory key={row.Id} (diffing identity).
    • Meta: isExposed, targets (AppPage/RecordPage/HomePage/quick action/utility bar) + targetConfigs (design-time props). Record pages auto-inject @api recordId and objectApiName.
    • Base components (lightning-*): card, input, button, combobox, datatable, spinner, record-form family. Toasts: dispatch ShowToastEvent from lightning/platformShowToastEvent.
    // helloExplorer.js
    import { LightningElement } from 'lwc';
    export default class HelloExplorer extends LightningElement {
        name = '';
        topics = [{ id: 1, label: 'Apex' }, { id: 2, label: 'LWC' }];
        get greeting() { return this.name ? `Hello, ${this.name}!` : 'Type your name'; }
        handleInput(event) { this.name = event.target.value; }
    }
    <!-- helloExplorer.html -->
    <template>
      <lightning-card title="Hello Explorer">
        <div class="slds-p-around_medium">
          <lightning-input label="Name" onchange={handleInput}></lightning-input>
          <p lwc:if={name}>{greeting}</p>
          <p lwc:else>Waiting…</p>
          <template for:each={topics} for:item="t">
            <lightning-badge key={t.id} label={t.label}></lightning-badge>
          </template>
        </div>
      </lightning-card>
    </template>
  • Spec + deploy commands
    1. L1: the component above, typed by hand, plus a "clear" button and a computed character count getter.
    2. L2: lightning-datatable with hardcoded column defs (label/fieldName/type) + 10 static account rows; sortable columns (handle onsort — array sort + reassign, your JS practice applied).
    3. Deploy: sf project deploy start --source-dir force-app (or right-click → Deploy in VS Code). Lightning App Builder → new App Page → drop both components → activate.

    Done when: both live on an app page in your org; sorting works; you can explain why the sort handler reassigns the array.

  • Questions + key points
    • Lifecycle hooks in order, with one use case each.
    • @api vs @track vs plain fields? (public vs deep-track legacy vs default reactivity)
    • Why is key required in for:each? (diff identity, no index keys)
    • var vs let vs const; == vs ===?
    • Arrow functions and this? (lexical this — why handlers break with function())
    • What does the meta.xml control? (exposure, targets, design props)
DAY13

LWC II — data: wire, imperative, LDS

Theory 2.5h · Build 3.5h · Job 45m · Drill 45m · Comm 45m
0/7

"Wire vs imperative" is the #1 LWC interview question. Today you build accountSearch — the component you'll rebuild from scratch, timed, at the Day-14 gate.

  • Open theory — the free layer everyone forgets
    • lightning-record-form — whole form from layout/fields in one tag (fastest). record-edit-form / record-view-form — per-field control with lightning-input-field. All respect FLS + sharing automatically, share a client cache, and need no Apex — say "I use LDS first, Apex only when it can't" in interviews.
    • uiRecordApi module: getRecord (@wire single record), getFieldValue, createRecord / updateRecord / deleteRecord (imperative promises), getRecordNotifyChange([{recordId}]) — tell the cache you changed data behind its back via Apex.
    • Decision: single-record display/edit → LDS. Lists, aggregates, cross-object logic, DML with business rules → Apex.
  • Open theory — cacheable, provisioning, refreshApex, errors
    // Apex controller
    public with sharing class AccountSearchController {
        @AuraEnabled(cacheable=true)                 // wire REQUIRES cacheable
        public static List<Account> search(String term) {
            String like = '%' + String.escapeSingleQuotes(term) + '%';
            return [SELECT Id, Name, Industry FROM Account
                    WHERE Name LIKE :like WITH USER_MODE LIMIT 20];
        }
        @AuraEnabled                                  // imperative (DML allowed)
        public static void updateRating(Id accId, String rating) {
            try {
                update new Account(Id = accId, Rating = rating);
            } catch (Exception e) {
                throw new AuraHandledException('Could not update: ' + e.getMessage());
            }
        }
    }
    // JS side
    import search from '@salesforce/apex/AccountSearchController.search';
    import { refreshApex } from '@salesforce/apex';
    
    export default class AccountSearch extends LightningElement {
        term = '';
        wiredResult;                       // hold provisioned value for refreshApex
    
        @wire(search, { term: '$term' })   // '$term' = reactive: re-runs on change
        wired(result) {
            this.wiredResult = result;
            const { data, error } = result;
            if (data)  { this.rows = data;  this.error = undefined; }
            if (error) { this.error = error.body?.message; }
        }
        async handleRefresh() { await refreshApex(this.wiredResult); }
    
        async handleSave() {               // imperative for DML
            try {
                await updateRating({ accId: this.selectedId, rating: 'Hot' });
                await refreshApex(this.wiredResult);
            } catch (e) { this.error = e.body?.message; }
        }
    }
    • Wire: declarative, cached client+server side, reactive to $params, runs on load — read-only views. cacheable=true forbids DML inside.
    • Imperative: you decide when (click, debounce), can hit non-cacheable methods, do DML.
    • refreshApex(provisionedValue) — re-runs the wire; you must have stored the whole provisioned object.
    • AuraHandledException — the only way your error message survives to e.body.message cleanly; anything else becomes a generic "internal error".
    • Debounce pattern for search-as-you-type: clearTimeout(this.t); this.t = setTimeout(() => { this.term = v; }, 300);
  • Spec
    1. Search input (debounced 300ms) → wired search → datatable results; spinner while loading; friendly empty state; error toast on failure.
    2. Row action "Open" → NavigationMixin.Navigate to the record page. Row action "Mark Hot" → imperative updateRating → toast → refreshApex.
    3. Controller: both methods above + test class (happy, empty term, AuraHandledException path asserted with try/catch).
    4. Place on the App Page next to L1/L2.

    Done when: search feels instant, errors are human-readable, tests green. Tomorrow you rebuild this in 45 minutes — take notes on what slowed you down.

  • Questions + key points
    • Wire vs imperative — when each? (the full answer, with cacheable + reactivity + DML)
    • What does cacheable=true actually do? (client cache + wire eligibility + no DML)
    • How does refreshApex work? (provisioned value, not the data)
    • Why AuraHandledException? (clean message to e.body.message)
    • LDS vs Apex — decision rule? (single-record CRUD free vs lists/logic)
DAY14

LWC III — component communication + Week-2 gate

Theory 1.5h · Build 4h · Gate 1.5h · Job 45m · Comm 45m
0/8

Three communication routes, one mini service console, and the Week-2 gate. PD1 nightly questions start tonight.

  • Open theory — parent↔child↔stranger
    ① Parent → child: @api
    // child exposes:  @api caseRecord;  @api focus() { ... }
    <c-case-card case-record={selectedCase}></c-case-card>
    // parent calls a public method:
    this.template.querySelector('c-case-card').focus();
    ② Child → parent: CustomEvent
    // child dispatches (name: lowercase, no "on", no caps):
    this.dispatchEvent(new CustomEvent('caseselect', {
        detail: { caseId: this.caseRecord.Id }
    }));
    // parent listens in markup:
    <c-case-card oncaseselect={handleSelect}></c-case-card>
    // handleSelect(event) { this.selectedId = event.detail.caseId; }

    Defaults: bubbles:false, composed:false — the event stops at the parent; crossing shadow boundaries is opt-in and rarely right.

    ③ Unrelated components: Lightning Message Service
    // messageChannels/CaseSelected.messageChannel-meta.xml declares the channel
    import { publish, subscribe, MessageContext } from 'lightning/messageService';
    import CASE_SELECTED from '@salesforce/messageChannel/CaseSelected__c';
    
    @wire(MessageContext) messageContext;
    // publisher:
    publish(this.messageContext, CASE_SELECTED, { caseId: id });
    // subscriber (connectedCallback):
    this.sub = subscribe(this.messageContext, CASE_SELECTED,
        (msg) => this.load(msg.caseId));
    // disconnectedCallback: unsubscribe(this.sub);

    Decision: same DOM tree → props down, events up. Different regions/tabs (even LWC↔Aura↔Visualforce) → LMS. Table in Reference → LWC cheatsheet.

  • Spec
    1. caseListFiltered (parent): status combobox filter → wired case list → renders caseCard children (@api caseRecord).
    2. caseCard (child): shows subject/priority/age; click → CustomEvent caseselect up AND parent publishes on LMS channel.
    3. caseDetailPanel (stranger, separate region): subscribes to LMS → loads case via lightning-record-view-form; "Close case" button → imperative Apex → toast → refreshApex on the list (getRecordNotifyChange for the form).
    4. Seed 15 cases via anonymous script. Assemble on the App Page: list left, detail right.

    Done when: click a card on the left, detail loads on the right through LMS, closing a case updates both — narrate that flow once on video (it's a portfolio demo).

  • The gate
    1. Timed: rebuild accountSearch (fresh component, 45 min): debounced input, wire, datatable, one imperative action, toast. Pass = works, even if ugly.
    2. Quiz /12, pass ≥ 9: future vs Queueable vs Batch vs Schedulable (4 pts) · callout after DML error · Named Credential why · mock a callout · expose REST + auth · PE vs CDC · wire vs imperative · refreshApex · the 3 communication routes.
WEEK 2 EXIT: repo #2 live with the console demo · search LWC rebuilt in 45 min · quiz ≥ 9/12 · applications flowing daily · PD1 nightly habit started.

Week 3 — The Three Clouds + Capstone

Sales and Service Cloud through a developer's eyes, then Financial Services Cloud — which is a managed package on top of the first two, so everything from Weeks 1–2 transfers. From Day 19 you're building the capstone: the banking project that becomes your "experience" in every interview answer.

  • Repo #3: bank-onboarding-capstone — the resume centerpiece. Full checklist: Reference → Capstone.
  • Org strategy: if the FSC trial works out, build the capstone there (real FinServ__ objects — more impressive). If not, mirror the model in your dev org with custom objects. Either way code lives in Git; the trial expires — screenshots + demo video are your proof.
  • Exit gate (Day 21): whiteboard all three data models from memory; capstone pipeline demo-able end to end.
DAY15

Sales Cloud — the developer's view

Theory 2.5h · Build 3h · Job 45m · PD1 45m · Comm 45m
0/9

Lead-to-cash mechanics: what actually happens on lead convert, and the product/pricebook chain everyone fumbles in interviews.

  • Open theory — what convert really does
    • Path: capture (web-to-lead / API / manual) → assignment rules route → nurture → convert → Account + Contact + (optional) Opportunity, lead flips to IsConverted=true with ConvertedAccountId/ContactId/OpportunityId.
    • Field mapping: standard fields map automatically; custom lead fields need the mapping in Setup → Lead Fields → Map Lead Fields.
    • What fires on convert (interview favorite): the lead's update triggers (before/after) and insert triggers on Account/Contact/Opportunity for the created records. Validation rules and required fields are skipped by default unless "Require Validation for Converted Leads" is enabled — a classic "why did bad data get through?" answer.
    • Convert from Apex:
    Database.LeadConvert lc = new Database.LeadConvert();
    lc.setLeadId(lead.Id);
    lc.setConvertedStatus('Qualified');   // from LeadStatus where IsConverted=true
    lc.setDoNotCreateOpportunity(false);
    Database.LeadConvertResult r = Database.convertLead(lc);
    • Fire assignment rules from Apex (they don't run on plain DML):
    Database.DMLOptions dmo = new Database.DMLOptions();
    dmo.assignmentRuleHeader.useDefaultRule = true;
    newLead.setOptions(dmo);
    insert newLead;
  • Detect conversion in a trigger: Trigger.isUpdate && c.IsConverted && !Trigger.oldMap.get(c.Id).IsConverted.
  • Open theory — the chain, drawn
    Product2 ──< PricebookEntry >── Pricebook2 (Standard + custom books)
                         │
                         ▲
    Opportunity ──< OpportunityLineItem (references PricebookEntry)
    Quote ──────< QuoteLineItem  (mirrors OLI)
    • Rules that make it an interview question: a product must have a Standard pricebook entry before any custom pricebook entry; an Opportunity locks to one pricebook (Pricebook2Id) once it has line items; OLI stores UnitPrice × Quantity → TotalPrice; Amount rolls up from OLIs when products are used.
    • In tests: Test.getStandardPricebookId() gives you the standard book without SeeAllData.
    • Campaigns: Campaign ──< CampaignMember (Lead/Contact) — attribution basics; Opportunity Contact Roles for who-influenced-what.
    • Person Accounts (FSC prerequisite): B2C model where Account+Contact merge into one record — IsPersonAccount, PersonContactId, person fields like PersonEmail. Enabled by Salesforce support, irreversible; account record types split business vs person. Code smell to avoid: assuming every Account has separate Contacts — check IsPersonAccount. Your FSC trial org has them on; explore there.
  • Spec
    1. Config (admin muscles): custom lead fields Product_Interest__c (picklist: Savings/Loan/Investment) + Monthly_Income__c (Currency) with lead field mapping to Opportunity/Account; one assignment rule (Banking leads → you, else queue "Unassigned Leads"); auto-response rule with a template.
    2. Web-to-lead: generate the form (Setup → Web-to-Lead), save the HTML locally, open in a browser, submit — watch a lead arrive with assignment applied. (This HTML file becomes the capstone intake later.)
    3. T5 LeadTrigger: on conversion (detect the flip), copy Product_Interest__c onto the new Opportunity's Type-style custom field and create a follow-up Task for the opp owner ("Call within 24h"). Bulk-safe (convert 10 via Apex in one go in your test). Handler framework, tests, done.

    Done when: submit form → lead assigned → convert → opp carries data + task exists; tests cover bulk conversion via Database.convertLead in a loop of LeadConvert objects (one convertLead call with a List).

  • Questions + key points
    • What exactly happens on lead conversion? (records created, field mapping, IsConverted flags)
    • Which triggers fire on convert? Do validation rules run? (update on lead + inserts on A/C/O; skipped unless the setting)
    • Draw the product → pricebook → OLI chain. (from memory, with the standard-entry-first rule)
    • How do you run assignment rules from Apex? (DMLOptions header)
    • What is a person account and what changes in code? (merged A+C, IsPersonAccount checks)
    • Opportunity Amount with products — where does it come from? (OLI rollup)
DAY16

Service Cloud — cases, SLAs & the console

Theory 2.5h · Build 3h · Job 45m · PD1 45m · Comm 45m
0/9

Case routing end-to-end and the entitlement/milestone model — the part of Service Cloud developers actually get asked about.

  • Open theory — channels in, rules through, agents out
    • Channels in: Web-to-Case, Email-to-Case (On-Demand = Salesforce-hosted routing address; the "agent" variant runs behind a firewall), portals/communities, API, phone (CTI).
    • Routing: Queues hold ownership; Case Assignment Rules (one active rule, ordered entries) place cases; Auto-Response Rules acknowledge; Escalation Rules are time-based (re-assign + notify when age crosses thresholds); Omni-Channel pushes work to available agents — queue-based or skills-based routing, presence statuses, capacity weights (know the concepts + words).
    • Support Processes = status subsets per record type (e.g., Onboarding case vs Complaint case).
    • Knowledge: Knowledge__kav versioned articles, record types + data categories; attach to cases; deflection.
    • Console note for devs: LWCs on record pages work in console apps; workspace tabs are manipulable via lightning/platformWorkspaceApi — name-drop, don't deep-dive.
  • Open theory — the model + the auto-complete pattern
    Account ──< Entitlement (what support the customer bought)
                    │  uses Entitlement Process (versioned SLA timeline)
                    │        └── Milestones (MilestoneType: "First Response", "Resolution")
    Case ── linked to Entitlement → CaseMilestone rows track TargetDate vs CompletionDate
    • Flow: case gets an Entitlement → its process instantiates CaseMilestone rows with target times (business hours aware); violation = SLA breach; escalation actions fire on warning/violation.
    • The dev pattern — auto-complete a milestone when the SLA-satisfying thing happens (first agent comment/email):
    public static void completeMilestone(Set<Id> caseIds, String milestoneName) {
        List<CaseMilestone> open = [
            SELECT Id FROM CaseMilestone
            WHERE CaseId IN :caseIds
              AND MilestoneType.Name = :milestoneName
              AND CompletionDate = null];
        for (CaseMilestone cm : open) { cm.CompletionDate = System.now(); }
        update open;
    }
    • Trigger it from CaseComment/EmailMessage after-insert (agent-authored only). This exact snippet is a beloved interview scenario: "how do you stop the First Response clock?"
  • Spec
    1. Queue "Onboarding Ops" (Case); assignment rule: Origin=Web → queue, Priority=High → you; auto-response template; escalation rule: High + 2h unresolved → escalate.
    2. Enable Entitlement Management → MilestoneTypes "First Response" (2h) + "Resolution" (24h) → Entitlement Process "Standard Support" → Entitlement on 3 test accounts; set the case lookup so new cases pick it up (entitlement auto-selection via trigger: copy Account's active entitlement onto the case — small T-snippet, write it).
    3. Create cases via web-to-case form + manual; watch CaseMilestone rows appear (add the Milestones related list / Milestone Tracker to the case page).
  • Spec
    1. T6: CaseComment after insert (comment by internal user) → completeMilestone(caseIds, 'First Response'). Bulk-safe; tests: case with entitlement, add comment, assert CompletionDate set.
    2. L6 caseSlaTracker (Case record page): imperative Apex loads open CaseMilestones (@api recordId); render name + countdown to TargetDate; setInterval in connectedCallback ticks it down, cleared in disconnectedCallback (say why in the README — interval leaks are an interview probe); red style under 30 min.

    Done when: comment on a case completes First Response live while the tracker shows it; interval verified cleaned (console.log on disconnect).

  • Questions + key points
    • Entitlement vs Entitlement Process vs Milestone vs CaseMilestone? (the model, drawn)
    • How do you auto-complete "First Response"? (the snippet, narrated)
    • Queues vs assignment rules vs Omni-Channel? (hold vs place vs push)
    • Email-to-Case flavors? (on-demand vs behind-firewall agent)
    • Escalation rules — what triggers them? (time thresholds, business hours)
    • Where would Knowledge fit in a banking service desk? (articles, categories, deflection)
DAY17

Flow vs Apex + the sharing model — the architecture day

Theory 3h · Build 3h · Job 45m · PD1 45m · Comm 45m
0/9

The two judgment topics that mark you as senior: when NOT to write code, and who-sees-what from OWD to Apex managed sharing.

  • Open theory — flow types, invocables, when-which
    • Types: record-triggered (before-save = fast field updates, no related records; after-save = everything else), screen flows (guided UI), scheduled, autolaunched (called by Apex/API), platform-event-triggered. Workflow Rules + Process Builder are retired-in-spirit — migrate answers to Flow.
    • Decision framework (say it this cleanly): declarative first for field updates, simple record creation, guided screens, approvals. Apex when: complex collection logic, cross-object transforms at volume, callouts with retry/error engineering, anything needing unit tests, reusable services. Watch-outs for flow at scale: loops with DML inside, no bulk-safe error isolation, hard to code-review.
    • Order reminder: before-save flows → before triggers → after triggers → after-save flows.
    • Apex ⇄ Flow bridges:
    public class KycActions {
        public class Request  { @InvocableVariable(required=true) public Id accountId; }
        public class Result   { @InvocableVariable public String status; }
    
        @InvocableMethod(label='Run KYC Check' description='Calls KYC service')
        public static List<Result> run(List<Request> requests) {   // ALWAYS list-in, list-out
            List<Result> out = new List<Result>();
            for (Request r : requests) { /* bulk-safe work */ }
            return out;
        }
    }
    // Apex calling a flow:
    // new Flow.Interview.My_Flow(new Map<String,Object>{'recordId'=>id}).start();
  • Open theory — the who-sees-what ladder
    1. Profiles / Permission Sets — object CRUD + FLS (what kind of access). Modern orgs: minimal profile + permission-set-group model.
    2. OWD (org-wide defaults) — the baseline per object: Private / Read / Read-Write / Controlled by Parent. Everything below only opens up from here…
    3. Role hierarchy — managers see subordinates' records (grant access via hierarchies).
    4. Sharing rules — owner-based or criteria-based, to roles/groups. Restriction rules are the rare "take away" tool.
    5. Manual sharing / Teams (account/case/opportunity teams).
    6. Apex managed sharing — programmatic rows in the object's Share table:
    // custom object Loan_Application__c → share table Loan_Application__Share
    Loan_Application__Share sh = new Loan_Application__Share(
        ParentId      = loanApp.Id,
        UserOrGroupId = complianceGroupId,
        AccessLevel   = 'Read',
        RowCause      = Schema.Loan_Application__Share.RowCause.Compliance_Review__c);
    insert sh;   // Apex sharing reasons: custom objects only
    • Implicit sharing exists (account ↔ child cases/opps) — name it if asked why someone "mysteriously" sees a case.
    • Debug path for "user can't see record" (rehearse verbatim): object perms → FLS → OWD → role → sharing rules → manual/teams → share table rows.
    • Record locks: parallel updates → UNABLE_TO_LOCK_ROW; mitigate with SELECT ... FOR UPDATE, retry, or serializing writes (say "I'd queue updates through one async path").
  • Spec
    1. Before-save flow on Case: default Priority by Origin (Web→Medium, Phone→High) — note in README why this beats a before trigger here (no code to maintain) and when it wouldn't (complex lookups at volume).
    2. Screen flow "Log a Complaint": screen (account picker + description) → create Case + follow-up Task → fault path on the create element → screen showing the error + Error_Log__c record via… your invocable logger (wrap ErrorLogger in an @InvocableMethod — 10 lines).
    3. Invocable KYC stub: the KycActions class above returning a fake status; call it from the screen flow; you'll swap the fake for the real callout in the capstone.

    Done when: all three run in the org; you can argue flow-vs-apex for each with a straight face.

  • Questions + key points
    • Flow vs Apex — your framework? (declarative-first + the escape hatches; give examples both ways)
    • Before-save flow vs before trigger — order and when each?
    • Walk the sharing ladder from OWD to manual. (the numbered list, fluent)
    • User can't see a record — debug path? (verbatim rehearsed)
    • What is Apex managed sharing and when did you use it? (T7 story: compliance visibility)
    • @InvocableMethod signature rules? (static, list-in/list-out, one per class)
DAY18

Financial Services Cloud I — the data model

Theory 3h · Build 3h · Job 45m · PD1 45m · Comm 45m
0/9

FSC demystified: it's Sales + Service + a managed package of banking objects. Learn the household model cold — it's the one thing every FSC interviewer checks.

  • Open theory — the platform view
    • FSC = a managed package (namespace FinServ__) installed on core Sales + Service Cloud, plus verticals: retail banking, wealth management, insurance, mortgage.
    • Dev implications (interview gold): objects/fields arrive namespaced (FinServ__FinancialAccount__c, and in SOQL: FinServ__PrimaryOwner__c); you can't modify package code — you extend with your own triggers/LWCs/flows on their objects; upgrades are Salesforce-pushed, so never depend on package internals; your triggers coexist with package automation (order matters — test!).
    • Everything you learned in Weeks 1–2 IS FSC development: triggers on FinServ objects, LWCs on FSC record pages, integrations to core banking systems. FSC-specific knowledge = the data model + a feature vocabulary. That's why this is crackable in days, not years.
  • Open theory — the household model (THE FSC question)
                Account (record type: Household)
                  │  membership via AccountContactRelation (ACR)
       ┌──────────┴──────────┐
     Person Account        Person Account
     "Rahul Mehta"         "Priya Mehta"
       │ FinServ__PrimaryOwner__c        (roles on ACR: Client, Spouse, …)
       └──< FinServ__FinancialAccount__c  (Checking, Loan, Investment…)
                 └──< FinServ__FinancialAccountRole__c (joint owners, beneficiaries)
    • Client modeling: two options — Person Accounts (recommended, what your trial uses) or the legacy Individual model (Account + Contact 1:1 pairs). Know both exist and why PA won (single record, simpler code).
    • Household = an Account with the Household record type. Members join via AccountContactRelation — the standard object FSC leans on so one person can belong to several households/businesses with roles. FSC adds fields (primary group, roles) and rollup behavior on top.
    • Reciprocal Roles (FinServ__ReciprocalRole__c): paired titles — "Power of Attorney ↔ Grantor", "Accountant ↔ Client" — used by Contact-Contact / Account-Account relationship objects (FinServ__ContactContactRelation__c, FinServ__AccountAccountRelation__c).
    • Actionable Relationship Center (ARC) — the UI graph that renders all of this; name it when asked "how does a banker see the family?"
  • Open theory — the money objects + RBL
    • FinServ__FinancialAccount__c — the star: record types per product (Bank Account, Loan, Investment, Credit Card…), key fields FinServ__PrimaryOwner__c (→Account), FinServ__Balance__c, FinServ__Household__c.
    • FinServ__FinancialAccountRole__c — joint owners/beneficiaries/trustees (who else touches this account).
    • Supporting cast: FinServ__AssetsAndLiabilities__c (held-away assets), FinServ__FinancialGoal__c, FinServ__FinancialHolding__c + FinServ__Securities__c (wealth positions).
    • Rollup-By-Lookup (RBL): FSC's config-driven rollups (FinServ__RollupByLookupConfig__c) — Total Bank Deposits, Total Investments on the client and household — computed by the package on lookup relationships (where standard rollup summary fields can't go, since they need master-detail). Your Day-5 Apex rollup is the same idea hand-built: say exactly that in interviews. At scale some orgs move rollups to Data Processing Engine — name-drop only.
  • Spec (trial org) + fallback (vanilla org)
    1. In the FSC trial: create person accounts Rahul + Priya → household "Mehta Household" with both as members (roles) → 4 financial accounts (checking, savings, home loan, investment) with balances → add a joint-owner FinancialAccountRole → watch household RBL totals update.
    2. 8 SOQLs committed to the repo: ① all FAs of a client with balances ② household members via ACR ③ FAs rolled to household ④ joint owners of an FA ⑤ total balance by record type (aggregate) ⑥ clients with loans over X ⑦ contact-contact relations with roles ⑧ life-events-style query on any related list you find. Explore Object Manager to find real API names — that exploration IS the learning.
    3. Draw the model by hand (paper), photograph, commit as fsc-data-model.jpg. You will redraw this in interviews.
    4. Fallback (no trial): mirror it in your dev org: Financial_Account__c (record types, Balance__c, Primary_Owner__c lookup→Account), Financial_Account_Role__c, household = Account record type + AccountContactRelation (enable "Allow users to relate a contact to multiple accounts" in Account Settings). Same queries, your own namespace — and your Day-5 rollup plays the RBL part.
  • Questions + key points
    • How does FSC model a household? (Account record type + ACR membership + roles — whiteboard it)
    • Why ACR instead of Contact.AccountId? (many-to-many: one person, many households/businesses)
    • Person Account vs Individual model? (merged record vs A+C pairs; PA recommended)
    • What is Rollup-By-Lookup and how does it differ from rollup summary fields? (config rollups over lookups vs master-detail-only RSF)
    • Name 6 FSC objects and their jobs. (glossary in Reference → FSC glossary)
    • What changes for a developer when objects come from a managed package? (namespace, no source edits, upgrade safety, coexisting automation)
DAY19

FSC II — features devs touch + capstone kickoff

Theory 2h · Build 4h · Job 45m · PD1 45m · Comm 45m
0/8

The FSC feature vocabulary that fills out your answers — then the capstone repo goes live.

  • Open theory — one confident sentence per feature
    • Action Plans: reusable task checklists from templates (ActionPlanTemplate → ActionPlan) attached to records — the standard answer for "repeatable onboarding/KYC process with document checklist items."
    • Life Events: timeline of client milestones (marriage, home purchase, retirement) on the person account page — advisors act on them; devs automate creating them from data.
    • Interaction Summaries: structured, compliance-friendly meeting notes with role-based visibility — the wealth-management darling.
    • Compliant Data Sharing (CDS): participant-role-based access on sensitive records (deals, interactions) — finer than sharing rules, built for "only the deal team sees this."
    • Financial Deals (corporate/investment banking) — pipeline objects governed by CDS.
    • Insurance vertical objects (name-drop table): InsurancePolicy, InsurancePolicyParticipant, Claim, ClaimParticipant.
    • Mortgage objects: ResidentialLoanApplication + document checklist flow.
    • Data Processing Engine (DPE): batch transform engine some FSC orgs use for heavy rollups.
    • Interview stance: you don't claim years with these — you claim the model: "I know the objects and where custom code hooks in; here's my capstone on that data model."
  • Open the architecture
    [Web-to-Lead form]──┐
    [REST LeadIntake]───┤→ Lead (+dedupe trigger)
                        │      └─ convert → Person Account + Household
                        │                    └─ Financial_Account "Pending KYC"
                        │                          └─ Queueable KYC callout (Named Cred)
                        │                                ├─ success → Platform Event KYC_Result__e
                        │                                │    └─ PE trigger → Onboarding Case
                        │                                │         └─ Entitlement + Milestones
                        │                                │              └─ escalation + L6 tracker
                        │                                └─ failure → Finalizer retry + Error_Log__c
    [Nightly Schedulable] → Dormant Account Batch → summary email
    [LWC] newFinancialAccountWizard · clientFinancialSummary · caseSlaTracker
    [Flow] before-save case defaults · screen flow "Service Request" · invocable KYC

    Notice: every box is something you already built in Weeks 1–3. The capstone is assembly + polish, not new invention. That's also your interview line: "I designed it so each piece demonstrates one platform capability."

  • Spec
    1. Repo #3 bank-onboarding-capstone: sfdx project, README skeleton (problem → architecture diagram above → setup steps), data/seed.apex script.
    2. Objects (fallback org) / field additions (FSC org): KYC fields on Lead (Product_Interest__c, Monthly_Income__c — reuse Day 15), KYC_Status__c on Account, Financial_Account object per Day 18 fallback if needed.
    3. Intake: port the web-to-lead form + your C2 REST endpoint into this repo/org; port the dedupe trigger (T1 logic, now on Lead by email+phone). All with tests from day one.

    Done when: both intake paths create deduped Leads in the capstone org; repo public with the architecture in the README.

  • Questions + key points
    • What are Action Plans and when would you use them? (templated checklists; onboarding/KYC)
    • Compliant Data Sharing vs sharing rules? (participant roles on the record vs org-wide criteria)
    • Where does custom code hook into FSC? (triggers on FinServ objects, LWCs on FSC pages, integrations, flows)
    • Name the insurance-vertical objects. (policy, participants, claim)
    • Pitch your capstone in 90 seconds. (first rehearsal of the money answer)
DAY20

Capstone build I — the onboarding pipeline

Build 6.5h · Job 45m · PD1 45m · Comm 30m
0/8

Pure build day. By tonight a lead entering either door comes out the other end as a KYC-checked client with an onboarding case.

DAY21

Capstone build II — service side + UI · Week-3 gate

Build 5h · Gate 1.5h · Job 45m · PD1 45m
0/7

The capstone gets its face: the account wizard, the financial summary, live SLAs — then the Week-3 gate.

  • Spec
    1. 3 steps via lwc:if state machine: ① type (combobox: Savings/Loan/Investment) ② details (amount, nickname — validate > 0, required) ③ confirm (summary + submit).
    2. Submit → imperative Apex (with sharing, USER_MODE) creates the Financial Account linked to the client (@api recordId from the account page) → success toast → NavigationMixin to the new record. Errors → AuraHandledException surfaced in the wizard, not a toast-and-pray.
    3. Progress indicator (lightning-progress-indicator) across steps. Tests on the controller; manual test on a person account page.
  • The gate
    1. Whiteboard test: draw all three from blank paper: sales product chain · entitlement model · FSC household. Record yourself narrating each (3 min each). Fumble = redo that theory tomorrow morning.
    2. Quiz /12, pass ≥ 9: lead convert mechanics · triggers on convert · product chain rules · milestone auto-complete · queues vs omni · flow-vs-apex framework · sharing ladder · debug "can't see record" · household via ACR · RBL vs RSF · person accounts · managed-package implications.
WEEK 3 EXIT: capstone pipeline demo-able · all three data models drawable from memory · quiz ≥ 9/12 · FSC vocabulary fluent.

Week 4 — Certify · Drill · Interview

The capstone gets its final coat, PD1 gets taken, and every remaining hour converts knowledge into spoken answers. Interviews from your Week-2 applications land this week — treat the first one or two as paid practice rounds.

  • PD1: ~$200 + taxes, 60 questions / 105 min, pass 68%. Book the Day-25 slot on Day 23 (Webassessor). If the fee is genuinely out of reach, convert Days 23–25 into extra mocks + applications — the capstone carries you.
  • Rule of the week: nothing new after Day 26. Depth on what you have beats breadth you can't defend.
DAY22

Capstone III — automation, DevOps story, demo polish

Build 4h · Theory 1.5h · Job 45m · PD1 45m · Comm 30m
0/8

Finish the build, then learn to talk about shipping it — the deployment question is asked in literally every 5-year interview.

  • The polish checklist
    1. README: problem statement (3 lines) → architecture diagram → feature list mapped to platform capabilities → setup (sf org login web, deploy command, data/seed.apex) → test instructions + coverage screenshot → "design decisions" section (event decoupling, finalizer retry, flow-vs-apex calls, security model).
    2. Seed script: one Execute-Anonymous file that makes a fresh org demo-able in 2 minutes.
    3. Demo video (3–4 min): macOS screen record (Cmd+Shift+5) + your voice: form submit → lead → convert → KYC → case with live SLA → wizard → summary tiles. Upload unlisted (YouTube/Loom), link in README + resume. This video does more shortlisting work than any bullet point.
  • Open theory — environments, deploy paths, CI/CD vocabulary
    • Environments: Developer/Developer Pro sandbox (metadata + no/partial data) → Partial Copy → Full Copy (staging/UAT) → Production. Scratch orgs = disposable source-driven orgs from config/project-scratch-def.json (true CI use).
    • Deploy paths: Change Sets (clicky, org-to-org, no history) vs source-driven with sf CLI: sf project deploy start --source-dir force-app --target-org UAT (+ --dry-run, --tests flags) vs unlocked packages (versioned metadata bundles) vs commercial CI tools (Gearset/Copado) vs DevOps Center (Salesforce's free pipeline UI on top of Git).
    • Your scripted answer (own it, it's true): "My projects are source-tracked in Git with the sf CLI; feature branches, PRs, deploy with tests to a target org, and I've set up a GitHub Actions workflow that runs sf project deploy validate on pull requests. In client orgs I'd slot into whatever the pipeline is — change sets, Copado, DevOps Center — the Git discipline is the same."
    • Bonus points vocabulary: --dry-run validation deploys, test levels (RunLocalTests for prod), destructive changes manifest, profiles-vs-permission-sets in version control (spiky topic — permission sets deploy cleaner).
    • Actually add the GitHub Action to the capstone (20 min, template below) so the sentence above is verifiably true:
    # .github/workflows/validate.yml
    name: validate
    on: [pull_request]
    jobs:
      validate:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - run: npm install -g @salesforce/cli
          - run: echo "$SFDX_AUTH_URL" | sf org login sfdx-url --sfdx-url-stdin -a ci
            env: { SFDX_AUTH_URL: ${{ secrets.SFDX_AUTH_URL }} }
          - run: sf project deploy validate --source-dir force-app --target-org ci --test-level RunLocalTests
  • Questions
    • Describe your deployment process. (the scripted answer, verbatim, twice)
    • Change sets vs CLI vs packages — trade-offs?
    • Sandbox types and what you'd use each for?
    • How do you make sure a deploy doesn't break prod? (validate + tests + rollback plan)
DAY23

PD1 intensive I — mock #1 + the Visualforce survival kit

PD1 5h · Theory 1h · Job 45m · Comm 30m
0/7

First full timed mock, brutal review, and the one legacy topic (Visualforce) you haven't touched. Book the real exam today.

  • Blueprint (approx weights) + booking
    • Developer Fundamentals ~23% · Process Automation & Logic ~30% · User Interface ~25% · Testing, Debugging & Deployment ~22%. 60 questions, 105 min, pass 68%, proctored online or center.
    • Book via Webassessor (Kryterion) for Day 25, morning slot. Online-proctored needs: quiet room, webcam sweep, no second monitor.
    • Free official prep: Trailhead's "Prepare for Your Salesforce Platform Developer I Credential" trailmix — skim its unit quizzes tonight.
  • Open theory — the 60-minute VF tour
    <apex:page standardController="Account" extensions="AccountExt">
        <apex:form>
            <apex:pageBlock title="Edit">
                <apex:inputField value="{!Account.Name}"/>
                <apex:commandButton action="{!save}" value="Save"/>
            </apex:pageBlock>
        </apex:form>
    </apex:page>
    • Standard controller = free CRUD for one record ({!save}, {!Account.Name}); extensions add custom Apex (constructor takes ApexPages.StandardController); fully custom controllers exist.
    • Expression syntax {!...}, apex:repeat / pageBlockTable for lists, rerender for partial refresh, ViewState = the serialized page state (the classic "why is my VF page slow" answer — too much state).
    • Where VF survives in 2026: renderAs="pdf" documents, email templates, a few legacy consoles. Everything new = LWC — say that with confidence.
DAY24

PD1 intensive II + first full mock interview

PD1 4h · Mock 1.5h · Job 45m · Comm 45m
0/7

Second mock exam, then the dress rehearsal that matters: a full 60-minute technical interview, recorded.

  • The script (have a friend read it, or run it with Claude)
    1. "Introduce yourself" (2 min — rehearsed but human)
    2. "Walk me through your main project" (the capstone pitch + follow-ups: why events? why queueable? what breaks at 10× volume?)
    3. Rapid technical: order of execution · future vs queueable · wire vs imperative · governor limits you've hit · test a callout
    4. Scenario: "Nightly sync of 200k loan records from a core banking system — design it." (batch + upsert External Id + error handling + monitoring)
    5. "You don't have 5 years — why should we take you?" (the script from Reference, delivered calm)

    Watch the recording at 1.5×. Score: structure (skeleton used?), pace (silence beats "umm"), specificity (project references?). Fix the worst 5 answers by re-recording just those.

DAY25

PD1 exam day

Exam ~3h with buffer · Job 1.5h · Rest
0/5

Take the exam. Then let the result work for you the same afternoon.

  • Both branches
    • Passed: LinkedIn post (template in Reference) — cert + capstone link in one post; add credential to resume/Naukri headline ("PD1 Certified"); message the 5 warmest recruiter contacts directly with the update. Then 15 applications riding the momentum.
    • Missed it: retake is $100 after 24h wait — book it for ~Day 29 now, log the section scores into the flashcard file, and do the 15 applications anyway. The capstone + interview skills were always the main bet; the cert is a booster, not the rocket.
DAY26

Scenario-round training + the STAR bank

Drill 4.5h · Build 1h · Job 45m · Comm 1h
0/6

Five-year interviews are won in the scenario round. Today you rehearse the 15 designs that cover 90% of what gets asked.

  • Scenarios + approach hints (hide hints on first pass)
    1. Nightly sync of 200k records from core banking. (Batch + upsert External Id + partial-success logging + monitoring + reconciliation report)
    2. Users hit UNABLE_TO_LOCK_ROW during mass updates. (contention on parent; FOR UPDATE, retry with backoff, serialize via queueable, reduce parent touches)
    3. "A user can't see a record." (the sharing debug ladder, verbatim)
    4. Real-time notification to an external app when opportunity closes. (Platform Event / CDC + subscriber; why not callout-from-trigger)
    5. Trigger works for one record, fails on Data Loader. (bulkification story + how you'd find it: debug logs, limit usage)
    6. Approval needed from an external compliance system before case closes. (validation blocks close until flag; queueable callout on submit; PE on response flips flag)
    7. Duplicate customers keep appearing from three intake channels. (normalize + dedupe trigger, duplicate rules, upsert External Id per channel, survivorship rules)
    8. The org hits SOQL 101 in production after a release. (find the loop query — logs/limits, fix, and the process answer: PR review + bulk tests would have caught it)
    9. Design the household total-balance rollup — FSC edition. (RBL if package available; else your Apex rollup pattern; discuss recalcs and skew)
    10. Millions of financial accounts — a report times out. (LDV: selective filters, indexes/skinny-table name-drop, summary object updated by batch, archive strategy)
    11. Screen flow or LWC for the account-opening wizard? (flow for speed/admin ownership; LWC for UX control/complex validation — argue both, pick one, justify)
    12. A managed-package trigger conflicts with yours. (no order guarantee; make yours idempotent/defensive, bypass switches, log and isolate)
    13. Sandbox-to-prod deploy failed on missing coverage. (test levels, the fix, and the prevention: CI validate on PR)
    14. Batch job overlaps its previous run. (check AsyncApexJob for running instance before executeBatch; or chain from finish)
    15. PII fields must be hidden from most users but used in code. (FLS + permission sets; without sharing service with stripInaccessible on the way out; audit)

    Skeleton for every scenario: clarify assumptions (volume? real-time?) → name the pattern → walk the happy path → failure handling → limits check → "in my capstone I did the smaller version of this."

  • The 8 stories (90 seconds each)
    1. Hardest bug of the month (KYC retry? LMS wiring?) — Situation, Task, Action, Result with a number
    2. Learned something fast under pressure (this entire month — frame one week of it)
    3. A time you were wrong and corrected course (a design you reversed — flow→apex or vice versa)
    4. Conflict/disagreement handled (from any past job/college — genuine beats impressive)
    5. Deadline you protected (Day-25 exam pipeline while interviews ran)
    6. Feedback you acted on (your comm recordings improving — meta but true and memorable)
    7. Something you're proud of (the capstone demo)
    8. Failure that taught you (pick real; rehearse the recovery half harder than the failure half)
DAY27

Mock marathon — three rounds, scored

Mocks 3.5h · Review 1.5h · Job 45m · Comm built-in
0/6

Simulate the real gauntlet: technical, scenario, HR — back to back, scored against a rubric, weakest answers rebuilt same day.

DAY28

Clouds + FSC final polish · JS brush-up

Drill 4h · Theory 1.5h · Job 45m · Comm 45m
0/6

Everything cloud-specific gets one final varnish: models redrawn, FSC scenarios rehearsed, JS sharp for LWC rounds.

  • The 8 FSC scenarios
    1. Design household total-deposits with and without RBL
    2. One person in two households — model it (ACR roles)
    3. Advisor leaves — reassign book of business (ownership + shares + open items)
    4. Only the deal team may see interaction notes (CDS / participant roles)
    5. Auto-create onboarding Action Plan when household forms (your Day-19 flow, narrated)
    6. KYC flag from an external bureau must gate account opening (your capstone!)
    7. Migrate 500k legacy financial accounts in (Bulk API + upsert External Id + batch validation)
    8. Person account vs individual model — a bank asks which; advise
DAY29

Full dress rehearsal + logistics kit

Mock 2h · Prep 2h · Job 45m · Comm 1h
0/6

One continuous end-to-end interview simulation, then everything around the interview gets professionalized.

  • The kit
    • Questions to ask them (pick 3): "What does the first 90 days look like for this role?" · "How is work split between config and code on your projects?" · "What's the team's deployment pipeline?" · "What would make someone exceptional in this seat by month six?"
    • Salary: research your city's band for 1–3 yr SF devs (peers, Glassdoor, recruiter asks); answer as a band + "flexible for the right learning curve." Never a single number first.
    • Notice period / availability: one honest sentence, rehearsed.
    • Video setup: camera at eye level, light in front, phone on silent, capstone org logged in + demo tab ready in background for the "can you show me?" moment.
    • The gap script, final version — say it once more to the mirror: JD asks 5 years → "here's the stack, here's my build on exactly that stack, test my depth anywhere."
DAY30

Launch day — surge, publish, sustain

Job 3h · Publish 1.5h · Plan 1h
0/6

The sprint ends; the campaign continues. Today maximizes surface area while everything is sharp.

  • The maintenance loop (until offer)
    • Daily (2h): 45m random-fire drills · 45m job hunt · 30m comm rep (one explainer or one answer re-record).
    • Alternate days: one full mock OR one capstone enhancement (ideas: Agentforce experiment, batch reconciliation report, JS Developer cert prep — pick from your gap list).
    • After every real interview: same-day debrief — every question you fumbled becomes a flashcard + a re-recorded answer within 24h. Real interviews are your best training data now.
    • Weekly: re-read this dashboard's Reference tab once, cover to cover — 30 minutes keeps everything warm.
SPRINT EXIT: 3 public repos + demo video · PD1 done or booked · 200+ applications in flight · scenario + STAR banks rehearsed · communication visibly transformed on video. Offers follow pipelines — keep the loop running.

Reference — everything in one place

The tables you memorize, the question bank you drill from, the scripts you send, and the capstone checklist. Days link here; before every interview, skim this tab top to bottom (≈30 min).

Governor limits (memorize this table)

Limit (per transaction)SynchronousAsynchronous
SOQL queries100200
SOQL rows retrieved50,00050,000
SOSL queries2020
DML statements150150
DML rows10,00010,000
CPU time10,000 ms60,000 ms
Heap size6 MB12 MB
Callouts (HTTP)100 · 120s cumulative timeout100 · 120s
@future calls50not from future/batch
System.enqueueJob501 per async execution
Emails (send methods)1010
Platform capValue
Batch scope sizedefault 200 · max 2,000
Batch start() QueryLocator50M rows
Concurrent batch jobs / flex queue5 running · 100 holding
Scheduled jobs100
Queueable chain depth (Dev/Trial orgs)5 (prod: unlimited)
Interview delivery: don't just recite — attach a consequence. "100 SOQL sync, which is why a query in a 200-record trigger loop dies: 200 > 100."

Order of execution (full)

  1. Record loaded from DB (or initialized for insert/upsert)
  2. New field values loaded; system validation (UI saves: layout required fields, formats, max lengths; API adds FK checks)
  3. Record-triggered flows — before save
  4. All before triggers
  5. System validation re-runs + custom validation rules
  6. Duplicate rules (block → the save aborts here)
  7. Record saved to the database — Id exists, not committed
  8. All after triggers
  9. Assignment rules
  10. Auto-response rules
  11. Workflow rules — a field update re-runs system validation + before/after update triggers once more (no recursion beyond that)
  12. Escalation rules
  13. Flow automations: processes and record-triggered flows — after save
  14. Entitlement rules
  15. Roll-up summary fields recalc on parent (parent's own save cycle runs) — then grandparent
  16. Criteria-based sharing evaluation
  17. COMMIT
  18. Post-commit logic: emails send, async jobs (@future/queueable/batch) actually enqueue, outbound messages

Checked against the current Apex Developer Guide? Skim the official "Triggers and Order of Execution" page once during Week 1 — steps get refined between releases, and saying "as of the current release" earns points.

Async Apex — the comparison table

@futureQueueableBatchSchedulable
Parameters / stateprimitives only, static methodany state via constructorany state; Stateful persists across chunksnone (thin shell)
Returns job Idnoyesyesyes (CronTrigger)
Chainingnoyes — 1 child per executefrom finish()launches others
Calloutswith (callout=true)with AllowsCalloutswith AllowsCalloutsvia what it launches
Data volumesmallsmall–mediumup to 50M rowsn/a
ExtrasTransaction Finalizersper-chunk limit resetCRON timing
Use formixed DML, tiny fire-and-forgetdefault choice: callouts, chained workmass updates, nightly jobstiming wrapper

Integration pattern picker

NeedReach for
Notify external system, fire-and-forgetPlatform Event (+ CometD/Pub-Sub subscriber)
Replicate record changes outwardChange Data Capture
External system creates/updates SF dataStandard REST/Composite API; custom Apex REST for shaped payloads
SF calls external API in real timeCallout via Named Credential (async if from a trigger path)
Millions of rows in/outBulk API 2.0 (+ upsert on External Id)
Declarative SOAP ping with retriesOutbound Message (workflow-era but still asked)
Nightly reconciliationBatch Apex + Schedulable

Trigger context availability

EventnewnewMapoldoldMapCan edit records?
before insert✓ (no Id yet)
after insert✓ read-only
before update
after update✓ read-only
before delete— (old is read-only)
after delete
after undelete✓ read-only

LWC cheatsheet

DecoratorMeaningGotcha
@apipublic property or method (parent-settable)don't mutate inside the child; not ready in constructor
@wiredeclarative data (adapter or cacheable Apex)needs cacheable=true; reactive via '$param'
@trackdeep-track object/array mutation (legacy)plain fields already reactive on reassignment — prefer reassign
Hook (in order)Use forTrap
constructorinit only@api not set yet; no DOM
connectedCallbackfetch, subscribe (LMS), intervalsruns again if re-inserted
renderedCallbackDOM-dependent workre-runs every render — guard it
disconnectedCallbackcleanup: unsubscribe, clearIntervalskipping = leaks (interview probe)
errorCallbackboundary for child errorsonly child errors, not own
Talk between…Use
Parent → child@api properties · @api methods via querySelector
Child → parentCustomEvent (lowercase name, detail payload, onxxx in markup)
Unrelated / cross-region (LWC·Aura·VF)Lightning Message Service (channel + publish/subscribe)
Single-record CRUD, zero ApexLDS: record-form family + uiRecordApi
Lists / complex reads@wire cacheable Apex; refreshApex to refetch
On-demand + DMLImperative Apex + AuraHandledException

The three data models (redraw weekly)

Sales — product chain
Product2 ──< PricebookEntry >── Pricebook2 (Standard first, then custom)
Opportunity(Pricebook2Id) ──< OpportunityLineItem → references PricebookEntry
Lead ──convert──> Account + Contact + Opportunity   Campaign ──< CampaignMember
Service — SLA engine
Account ──< Entitlement ── uses ──> Entitlement Process ──< Milestones
Case(EntitlementId) ──< CaseMilestone (TargetDate | CompletionDate)
Channels → Queues → Assignment rules → agents (Omni) · Escalation = time-based
FSC — household
Household (Account, RT=Household) ──< AccountContactRelation (roles) >── Person Accounts
Person Account ──< FinServ__FinancialAccount__c ──< FinServ__FinancialAccountRole__c
RBL configs roll balances → client & household totals · ARC renders the graph

FSC object glossary

ObjectWhat it is
FinServ__FinancialAccount__cbank account / loan / card / investment — the core object
FinServ__FinancialAccountRole__cjoint owners, beneficiaries, trustees on a financial account
AccountContactRelation (std)household/business membership with roles — FSC's connective tissue
FinServ__ReciprocalRole__cpaired role titles (Power of Attorney ↔ Grantor)
FinServ__AccountAccountRelation__c / ContactContactRelation__centity-to-entity and person-to-person relationships
FinServ__AssetsAndLiabilities__cheld-away assets and debts (net-worth picture)
FinServ__FinancialGoal__cclient goals (retirement, education)
FinServ__FinancialHolding__c + FinServ__Securities__cwealth positions in instruments
FinServ__RollupByLookupConfig__cthe RBL rollup definitions (Total Bank Deposits…)
ActionPlanTemplate / ActionPlanreusable task+document checklists (onboarding, KYC)
PersonLifeEventlife-event timeline entries (marriage, home, retirement)
InsurancePolicy · InsurancePolicyParticipant · Claiminsurance vertical
ResidentialLoanApplicationmortgage vertical intake

Master question bank (~90)

Core Apex

  • List vs Set vs Map + trigger use cases
  • Why governor limits exist
  • Static variable lifetime
  • sObject vs concrete types; dynamic get/put
  • Safe navigation ?. and ??
  • SOQL child↔parent both directions
  • AggregateResult handling
  • SOSL vs SOQL
  • Selective queries + indexes
  • SOQL injection prevention
  • insert vs Database.insert
  • upsert + External Id
  • Savepoints/rollback
  • Mixed DML + fix
  • Custom exceptions; uncatchable LimitException

Triggers

  • Order of execution (recite)
  • before vs after use cases
  • Context variable availability by event
  • Bulkification pattern narrated
  • One-trigger-per-object rationale
  • Recursion guards (Set<Id> vs boolean)
  • addError vs throw
  • Workflow re-fire behavior
  • Trigger bypass strategies
  • Why no callouts in triggers

Async

  • future vs Queueable (full)
  • Why primitives-only in future
  • Chaining + limits + test guard
  • Transaction Finalizers
  • Batch start/execute/finish
  • QueryLocator vs Iterable
  • Database.Stateful
  • Scope sizing for callout batches
  • CRON strings (write one live)
  • Monitoring: AsyncApexJob, flex queue

Integration

  • Callout end-to-end narration
  • Named vs External Credentials
  • OAuth flows: auth code / client credentials / JWT
  • Callout-after-DML error
  • Typed vs untyped JSON
  • Reserved-word JSON keys
  • Expose Apex REST + auth story
  • HttpCalloutMock patterns
  • Platform Events vs CDC vs Outbound Message
  • Publish immediately vs after commit
  • Bulk API when/why
  • Idempotency design

LWC

  • Bundle anatomy + naming
  • Reactivity rules; when @track
  • Lifecycle hooks in order + traps
  • wire vs imperative (full)
  • cacheable=true meaning
  • refreshApex mechanics
  • AuraHandledException why
  • 3 communication routes + LMS details
  • LDS vs Apex decision
  • key in for:each
  • Debounce implementation
  • this + arrow functions; promises; async/await

Declarative & security

  • Flow types + before vs after save
  • Flow vs Apex framework
  • @InvocableMethod rules
  • Sharing ladder OWD→manual
  • with/without/inherited sharing
  • USER_MODE / stripInaccessible
  • Apex managed sharing + rowCause
  • Restriction rules
  • Profiles vs permission sets
  • CMDT vs Custom Settings

Sales Cloud

  • Lead convert mechanics + what fires
  • Validation on convert setting
  • Assignment rules from Apex (DMLOptions)
  • Product/pricebook chain + rules
  • Standard pricebook in tests
  • Opportunity Amount with products
  • Person accounts + code implications
  • Campaigns/OCR basics

Service Cloud

  • Entitlement model drawn
  • Milestone auto-complete pattern
  • Queues vs assignment vs Omni
  • Email-to-Case flavors
  • Escalation rules
  • Support processes/record types
  • Knowledge basics
  • Console + LWC note

FSC

  • Household model whiteboard
  • ACR vs Contact.AccountId
  • Person Account vs Individual model
  • RBL vs rollup summary
  • 6+ objects with jobs
  • Action Plans use case
  • Compliant Data Sharing
  • Life Events / Interaction Summaries
  • Managed-package dev implications
  • Insurance/mortgage object name-drops

DevOps + HR

  • Your deployment process (scripted)
  • Sandbox types
  • Change sets vs CLI vs packages
  • CI validation on PR
  • Intro (2 min)
  • Project pitch (3 min)
  • The 5-years question
  • Salary band answer
  • Why Salesforce / why us
  • Questions you ask them

Scripts & templates

Resume bullet formula

Built/Automated/Integrated + what + with which tech + outcome/scale. Examples: "Built an event-driven client-onboarding pipeline (Apex Queueable + Platform Events + REST callouts) that takes a web lead to a KYC-verified client with SLA-tracked cases — 85%+ test coverage." · "Designed a 3-step LWC account-opening wizard with imperative Apex, error surfacing, and navigation — deployed via sf CLI from Git with CI validation."

LinkedIn / Naukri headline

"Salesforce Developer | Apex · LWC · Integrations | Sales & Service Cloud · FSC data model | PD1 | Building in public: [repo link]"

Referral DM (≈70 words, personalize the first line)

"Hi [Name] — I saw your post about [specific thing]. I'm a Salesforce developer focused on Sales/Service Cloud and FSC; I recently built an end-to-end bank-onboarding app (Apex, LWC, Platform Events — 3-min demo: [link]). [Company] is hiring for [role]; would you be open to referring me, or a 15-min chat about what the team looks for? Happy to send my resume. Thanks either way!"

Follow-up nudge (7–10 days silent)

"Hi [Name] — following up on my application for [role] ([date]). Since applying I've [passed PD1 / shipped X in my capstone: link]. Still very interested — happy to do a technical screen anytime this week. Thanks!"

The 5-years answer (calm, once, then stop talking)

"Fair question. The 5 years is a proxy for being productive on Sales, Service and FSC from week one. Here's what I'd point to instead: an end-to-end build on exactly that stack — intake to KYC to SLA-tracked service, with tests and CI. I'd rather you probe my depth anywhere in it than count calendar years. Where would you like to start?"

Salary

"Based on the market for this stack I'm looking at [band]; for the right team and learning curve I'm flexible. What's the band for this role?" (Research the band from peers/Glassdoor first; never anchor with a single number.)

PD1 announcement post

"Certified: Salesforce Platform Developer I ✅ — 25 days into a self-designed sprint: 3 repos, one end-to-end banking app on the FSC data model (demo below), and a lot of governor limits memorized. Open to Salesforce Developer roles — Sales/Service Cloud + FSC. [demo link] [repo link]"

Capstone launch post (Day 30)

"30 days ago I committed to going deep on Salesforce development. Result: a working bank client-onboarding app — web/REST intake → dedupe → KYC via async callouts → Platform Events → SLA-tracked onboarding cases → LWC wizard + financial summary. Demo (3 min): [link]. Repo: [link]. Stack: Apex (Queueable/Batch/Finalizers), LWC, Platform Events, Named Credentials, FSC data model. Feedback welcome — and I'm interviewing!"

Communication protocol (daily, non-negotiable)

  • Feynman reps (20m): three 2-min phone videos on today's topics, as if to an interviewer. Watch. Re-record the worst. The improvement curve is steep and visible by Week 3.
  • One skeleton for every technical answer: what it is → when to use → limits/gotchas → "in my project I…". Drill until automatic under stress.
  • Read aloud (15m): one Salesforce blog/doc page daily — fluency with the exact vocabulary you'll need.
  • Mocks: 3×/week from Week 2 (friend, community peer, or Claude — technical, scenario, or HR round with feedback).
  • The rule: slow is smooth. A 2-second pause reads as thoughtful; filler words read as panic. When stuck: "Let me structure that" — then use the skeleton.

Capstone acceptance checklist

2026 buzz card — one confident sentence each

  • Agentforce: Salesforce's platform for autonomous AI agents that act on CRM data (topics, actions, guardrails) — "I've explored building agent actions on Apex invocables."
  • Prompt Builder: declarative prompt templates grounded in record data, callable from flows/Apex.
  • Data Cloud: the customer-data platform unifying external + CRM data into profiles; increasingly what "real-time" projects sit on.
  • Einstein: umbrella for platform AI (predictions, GPT features embedded in clouds).
  • DevOps Center: Salesforce's free Git-based release-management UI — the modern change-set replacement.
  • Hyperforce: Salesforce's public-cloud infrastructure re-platform (data residency, scale).
You're not claiming expertise — you're showing you're current. One sentence each, delivered without hesitation, is all interviews check.

Resources (the complete list)

  • Trailhead badges this plan uses (in order): Apex Basics & Database · Apex Triggers · Apex Testing · Asynchronous Apex · Apex Integration Services · Platform Events Basics · Lightning Web Components Basics · Lightning Web Components and Salesforce Data · Build Flows with Flow Builder · Data Security · Leads & Opportunities for Lightning Experience · Service Cloud for Lightning Experience · Financial Services Cloud Basics · trailmix "Prepare for Your Salesforce Platform Developer I Credential" — all at trailhead.salesforce.com
  • Docs you'll actually open: Apex Developer Guide + LWC Dev Guide + Component Library at developer.salesforce.com/docs · FSC Object Reference (search "Financial Services Cloud developer guide")
  • Free video: Apex Hours (apexhours.com + YouTube) — interview-oriented, community-run
  • Tools: Workbench (workbench.developerforce.com) for REST/SOQL poking · JSON2Apex (search it) for wrapper generation · Salesforce Inspector Reloaded browser extension
  • PD1: exam guide on Trailhead ("Platform Developer I exam guide") · question bank: Focus on Force (~$20) · booking: Webassessor
  • FSC trial: salesforce.com → "Financial Services Cloud trial" (30 days, person accounts pre-enabled)
  • Community: Trailblazer Community groups (your city) · Salesforce Saturdays · LinkedIn #SalesforceDevs