Module 9 — Admin Basics a Developer Must Know
Interview weight: 5–10% (rarely the headline question, but the "gotcha" follow-up in almost every loop — the one-liner traps separate 3-4yr candidates who only know Apex from those who know the platform) · Estimated time: 3–4 sessions (~60 min each) Target: By the end, you can answer "Profile vs Permission Set vs Role vs Record Type" without conflating access control and UI/process control, explain exactly when validation rules and approval processes fire (and don't), pick Custom Metadata vs Custom Settings correctly for a deployable feature flag, choose the right data tool for a given volume, and explain why a dashboard "can't be scheduled" — all in 2 minutes closed notes.
M0 — THE MAP (read this first, 5–10 min)
The one idea everything hangs on: ADMIN CLICKS ARE PRODUCTION CODE WITHOUT A CODE REVIEW
Every concept in this module — profiles vs permission sets, record types, validation rule scope, approval locking, CMT vs Custom Settings, data-loading tool choice, report/dashboard limits — is a consequence of one realization:
Every "just a config change" in Setup is a deployable, org-wide, unreviewed piece of logic that runs in production the instant you save it. A developer who thinks "that's admin stuff, not my problem" ships bugs that live in Setup instead of in code — and are ten times harder to find because no one looks at Setup in a code review.
Think of the org as a building with four control systems, each answering a different question, and the classic interview trap is candidates who think one system answers all four:
- Who is allowed in the building at all = Profiles (mandatory baseline, one per user) and Permission Sets (additive-only extensions).
- Who can see which office = Role Hierarchy (record visibility flows up the chain) and sharing (public groups extend visibility; OWD sets the floor).
- Who does the work / whose desk it lands on = Queues (ownership + routing) — NOT to be confused with Public Groups (sharing only). The #1 one-liner trap: "Groups are for sharing, queues are for ownership."
- What the office looks like and what forms are on the desk = Record Types (layouts, picklist values, business processes) — this system controls zero access. It is furniture, not a lock.
- The rulebook enforced at the door = Validation Rules (fire on create/update — never on delete) and Approval Processes (multi-step sign-off with record locking).
- The mail room = Assignment & Escalation Rules — auto-routes Leads/Cases to queues/users, escalates on SLA breach.
- The warehouse and the loading dock = Custom Metadata Types (deployable, packaged, cached, zero-SOQL-cost config — the feature-flag tool) vs Custom Settings (runtime, per-user/profile/org overrides, not a deployment artifact).
- The forklifts = data tools — Data Import Wizard (small, point-and-click), Data Loader (up to 5M records, CLI/scheduled), Workbench, Salesforce Inspector (quick browser inspection).
- The dashboards on the wall = Reports (Tabular/Summary/Matrix/Joined — only Summary and Matrix can feed a dashboard) and Dashboards (dynamic dashboards cannot be scheduled).
Why this map matters (the bridge): Interviewers use this module as a filter question, not a deep-dive: a candidate who says "Record Types control visibility" or "just use a Custom Setting for the feature flag" reveals they've never actually administered an org — a red flag for anyone who'll be asked to review a Flow, design a sharing model, or debug "it works for me but not the team." Every incident below is a real production war story where a developer (not just an admin) got burned by treating a config click as harmless. The fix is always the same four disciplines:
- Know which control system answers which question — access, visibility, ownership, or presentation — and never let one masquerade as another.
- Know exactly when automation fires (create/update vs delete; before lock vs after lock) — "it should have fired" is not a debugging strategy.
- Pick the deployable tool for anything that ships with code (CMT, not Custom Settings) and the right-volume tool for anything that loads data (Loader, not Import Wizard, past a few thousand rows).
- Treat every Setup change as production code: it needs a design decision, a test plan, and a rollback story.
By the end of this module, "knowing it" looks like this: given any one of the 9 problems below, you can (a) name the mechanism that failed, (b) explain the fix on a whiteboard, (c) describe the corrected design from memory, and (d) say which interview question it maps to.
The incidents (choose your own adventure — recommended order)
| # | Incident | The villain mechanism |
|---|---|---|
| 1 | The Layout That Thought It Was a Lock | Record Types confused with access control |
| 2 | The Delete That Slipped Past the Guard | Validation rules don't fire on delete |
| 3 | The Sharing Rule That Shared With No One | Public Group vs Queue confusion |
| 4 | The Approval That Froze the Trigger | Record locking during approval vs automation order |
| 5 | The Feature Flag That Couldn't Deploy | Custom Metadata Types vs Custom Settings |
| 6 | The Import Wizard That Choked at Midnight | Data Import Wizard vs Data Loader vs volume |
| 7 | The Dashboard the VP Wanted at 8 AM Sharp | Report types feeding dashboards + the scheduling wall |
| 8 | The Escalation Rule That Escalated Nothing | Assignment/escalation rule misconfiguration + SLA breach |
| 9 | 🏆 Capstone — The Org Where Admin and Dev Never Talked | Multi-system incident report |
Protocol reminder (from file 00): attempt in writing FIRST (≥2 hypotheses + 2 solution attempts), hard 45-min cap, hint ladder, then reveal, then REDO, then retrieval drill. The sealed answer sheet lives in
09b_Topic09_Admin_Basics_Answer_Sheet.md. You are expected to fail. The failure is the task.
INCIDENT 1 — THE LAYOUT THAT THOUGHT IT WAS A LOCK
STAKES
Legal flags a compliance gap two days before an audit: "Sensitive_Notes__c should only be visible to the Legal team on Litigation-type Cases." The admin's fix, shipped that afternoon: a new Record Type "Litigation Case" with a page layout that hides Sensitive_Notes__c from everyone except the Legal page layout assignment. The admin demos it — support reps open a Litigation case, don't see the field, ships it as "done, compliant." Three weeks later, a support rep pulls the same case in a list view with Sensitive_Notes__c as a column, and in a report. Both show the field, in full, to everyone. The auditor's first question: "who can see this field?"
THE INCIDENT
New Record Type: "Litigation Case" (Case object)
- Page Layout assignment: Legal profile → "Litigation Layout" (field included)
All other profiles → "Standard Layout" (field removed from layout)
- Field-Level Security on Sensitive_Notes__c: unchanged — still "Visible" for every profile
- No sharing rule, no OWD change, no field permission change
Result: Support rep's Case detail page — field not visible (layout hides it).
Support rep's list view / report / API query — field fully visible.THE PROBLEM
The admin believed the field was "hidden from support." Name exactly what a Record Type controls (three things) and what it never controls, explain precisely why the field still leaked through list views/reports/API, and design the actual compliant fix using the correct mechanisms.
Write: (1) what Record Types control vs never control, (2) why layout-hiding ≠ security, (3) the corrected design.
HINT LADDER
- Hint 1 (the avenue): (1) Record Types control picklist values, page layout assignment, and business process (support process / sales process) per record — nothing about who can read or write a field or record. (2) Page layout hiding is a UI convenience only — every other read path (list views, reports, Global Search, SOQL/API, related lists) bypasses the layout entirely. (3) The only mechanisms that actually restrict field visibility are Field-Level Security (FLS) via profile/permission set, and the only mechanisms that restrict record visibility are OWD + sharing rules + role hierarchy.
- Hint 2 (the mechanism): (1) A Record Type is a routing/presentation object:
RecordTypeIdpicks which picklist value set, which layout, and which support/sales process apply to a record — it is metadata about how the record looks and behaves, not an access-control object. (2) Removing a field from a layout only removes it from that one rendered page; the field's underlying permission (FLS) is untouched, so it still appears anywhere the platform doesn't consult the layout — reports, list view columns, Kanban, Chatter feed field tracking, SOQL, REST/Bulk API, Data Loader exports. (3) Compliant fix: setSensitive_Notes__cFLS to Read/Write only for the Legal permission set/profile (and "None" for everyone else) — this is enforced everywhere, not just the one page. - Hint 3 (the skeleton): Record Types own: picklist values, layout assignment, business process. Record Types never own: object/field/record access. Fix: (a) FLS on
Sensitive_Notes__c→ visible only to Legal profile/permission set; (b) optionally restrict the record itself (private OWD on a Litigation-flagged subset via a sharing rule/Apex sharing) if the whole record — not just the field — must be restricted; (c) keep the Record Type for what it's actually good at: layout + process, and stop treating it as a security control.
THE REVEAL — POSTMORTEM
What actually happened (real class of incidents — "we hid it from the layout" compliance near-miss; one of the most common audit findings in Salesforce orgs):
What a Record Type actually controls (memorize — the interview answer):
- Which picklist values are available for that record (e.g., Case Type shows different values for "Litigation" vs "Standard" Cases).
- Which page layout is assigned, per profile, for records of that type.
- Which business process applies (Support Process, Sales Process, Lead Process) — the guided path/stage set.
What it never controls: object permissions (CRUD), field-level security, record-level sharing, or anything reachable outside the one rendered detail page. Hiding a field from a layout is exactly as secure as removing a folder icon from a desktop while the file itself stays world-readable — the file is still there for anyone who opens it a different way.
Why the leak was inevitable: every other UI surface — list views, related lists, Kanban, reports, dashboards, Global Search, Chatter — renders fields based on FLS, not on the record's layout assignment. The Bulk API, REST API, SOQL, and Data Loader exports never consult page layouts at all. A layout-only "fix" secures exactly one page and leaves five other doors wide open. This is the textbook version of the Module 5 (Security & Sharing) trap: "FLS vs page layout hiding: layout hiding still exposes in reports/search/API; FLS hides everywhere."
The compliant fix:
- Field-Level Security on
Sensitive_Notes__c: "None" for every profile/permission set except the Legal permission set (Read/Write or Read Only as required). This is enforced platform-wide — layouts, reports, list views, API, everything. - If the entire record (not just the field) must be restricted to Legal, that's a sharing problem — OWD Private on Case (or a criteria-based sharing rule) restricting Litigation-type records to a Legal public group, not a Record-Type problem.
- Keep the Record Type for its real job: the Litigation layout can still exist and be assigned — it's a nice UX touch (Legal sees a purpose-built layout) — but it is never the security boundary. Security is FLS + sharing; presentation is Record Type + layout.
Why the "obvious fixes" failed (the contrast):
- "Remove it from the layout" → secures one page; leaks via every other surface (reports, list views, API).
- "Create a separate Record Type per audience" → still a presentation control; without FLS/sharing changes, the underlying data is equally exposed regardless of type.
- "It demoed fine" → the demo only exercised the one page the admin changed; the auditor's first move — a report or list view — was never tested.
KNOWLEDGE EXTRACTION (interview-ready)
- "What do Record Types control?" → Picklist values, page layout assignment, and business process per record — nothing about access.
- "Does hiding a field from a layout secure it?" → No. Layout hiding is UI-only; it doesn't touch reports, list views, Global Search, or API/SOQL access. Only FLS hides a field everywhere.
- "Profiles vs Permission Sets vs Roles vs Record Types — the one-liner?" → "Profiles handle permissions (mandatory baseline), Permission Sets add more, Roles handle record visibility via hierarchy, Record Types handle picklist/layout/process differentiation — not visibility at all."
- "How would you actually restrict a sensitive field to one team?" → FLS on the field (profile/permission set), and if the whole record needs restriction, OWD + sharing rules — never a Record Type or layout alone.
THE REDO
From memory: the three things a Record Type controls, the one thing it never controls, and the FLS + sharing fix.
RETRIEVAL DRILL
- Name the three things a Record Type controls.
- Does a Record Type ever restrict who can read a field or record?
- Why does layout-hiding fail against reports, list views, and the API?
- What mechanism hides a field everywhere, not just one layout?
- What mechanism restricts an entire record, not just a field?
INTERVIEW MAPPING
The "Profiles vs Permission Sets vs Roles vs Record Types" one-liner trap is a documented interviewer favorite specifically because junior candidates conflate presentation control with access control. This incident is its production form — naming the leak paths (reports/list views/API) is the senior-level answer.
INCIDENT 2 — THE DELETE THAT SLIPPED PAST THE GUARD
STAKES
Finance built a validation rule on Opportunity: "block any change if Stage = Closed Won and Amount doesn't match the signed contract total" — a control meant to stop revenue tampering. Compliance loved it. Then, during quarter-end cleanup, a sales ops admin bulk-deletes 40 duplicate Closed Won Opportunities via a list view — the exact kind of record the validation rule was supposed to lock down. No error. No block. Finance finds out three weeks later during revenue reconciliation: signed, closed-won revenue simply vanished, and the validation rule that was supposed to be the safety net "did nothing." The developer who built it insists "it's tested, it definitely fires."
THE INCIDENT
Validation Rule "Lock_Closed_Won_Revenue":
Error Condition Formula:
AND(
ISPICKVAL(Stage, "Closed Won"),
Amount <> Contract_Total__c
)
Error Message: "Cannot modify Amount on a signed Closed Won Opportunity."
Sales ops action: select 40 Closed Won Opportunities in a list view → Delete.
Result: all 40 deleted. Zero validation errors. Zero blocks.THE PROBLEM
Why did a validation rule everyone tested and trusted let 40 records disappear with no error at all? State precisely which DML operations trigger validation rules and which don't, then design the actual control that should have existed to prevent (or at least catch) this deletion.
Write: (1) exactly which operations fire validation rules, (2) why delete is different, (3) the real control design.
HINT LADDER
- Hint 1 (the avenue): (1) Validation rules fire on create and update only — never on delete (and not on undelete either). (2) "It's tested, it fires" was true — for updates. No one tested delete because no one thought to. (3) The real control for "don't let this record be destroyed" is not a validation rule at all — it's a permission (object-level Delete access) and/or record-level protection (e.g., a flag + Apex trigger, or simply revoking Delete on the profile for Closed records).
- Hint 2 (the mechanism): (1) Validation rules are evaluated as part of the save pipeline for insert/update (and upsert's insert/update halves) — they have no hook into the delete pipeline at all; a
DELETEDML statement (or a list-view bulk delete, which is DML underneath) never evaluates a validation rule, full stop. (2) The team's mental model — "the rule protects Closed Won records" — was only ever true for edits to Amount; they never asked "what stops someone from deleting the record entirely," a different question with a different mechanism. (3) Real controls for "protect from deletion": revoke the Delete object permission on the profile for users who shouldn't delete Opportunities (or scope by record status via an Apex trigger onbefore delete, which validation rules cannot do), and/or use Record-Triggered Flow / Apex with abefore deletecheck — the one place a "delete-blocking validation" actually has to live. - Hint 3 (the skeleton): Fact: validation rules = create/update only. Fix: (a) an Apex trigger (
before delete) or an equivalent that checksTrigger.oldMapforStageName == 'Closed Won'and adds an error — this is the only declarative-adjacent way to block a delete conditionally; (b) tighten object permission: remove "Delete" from the standard sales profile, grant it only via a permission set to ops leads; (c) for defense-in-depth, add a recycle-bin monitoring / audit trail (Field History or Shield Event Monitoring) so a slip is caught within hours, not weeks.
THE REVEAL — POSTMORTEM
What actually happened (real class of incidents — "the validation rule doesn't fire" delete-path surprise; a documented interview trap and a real quarter-end horror story):
The rule (memorize — asked cold in interviews): Validation rules evaluate on record create and update (including the relevant half of an upsert). They do not evaluate on delete or undelete. There is no configuration flag to change this — it's a platform behavior, full stop. A validation rule, no matter how airtight its formula, is invisible to a DELETE statement, a bulk list-view delete, a Data Loader delete job, or an API delete call.
Why the team never noticed: the rule was built, tested, and trusted against its actual job — blocking field edits on Closed Won records — and it did that job perfectly. Nobody asked the orthogonal question ("what stops someone from deleting the whole record?") because a validation rule feels like a general-purpose guard, and the team's mental model silently expanded its scope to "protects Closed Won Opportunities" instead of the narrower truth, "blocks specific field changes on create/update."
The real control design:
before deleteApex trigger (or the equivalent enforcement point) — the only mechanism that can conditionally intercept a delete: checkTrigger.oldMap.values()forStageName == 'Closed Won'(or any protected criteria) and call.addError(). This is genuinely a case where declarative tooling cannot express the rule — validation rules structurally cannot run on delete, so Apex (or, in newer orgs, a Flow's before-delete-adjacent patterns via record-triggered automation on delete context where available) is required.- Object permission discipline: remove Delete from the profile most sales reps and ops staff use; grant it via a scoped permission set only to the roles that legitimately need it. This is the cheapest, most durable fix — most "oops, deleted the wrong records" incidents are really "too many people had delete rights."
- Defense-in-depth / detection: Field History Tracking or Shield Event Monitoring on delete events, plus a scheduled reconciliation report (closed-won count vs. expected) so a gap surfaces in hours, not during quarter-end reconciliation.
Why the "obvious fixes" failed (the contrast):
- "Add another validation rule to block the delete" → structurally impossible; validation rules never see delete DML. This is the #1 trap answer.
- "The rule is tested, so it must be a bug" → it wasn't a bug; it was a correct rule solving the wrong problem (edit protection, not delete protection).
- "Just restore from the recycle bin" → works only within the 15-day recycle bin window and only reactively; the real fix is preventing the delete or catching it same-day, not recovering from it three weeks later.
KNOWLEDGE EXTRACTION (interview-ready)
- "Do validation rules fire on delete?" → No. Create and update only (and upsert's relevant half). Never delete, never undelete. This is the single most common validation-rule trap question.
- "How do you block a delete conditionally?" → Apex
before deletetrigger (.addError()onTrigger.old) — declarative tools cannot express delete-time conditions. Or restrict the Delete object permission entirely via profile/permission set. - "Can validation rules be scoped by profile?" → Yes —
$Profile.Name(or$Permission) in the formula lets a rule apply only to certain profiles, a common pattern for "admins can override, everyone else is blocked." - "What's the deeper lesson?" → A validation rule protects a field change, not a record's existence. Confusing the two is exactly the gap that let 40 signed deals vanish with zero errors.
THE REDO
From memory: which DML operations fire validation rules, why delete is structurally excluded, and the three-part real control (Apex before-delete, permission discipline, detection).
RETRIEVAL DRILL
- Which two DML operations do validation rules fire on?
- Do validation rules fire on delete or undelete?
- How do you conditionally block a delete if not with a validation rule?
- What global variable lets a validation rule apply only to certain profiles?
- What's the cheapest, most durable fix for "too many people can delete records"?
INTERVIEW MAPPING
"Do validation rules fire on delete?" is a scripted trap question in nearly every admin-adjacent developer interview — the wrong answer ("yes, if I write the formula right") instantly signals someone who has never actually tested the boundary.
INCIDENT 3 — THE SHARING RULE THAT SHARED WITH NO ONE
STAKES
A new "VIP Escalations" team is formed to handle high-value Cases. The admin is told: "make sure the VIP team can see and route all VIP-flagged Cases." The admin creates a Queue named "VIP Escalations," adds the five team members as queue members, and writes a criteria-based sharing rule: "VIP-flagged Cases → share with VIP Escalations." Go-live Monday. Tuesday: VIP Cases are being auto-assigned to the queue correctly — but when the five team members open the case list, they see nothing. The Cases are "in the queue" but invisible to the very team that's supposed to work them. The admin insists the sharing rule is configured exactly as designed.
THE INCIDENT
Queue: "VIP Escalations" — Members: 5 users (Alice, Bob, Carol, Dan, Erin)
Case Assignment Rule: IF VIP__c = true → Owner = Queue "VIP Escalations" ✅ working, cases route correctly
Sharing Rule: "Share VIP Cases"
Criteria: VIP__c = true
Share with: Public Group "VIP Escalations" ← does not exist; admin picked "VIP Escalations" thinking
it meant the queue, but the sharing rule's "share with"
picklist only lists Roles, Public Groups, and Territories —
NOT Queues.
Result: the sharing rule target the admin actually selected was a same-named but EMPTY public group
(auto-created placeholder, or simply misconfigured to a role), sharing VIP Cases with nobody real.THE PROBLEM
Cases are correctly owned by the queue, yet the queue's own members can't see them. Explain the mechanism gap (why a sharing rule can't "share with a queue" the way you'd expect), and design the fix that actually gets the VIP team visibility on their own queue's cases.
Write: (1) why queue ownership doesn't automatically grant queue members visibility, (2) the groups-vs-queues distinction precisely, (3) the corrected sharing design.
HINT LADDER
- Hint 1 (the avenue): (1) Owning a queue does not automatically grant its members record access the way owning a record personally would under most OWDs — visibility still runs through the standard model (OWD/role hierarchy/sharing rules), and queue membership isn't itself a sharing mechanism. (2) Sharing rules share with Roles, Public Groups, Territories, or "Roles and Subordinates" — a Queue is not a valid sharing-rule target at all. (3) The fix: create a Public Group containing the same 5 users (or containing the queue, in orgs/objects where that's supported) and share with that group — queues are for ownership/routing, groups are for sharing.
- Hint 2 (the mechanism): (1) If Case OWD is Private (or Public Read Only) and a Case is owned by a queue, the members of that queue get access to the record because queue ownership does confer access to queue members on most objects — but this incident's twist is that the admin's sharing rule was pointed at a nonexistent/empty "VIP Escalations" public group (a naming collision, not real membership), meaning the actual access path was never verified, and any assumption that "the sharing rule is doing the work" was false — the queue-ownership access and the broken sharing rule were two unrelated, unverified paths. (2) The deeper, more common version of this bug: teams try to share records owned by users (not the queue) with what they think is the queue's group, but a Queue and a Public Group are different metadata types — you cannot select a Queue in a sharing rule's "share with" picklist, only its Public Group equivalent (if one was deliberately created to mirror membership). (3) Correct model: Queues own records and route work; Public Groups are share targets. If you need "the VIP team" to see records regardless of literal ownership, build a Public Group with the same 5 members and reference that group everywhere sharing is configured — independent of whatever the queue is named.
- Hint 3 (the skeleton): Groups are for sharing, queues are for ownership — the two systems are not the same object and are not interchangeable in the UI. Fix: (a) verify actual Case OWD and confirm queue-owned records already grant queue-member access (recheck instead of assuming); (b) create a real Public Group ("VIP_Escalations_Team") with the 5 named members; (c) point the sharing rule (or any additional cross-object sharing need) at that Public Group, never at the queue's name; (d) document that queue membership and public group membership are maintained separately and can drift — add a review step to onboarding/offboarding.
THE REVEAL — POSTMORTEM
What actually happened (real class of incidents — the "queue vs group" naming trap; one of the most common early-career admin mistakes, and a favorite developer interview one-liner):
The core distinction (memorize — the exact interview phrasing): "Public groups are for sharing; queues are for ownership and routing." A Queue is a special kind of record owner — Cases/Leads (and other queue-enabled objects) can be owned by a queue, and its members get access via that ownership path (subject to OWD) so they can pick up and work the records. A Public Group is purely a sharing target — a named bundle of users/roles/other groups that sharing rules, manual sharing, and report/folder access can reference. They are different metadata objects. A sharing rule's "Share with" field only lists Roles, Public Groups, Territories, and Roles-and-Subordinates — a Queue never appears there, because a queue is not a sharing construct.
Why "the VIP team sees nothing" happened despite queue ownership working: the incident is a compound failure — the assignment rule correctly makes the queue the owner, which (depending on OWD) should already grant queue members access to those specific Cases without needing a sharing rule at all. But the admin, believing a sharing rule was also required (or needed for other, non-owner viewers — managers, other teams), pointed that rule at a same-named but non-existent/misconfigured target — because the UI let them type/select something that looked right but wasn't the queue. The lesson generalizes: never assume a name match is a functional match — "VIP Escalations" the queue and "VIP Escalations" the (missing) group are unrelated records, and Setup does not warn you when you reference the wrong one.
The corrected design:
- Recheck the OWD-driven access first — if Case OWD is Private/Public Read Only and Cases are queue-owned, queue members should already see their own queue's cases without a sharing rule; verify this before assuming more configuration is needed.
- Create a real Public Group (e.g.,
VIP_Escalations_Team) with the actual 5 users as explicit members, used anywhere broader visibility is needed (managers who aren't in the queue, dashboards, report folders, Chatter groups). - Never target a Queue name in a sharing rule — it's not a valid target; if the same set of people needs both ownership (queue) and visibility for non-owners (group), maintain both memberships deliberately, and note they can drift on onboarding/offboarding.
- The one-liner to say out loud in review: "Groups are for sharing, queues are for ownership — if I need a team to receive/route work, that's a queue; if I need a team (or people outside the owner) to see records they don't own, that's a public group."
Why the "obvious fixes" failed (the contrast):
- "The sharing rule says VIP Escalations, so it must be right" → name collision, not a functional reference; Setup never validates that the string you typed maps to the entity you intended.
- "Just add the queue to the sharing rule" → structurally impossible — queues don't appear in the sharing-rule target picklist.
- "Recreate the queue" → the queue was never the problem; the missing/misreferenced public group was.
KNOWLEDGE EXTRACTION (interview-ready)
- "Queues vs Public Groups — the one-liner?" → "Groups are for sharing, queues are for ownership." Queues own and route records to their members; public groups are the target sharing rules/manual sharing/reports reference.
- "Can a sharing rule share with a queue?" → No — the "Share with" field lists Roles, Public Groups, Territories, Roles-and-Subordinates. Queues are never a valid target.
- "Does queue ownership give members access automatically?" → Yes, subject to OWD — a queue is a valid record owner, and queue membership grants the same access a personal owner would get, without needing an extra sharing rule for the members themselves.
- "When do you actually need a public group in this scenario?" → When someone who is NOT a queue member (a manager, another team, a dashboard folder) also needs visibility into queue-owned records.
THE REDO
From memory: the groups-vs-queues one-liner, why the sharing rule's target was wrong, and the corrected design (verify OWD path, real public group, correct target).
RETRIEVAL DRILL
- State the "groups vs queues" one-liner cold.
- Can a Queue be selected as a sharing rule's "Share with" target?
- Does queue ownership by itself grant members access, and under what condition?
- What was the actual root cause of "the VIP team sees nothing"?
- When do you need a Public Group even though a Queue already exists for the same team?
INTERVIEW MAPPING
"Groups are for sharing, queues are for ownership" is cited directly in interviewer prep material as the exact phrase candidates should be able to produce unprompted — this incident is its full production scenario, including the realistic naming-collision root cause.
INCIDENT 4 — THE APPROVAL THAT FROZE THE TRIGGER
STAKES
A discount-approval process locks Opportunity once submitted, exactly as designed — Finance wanted the numbers frozen mid-review. Two days after launch, the integration team reports: the nightly Apex batch job that recalculates Opportunity.Forecast_Category__c is throwing FIELD_CUSTOM_VALIDATION_EXCEPTION-adjacent record-locked errors on dozens of Opportunities every night, and — worse — a Record-Triggered Flow that's supposed to notify the account team when Amount changes silently stops firing on any Opportunity currently in approval. Sales managers are furious: "the deal is stuck in a black hole for two weeks and nothing updates."
THE INCIDENT
Approval Process "Discount_Approval" on Opportunity:
Submit action: Lock Record ✅ (by design — Finance wants frozen numbers)
Steps: Manager → VP → Finance (up to 14 days typical)
Final approval action: Field Update (Approval_Status__c = 'Approved'), Unlock Record
Nightly Batch Apex: updates Forecast_Category__c on ALL open Opportunities
→ fails with "record is locked" on any Opportunity mid-approval
Record-Triggered Flow "Notify_Account_Team": entry = IsChanged(Amount)
→ never fires on locked Opportunities because nothing CAN change Amount while locked
(mistaken for "the flow is broken" — it's actually correctly obeying the lock)THE PROBLEM
Two different symptoms, two different root causes. Explain exactly what "record locking during approval" does and doesn't prevent, why the batch job fails but the flow's "failure" is actually correct behavior, and design the fix for each (without breaking the Finance requirement that numbers stay frozen).
Write: (1) what approval locking actually locks, (2) why each symptom is happening, (3) the two separate fixes.
HINT LADDER
- Hint 1 (the avenue): (1) Approval-locking a record blocks most field updates (via UI, API, most Apex DML, batch) unless the updater is a user with "Modify All Data" or the change comes through an approved bypass mechanism (e.g., the approval process's own actions, or Apex running as a user with the right permission, or explicitly designed "allowed during lock" updates). (2) The batch job's failure is a real bug to fix (it's trying to write during a legitimate freeze — the batch needs to skip locked records, not force through them). (3) The flow's silence is NOT a bug —
Amountgenuinely cannot change while locked, soIsChanged(Amount)has nothing to detect; the real question is whether other fields the flow should react to are also affected. - Hint 2 (the mechanism): (1) Record locking during approval is enforced by the platform at the DML layer for the specific record submitted for approval (and, depending on config, related detail records) — it's not a validation rule, it's a hard lock most standard write paths respect. Users with "Modify All Data", or specific approval-related bypass logic, can still update; ordinary Apex DML from a regular running-user context cannot. (2) The batch job should be redesigned to filter out locked records (
WHERE ApprovalStatus...proxy field, or a check againstApproval.isLocked(recordId)-equivalent, or simply excluding records withApproval_Status__c = 'Pending') rather than attempting and failing the write, and should log/report skipped records for reprocessing after unlock — not silently error the whole batch chunk. (3) The flow's job is not broken: the account team's real ask ("tell us the status changes") should be served by a separate notification on the approval process itself (an approval step's own email alert / outbound message on submission or step transition) — not by the field-change flow, which structurally cannot see anything while the field is frozen. - Hint 3 (the skeleton): Fix 1 (batch): query-filter out locked/pending-approval Opportunities before the update DML; log skipped Ids; re-run (or a follow-up nightly job) once unlocked. Fix 2 (notification): add the "deal submitted for approval" / "deal is at step N" messaging directly to the approval process's email alerts (per-step), since the record-triggered flow cannot and should not try to detect changes that the lock is deliberately preventing. Keep the lock — it's doing its job; redesign around it, don't fight it.
THE REVEAL — POSTMORTEM
What actually happened (real class of incidents — "approval locking broke my automation" is one of the most common declarative/Apex interaction bugs reported by teams that added an approval process after their automation was already built):
What record locking during approval actually does: once a record is submitted (by default, at submission — configurable at which step), the platform locks it against edits through most standard paths — UI, most API/Apex DML, batch updates — except for users granted "Modify All Data" or logic the approval process itself performs (its own field updates on approve/reject/recall). This is a genuine, hard, platform-enforced lock, not a soft convention — and it is deliberately what Finance asked for.
Why the batch job broke: the nightly Apex batch was written before the approval process existed, with no awareness that some Opportunities might be mid-approval on any given night. It queried "all open Opportunities" without excluding locked ones, and its Database.update() calls threw locking errors for every locked record in the batch — likely also poisoning the whole execution chunk depending on how errors were handled (all-or-none vs partial success). This is a genuine bug: the batch needs explicit awareness of the lock state.
Why the flow's "failure" was correct, not a bug: IsChanged(Amount) on a locked record will never fire for that reason — Amount cannot change while locked, so there's nothing to detect. The account team's actual desire (visibility into approval progress) was never something a field-change-triggered flow could serve in the first place; they needed a process-status notification, a different requirement entirely that got misattributed to "the flow is broken."
The two separate fixes:
- Batch job: filter the query to exclude Opportunities currently in the approval process (
Approval_Status__c != 'Pending'proxy field, or check viaProcessInstance/ProcessInstanceSteprecords, or catch-and-skip with logging) — never attempt a write you know will be rejected; log skipped records for a deliberate post-unlock reconciliation pass. - Notification: add email alerts on the approval process's own steps (submission, each approver action, final approval/rejection) — this is the correct, purpose-built mechanism for "tell the team what's happening to a record while it's locked," and it doesn't fight the lock at all.
Why the "obvious fixes" failed (the contrast):
- "Give the batch job's running user 'Modify All Data'" → technically unlocks the write, but defeats the entire purpose of the Finance-mandated freeze — exactly the security anti-pattern of granting a broad bypass to solve a narrow scheduling problem.
- "Rebuild the notification flow to also watch
Approval_Status__c" → still fragile and duplicates what the approval process's native step-level email alerts already do better and more reliably. - "Unlock the record automatically before every batch run" → defeats the entire control; Finance's frozen-numbers requirement would be violated nightly.
KNOWLEDGE EXTRACTION (interview-ready)
- "What does approval locking actually block?" → Most standard write paths (UI, API, ordinary Apex DML, batch) to the submitted record, except users with "Modify All Data" or the approval process's own actions. It's a hard platform lock, not a validation rule.
- "How should automation coexist with an approval process?" → Automation that writes to in-approval records must filter locked records out and reconcile after unlock; automation that notifies on status should hook the approval process's own step-level email alerts, not a field-change trigger that structurally can't fire.
- "Is a flow failing to fire on a locked record a bug?" → Not necessarily — if the field it watches literally cannot change while locked, the flow is behaving correctly; the real gap is usually a mismatched requirement, not a broken flow.
- "What's the escalation-safe way to bypass a lock when truly necessary?" → A tightly scoped process (e.g., a "recall and resubmit" action, or a narrowly permissioned utility with audit logging) — never a blanket "Modify All Data" grant to a batch job's running user.
THE REDO
From memory: what the lock actually blocks and who's exempt, why the batch fails and why the flow's silence is correct, and the two separate fixes.
RETRIEVAL DRILL
- What write paths does approval-process record locking block?
- Who/what is exempt from the lock by default?
- Why did the flow's silence turn out to be correct behavior, not a bug?
- How should a batch job be redesigned to coexist with an approval process?
- Where should "notify the team of approval progress" logic actually live?
INTERVIEW MAPPING
Approval processes interacting with automation order is a documented senior-level differentiator — junior candidates know approval processes exist; senior candidates know they lock records and can explain exactly how that intersects with triggers, flows, and batch jobs already running in the org.
INCIDENT 5 — THE FEATURE FLAG THAT COULDN'T DEPLOY
STAKES
A dev team ships a new pricing engine behind a feature flag: Enable_New_Pricing__c, built as a Custom Setting (hierarchy-based, org-default false) so it can be "toggled per org without a deploy." It works great in the sandbox where the admin manually checked the box. Release day: the change set moves to production... and the flag comes back false, exactly as it was before anyone touched it — because Custom Setting data values are not included in a standard metadata deployment. The pricing engine silently stays disabled in prod, sales thinks the new pricing is live, quotes go out with the OLD engine's numbers for two full days before anyone notices the discrepancy.
THE INCIDENT
Custom Setting: Pricing_Config__c (Hierarchy)
Field: Enable_New_Pricing__c (Checkbox), Org default value = false
Sandbox: admin manually sets Org Default record's checkbox to TRUE via Setup UI.
Change set / deploy to Production: includes the Custom Setting's FIELD METADATA
(the object/field definition) — but NOT the actual DATA VALUE the admin set in sandbox.
Production: Enable_New_Pricing__c reverts to its default (false) because no deploy
mechanism carried the toggled VALUE across — only the schema moved.THE PROBLEM
Why did the deploy "succeed" but the toggle come back false? Name precisely what does and doesn't travel with a Custom Setting across environments, then redesign the feature flag using the tool actually built for this — explaining exactly why it solves the deployability problem.
Write: (1) what deploys and what doesn't for Custom Settings, (2) the correct tool and why, (3) the corrected design.
HINT LADDER
- Hint 1 (the avenue): (1) Change sets / standard metadata deploys move the Custom Setting's schema (object, fields) — they do not reliably carry the data values an admin entered through Setup (Custom Setting data is org-specific runtime data, more like a spreadsheet row than a deployable artifact). (2) The tool built for exactly this ("a value the codebase and admins both need, consistently, across every environment, without a data migration step") is a Custom Metadata Type (CMT) — its records ARE metadata, and metadata deploys with the package/change set/unlocked package like any other component. (3) Redesign: replace the Custom Setting with a CMT record (or records) and reference it from Apex/Flow exactly the same way, but now the "true/false" travels automatically with every deploy.
- Hint 2 (the mechanism): (1) Custom Settings are meant for runtime configuration that varies per org, per profile, or per user and is expected to be set live, in each environment, by an admin — the platform never promises to carry that data across a deploy because it's explicitly not meant to be a code artifact; it's closer to org-specific "instance data." (2) Custom Metadata Type records are themselves metadata components — like a Custom Label or a Flow, a CMT record is defined in XML and travels with any deployment mechanism (change sets, packages, SFDX source deploys) exactly like the object/field definitions do; this is precisely why Salesforce's own guidance recommends CMT for feature flags, thresholds, and configuration that must be identical (or intentionally different, but explicitly defined) across every sandbox and production. (3) Bonus capability CMT has that Custom Settings don't: relationship fields to other objects/metadata, and zero-SOQL access via
Object__mdt.getInstance()-style calls that don't count against the SOQL governor limit the way a Custom Setting or custom object query typically would in some access patterns — reinforcing CMT as the platform-idiomatic choice for exactly this "deployable flag" scenario. - Hint 3 (the skeleton): Custom Setting = per-org/profile/user runtime override, admin sets it live in each org, NOT a deployment artifact. Custom Metadata Type = deployable, packageable, cached, relationship-capable, zero-SOQL-cost — the correct tool for a feature flag. Fix: create
Pricing_Config__mdtwith anEnable_New_Pricing__ccheckbox record, set its value totrueas part of the deployed metadata itself (or in a controlled post-deploy metadata update), reference it from Apex (Pricing_Config__mdt.getInstance('Default').Enable_New_Pricing__c) — the flag's state is now literally part of what gets deployed and reviewed, same as code.
THE REVEAL — POSTMORTEM
What actually happened (real class of incidents — the "Custom Setting value didn't survive the deploy" release-day surprise; one of the most cited CMT-vs-Custom-Settings interview scenarios because it's a genuinely common mistake):
What actually travels in a deploy: a change set (or any metadata deployment mechanism) moves the definition of a Custom Setting — the object, its fields, its type (List vs Hierarchy) — because that definition IS metadata. But the data a Custom Setting holds (the actual row/value an admin entered through Setup, e.g., the org-default record's checkbox) is org-specific runtime data, not metadata, and is not guaranteed to move with a standard deploy. Every environment starts that Custom Setting at its schema-defined default until someone (or some post-deploy script) sets it manually, per environment.
Why Custom Metadata Types solve this exactly: a Custom Metadata Type record is metadata itself — defined as XML components, just like a Flow or a Custom Label — so it deploys, packages, and version-controls exactly like code. This is the single structural difference the interview question is testing: "Custom Metadata is deployable/packageable; Custom Settings are runtime overrides you set per org." CMT is also cached at the platform level and doesn't cost a SOQL call the way live queries typically do, has relationship fields to other objects (a Custom Setting cannot relate to another object the same way), and can be included in a managed/unlocked package for AppExchange-style distribution — none of which is true for Custom Settings.
The corrected design:
- Create
Pricing_Config__mdt(Custom Metadata Type) withEnable_New_Pricing__c(Checkbox) and any other pricing-engine toggles as fields. - Define the flag's value as part of the metadata itself — the deployed component carries the intended value, so what's reviewed in the PR/change set IS what goes live; no separate manual "don't forget to flip it in prod" step.
- Reference it identically from Apex/Flow:
Pricing_Config__mdt.getInstance('Default').Enable_New_Pricing__c— same call pattern developers already expect from a Custom Setting, so the migration is nearly drop-in. - Reserve Custom Settings for genuinely runtime, per-org/profile/user values an admin is expected to tune live in each environment without a deploy — e.g., a per-org API rate-limit override, not a release-gating feature flag.
Why the "obvious fixes" failed (the contrast):
- "Just remember to manually toggle it in prod after every deploy" → works until someone forgets (exactly what happened), and it means the "real" state of the flag lives in a person's memory, not in source control.
- "Use a Custom Label instead" → Custom Labels are also metadata and DO deploy, but they're translatable text values, not typed config with relationship fields — a legitimate near-miss for simple string flags, but the wrong tool for structured, relatable configuration.
- "Write a post-deploy Apex script to set the Custom Setting value" → adds deployment complexity (an extra step, another thing that can be skipped) to work around a tool that was never meant to carry deployable state, instead of switching to the tool that already solves it natively.
KNOWLEDGE EXTRACTION (interview-ready)
- "Custom Metadata Types vs Custom Settings — the one-liner?" → CMT = deployable, packageable, cached, relationship fields, the platform-idiomatic feature-flag tool. Custom Settings = runtime, per-org/profile/user override an admin sets live in each environment, not a deployment artifact.
- "Why did my Custom Setting value not show up after the change set deployed?" → Because the schema deploys but the data value doesn't — that's org-specific runtime data, not metadata.
- "When would you actually still use a Custom Setting?" → Genuine runtime, per-environment tuning values an admin is expected to set live (rate limits, per-profile overrides) rather than values that should travel with the release.
- "Does querying a Custom Metadata Type cost a SOQL query?" → CMT records are platform-cached and accessed via
getInstance()/getAll()patterns that avoid the typical SOQL governor cost profile of a live custom-object query — part of why it's the zero-SOQL-cost design pattern for feature flags.
THE REDO
From memory: what does/doesn't travel in a deploy for Custom Settings, why CMT is the correct tool, and the corrected design with the exact reference pattern.
RETRIEVAL DRILL
- What part of a Custom Setting deploys, and what doesn't?
- Why is a Custom Metadata Type record itself considered "metadata"?
- Name two capabilities CMT has that Custom Settings don't.
- When is a Custom Setting still the right choice?
- What's the Apex access pattern for reading a CMT value?
INTERVIEW MAPPING
"Custom Metadata vs Custom Settings" is explicitly listed as a core Admin-Basics interview theme — the feature-flag-that-didn't-deploy scenario is the exact production incident interviewers are probing for when they ask "which would you use, and why?"
INCIDENT 6 — THE IMPORT WIZARD THAT CHOKED AT MIDNIGHT
STAKES
Marketing hands the ops team a CSV of 380,000 new Leads from a trade show list purchase, due to be usable for a campaign launching at 8:00 AM. The admin, comfortable with the point-and-click Data Import Wizard from years of small cleanups, kicks off the import at 11:00 PM. By 2:00 AM it's still "processing," by 6:00 AM it has silently failed partway with no detailed error log beyond a generic count, and nobody can tell which of the 380,000 rows succeeded, which duplicated existing Leads, and which were rejected — with the campaign launch four hours away and the marketing lead asking "did it work?"
THE INCIDENT
Tool used: Data Import Wizard (Setup → Data Import Wizard)
- Point-and-click, field mapping UI, built-in dedupe rules
- Recommended for: a few thousand records, no CLI/scheduling, minimal logging
File: 380,000-row CSV
Result: extremely long processing time, generic summary ("X succeeded, Y failed"),
no per-row error detail exportable, no way to resume/re-run just the failures,
no way to schedule/automate a retry before the 8 AM deadline.THE PROBLEM
Which tool should have been used for this volume, and precisely why does the Import Wizard fail to scale operationally (not just technically) for a job like this? Name the four standard data tools, what each is actually for, and design the correct approach for tonight's 380,000-row load with a deadline.
Write: (1) why the Wizard was the wrong tool here, (2) the four tools and their real use cases, (3) the corrected approach.
HINT LADDER
- Hint 1 (the avenue): (1) The Data Import Wizard is designed for simple, small-volume, one-off, point-and-click imports (typically a few thousand records) with basic built-in duplicate management — it has no meaningful logging, no CLI, no scheduling, and no easy way to isolate/re-run just the failed rows. (2) The four tools: Data Import Wizard (small, simple, no code), Data Loader (up to 5,000,000 records, CLI-capable, scheduled, detailed success/error CSV logs), Workbench (browser-based, ad hoc SOQL/DML/API exploration, good for one-off diagnostic queries and small batch jobs, not a production ETL tool), Salesforce Inspector (browser extension, fast inline inspection/export/edit, not built for large loads). (3) Tonight's job: Data Loader, run in insert mode with a mapped CSV, detailed success/error log files that let you isolate and re-run exactly the failed rows before the deadline.
- Hint 2 (the mechanism): (1) The Import Wizard's UI-driven, single-threaded, browser-session-bound design means very large files take a long time and provide only a coarse "X succeeded / Y failed" summary — there's no exportable per-row error CSV to diagnose why rows failed (bad picklist value? duplicate? required field missing?), which is fatal when you need to fix and resubmit specific rows fast. (2) Data Loader, by contrast, produces two CSV files after every run: a success file (with new Salesforce Ids) and an error file (with the exact error message per failed row) — you can filter the error file, fix the specific issue, and re-run just those rows, which is the only realistic way to hit an 8 AM deadline on a 380K-row job. Data Loader also supports the command-line interface, so it can be scripted/scheduled and rerun unattended overnight rather than babysat through a browser tab. (3) Workbench and Salesforce Inspector are valuable but for different jobs: Workbench for ad hoc SOQL exports/small DML/API exploration and debugging; Inspector for quick inline record inspection/edits/exports directly from any Salesforce page, not built for hundreds of thousands of rows.
- Hint 3 (the skeleton): Tool matrix: Import Wizard = small/simple/no-code, few thousand rows max practical; Data Loader = up to 5M rows, CLI/scheduled, detailed logs — the correct choice here; Workbench = ad hoc SOQL/DML/API exploration, diagnostics; Inspector = quick browser-based inline inspection/edit/export. Tonight: switch to Data Loader, insert in reasonable batch sizes, capture success/error CSVs, isolate and fix failed rows from the error log, re-run just those, and script the whole thing so it can run unattended and be checked at 6 AM instead of babysat live.
THE REVEAL — POSTMORTEM
What actually happened (real class of incidents — the "used the wrong tool for volume" data-load disaster; a textbook admin-basics interview scenario because the tool choice itself is the entire test):
Why the Import Wizard was the wrong tool: it's explicitly designed for simple, point-and-click, low-volume imports — think "a few thousand records, one time, by someone who doesn't want to install anything." At 380,000 rows it's operating far outside its design envelope: the browser-bound UI session has to stay alive through the whole run, the built-in duplicate-management rules add per-row overhead, and — most damaging on a deadline — its feedback is a coarse summary count with no exportable per-row error detail, so there's no way to know which rows failed or why, and therefore no way to fix-and-retry just the failures.
The four standard data tools (memorize — the interview matrix):
- Data Import Wizard — Setup-native, wizard UI, field mapping + basic dedupe, no CLI, no scheduling, minimal logging. Best for: small, simple, occasional imports non-technical admins run themselves.
- Data Loader — desktop/CLI tool, handles up to 5,000,000 records, supports insert/update/upsert/delete/export, can be scripted and scheduled (command-line + config files), and produces detailed success and error CSV logs for every run — the production-grade choice for large or repeatable loads.
- Workbench — browser-based developer tool for ad hoc SOQL queries, small DML operations, REST/Bulk API exploration, metadata inspection — a diagnostic and small-scale utility, not built for hundreds of thousands of production rows.
- Salesforce Inspector (Reloaded) — a browser extension for fast inline record viewing/editing/exporting directly on any Salesforce page — excellent for quick spot-checks and small exports, not a bulk-load tool.
The corrected approach for tonight: run the 380,000-row file through Data Loader (CLI mode so it can run unattended overnight), in sensible batch sizes; capture the success and error log files it automatically produces; triage the error file for patterns (bad picklist values, missing required fields, duplicate matches); fix and re-submit exactly those rows; check the final counts against the source file before the 8 AM deadline — a process that is both faster and, critically, auditable, unlike the Wizard's opaque summary.
Why the "obvious fixes" failed (the contrast):
- "Just let the Wizard keep running, it'll finish eventually" → even if it eventually completes, there's no error detail to trust the result or fix failures before the deadline — "it finished" isn't the same as "we know what happened."
- "Split the CSV into smaller files and re-run the Wizard many times" → still no per-row error logging and still fully manual/browser-bound; doesn't solve the actual gap (visibility into failures).
- "Use Workbench to bulk-insert instead" → Workbench can technically push data via the API, but it's not built for logging/monitoring a 380K-row production load the way Data Loader's dedicated success/error file workflow is — the wrong tool for the job's scale, just a different wrong tool.
KNOWLEDGE EXTRACTION (interview-ready)
- "Data Import Wizard vs Data Loader — when do you pick which?" → Wizard: small, simple, occasional, no-code, built-in dedupe, no logging. Loader: up to 5M records, CLI/scheduled, detailed success/error CSV logs — the production choice for volume or repeatability.
- "Name all four standard data tools and their real jobs." → Import Wizard (small/simple), Data Loader (volume/scheduled/logged), Workbench (ad hoc SOQL/DML/API diagnostics), Salesforce Inspector (quick inline browser inspection/edit/export).
- "What does Data Loader give you that the Wizard doesn't, operationally?" → CLI scripting/scheduling (unattended runs) and detailed per-row success/error log files that let you isolate and retry exactly the failed records.
- "Why does tool choice matter for a developer, not just an admin?" → Because "how do we load/migrate this data" is a design decision with deadline, auditability, and rollback consequences — exactly the kind of production judgment call interviewers probe for beyond raw Apex skill.
THE REDO
From memory: why the Wizard failed operationally at this volume, all four tools and their real use cases, and the corrected Data Loader-based approach.
RETRIEVAL DRILL
- What's the practical volume ceiling where the Import Wizard stops being the right choice?
- What is Data Loader's stated maximum record volume?
- Name the two log files Data Loader produces after every run.
- What is Workbench actually good for, and what is it not built for?
- What is Salesforce Inspector, and when would you reach for it?
INTERVIEW MAPPING
"Data Import Wizard vs Data Loader vs Workbench vs Inspector" is a documented Admin Basics interview theme precisely because the answer requires knowing operational realities (logging, volume ceilings, scheduling), not just that the tools exist — this incident makes the deadline pressure and the missing-error-log gap concrete.
INCIDENT 7 — THE DASHBOARD THE VP WANTED AT 8 AM SHARP
STAKES
The VP of Sales asks for "a dashboard that refreshes automatically every morning at 7:00 AM so it's ready before I walk in at 8." The admin builds a beautiful dynamic dashboard — "Running as: logged-in user" — so each regional manager sees only their own region's data, exactly as security-conscious as the VP wanted. Comes time to schedule the 7 AM refresh: the "Schedule Refresh" option is missing entirely from the dashboard's menu. The admin spends an hour convinced it's a permissions bug, escalates to the platform team, who escalate to Salesforce Support — and the answer comes back in an afternoon: this was never going to work, by design.
THE INCIDENT
Dashboard "Regional Sales Overview"
Running User: "View dashboard as logged-in user" (dynamic dashboard)
Underlying reports: 3 Summary reports + 1 Matrix report, one Joined report for the exec rollup
Requirement: auto-refresh every morning at 7:00 AM, before anyone logs in
Setup → Dashboard → "..." menu → "Schedule Refresh" → OPTION NOT PRESENT
Admin's troubleshooting: rechecked profile access, folder sharing, dashboard edit permission — all fine.THE PROBLEM
Why is "Schedule Refresh" simply not there — is it a permissions bug or a platform rule? Explain exactly which report types can feed a dashboard, why dynamic dashboards specifically cannot be scheduled, and design a solution that still meets the VP's "ready by 8 AM" and "each manager sees only their region" requirements.
Write: (1) the report-type-to-dashboard rule, (2) why dynamic dashboards can't be scheduled and the reasoning behind it, (3) the redesign that satisfies both requirements.
HINT LADDER
- Hint 1 (the avenue): (1) Only Summary and Matrix reports can actually feed dashboard components (a Tabular report can't power most dashboard chart/table components without being converted; a Joined report has its own dashboard limitations too) — check the underlying reports match. (2) Dynamic dashboards ("run as logged-in user") are excluded from scheduled refresh by platform design — a scheduled refresh has to run as one fixed context, and "run as whoever happens to be viewing it" has no single identity to execute the refresh as at 7 AM when nobody's logged in. (3) Solution: either switch to a fixed running user with per-region folder/report filtering (loses per-viewer dynamism but enables scheduling), or keep it dynamic and rely on on-open refresh (dashboard refreshes when the manager opens it, not on a clock) plus a fast-cached "as of" timestamp so it still feels current by 8 AM.
- Hint 2 (the mechanism): (1) Tabular reports return a flat row list with no grouping — dashboards need grouped/summarized data to render charts, gauges, and most table components, which is exactly what Summary and Matrix reports provide (Matrix adds a second grouping dimension); Joined reports can technically be used in dashboard components in some contexts but carry their own restrictions and are commonly excluded from best-practice dashboard sourcing. (2) A static dashboard runs as one designated "Running User" — a fixed identity whose record/field access determines what the dashboard shows, regardless of who's viewing — which is exactly the stable context a scheduled job needs to execute against. A dynamic dashboard deliberately has NO single running user (that's its entire security feature — every viewer sees only their own accessible data) — so there is no identity for the platform to execute a 7 AM unattended refresh as, and Salesforce disables scheduling for dynamic dashboards for this structural reason, not as an arbitrary limitation. (3) To hit "8 AM, per-region security, no manual click" simultaneously: the practical compromise is either (a) build separate static dashboards per region (each with its own regional manager as Running User or a dedicated integration user scoped via folder/report filters) so each CAN be scheduled at 7 AM, sacrificing the single-dashboard elegance for scheduling capability; or (b) keep one dynamic dashboard and set expectations that it refreshes on-view, with cached data timestamped so managers see "as of 6:58 AM" from the last person who opened it, or a lightweight scheduled report-email as the true 7 AM artifact instead of a dashboard refresh.
- Hint 3 (the skeleton): Rule: only Summary and Matrix reports reliably power dashboard components (Tabular does not; Joined is limited). Rule: dynamic dashboards cannot be scheduled because there is no fixed running-user identity for the platform to execute the refresh as. Fix: split into per-region static dashboards (schedulable, each with appropriate Running User/folder scoping) OR keep it dynamic with on-open refresh + a scheduled report/email as the guaranteed 7 AM artifact. Either way: never promise "scheduled + dynamic" together — the platform has explicitly ruled that combination out.
THE REVEAL — POSTMORTEM
What actually happened (real class of incidents — the "Schedule Refresh option is missing" support ticket; a well-documented Admin Basics gotcha specifically because the missing menu option looks like a bug but is a deliberate design constraint):
The report-type rule (memorize): of the four report formats — Tabular (flat list, no grouping), Summary (one or more groupings with subtotals), Matrix (two-dimensional grouping, rows and columns), Joined (multiple report blocks combined) — only Summary and Matrix reports can feed dashboard components in the standard, reliable case. Tabular reports lack the grouping structure most dashboard components (charts, gauges, most tables) require. This is worth stating unprompted whenever a dashboard-building question comes up.
Why dynamic dashboards can't be scheduled (the actual reasoning, not just the rule): a dynamic dashboard's defining feature is that it has no single Running User — each viewer sees the dashboard's underlying reports run as themselves, respecting their own record/field access, which is precisely the security benefit the VP wanted (each regional manager sees only their region without needing separate dashboards or sharing rules to fake it). But a scheduled refresh is an unattended background job — it has to execute the underlying reports as someone, at a fixed time, with no viewer present to be "logged in as." Since a dynamic dashboard structurally has no fixed identity to run that job as, Salesforce simply doesn't offer the option — it isn't a bug or an oversight, it's the direct consequence of the same design that makes dynamic dashboards secure per-viewer in the first place.
The redesign that satisfies both requirements:
- Option A (most faithful to "ready by 8 AM"): split into per-region static dashboards, each with a Running User whose access matches that region (or a dedicated integration/service user with report-folder-level filtering) — each dashboard can now be scheduled to refresh at 7 AM, at the cost of maintaining multiple dashboards instead of one dynamic one.
- Option B (most faithful to "each manager sees only their region" via one dashboard): keep the dynamic dashboard, accept that it refreshes on open (or via manual "Refresh" click) rather than on a clock, and pair it with a scheduled Summary/Matrix report emailed at 7 AM as the guaranteed, time-certain artifact — the dashboard stays the exploration tool, the scheduled report email is the "definitely landed by 8 AM" artifact.
- Either way, set the expectation explicitly with the VP: "scheduled" and "dynamic" are mutually exclusive on this platform, and the choice is a genuine tradeoff, not a configuration the admin missed.
Why the "obvious fixes" failed (the contrast):
- "It's a permissions issue — grant the admin more access" → no permission unlocks a feature the platform has structurally disabled for this dashboard type; hours were lost chasing a nonexistent access bug.
- "Convert the underlying reports to Tabular for simplicity" → makes it worse — Tabular reports are exactly the format that can't reliably power dashboard components at all.
- "Just have the VP log in and refresh it manually before 8" → doesn't scale, isn't reliable, and ignores that a scheduled report-email is a simple, already-available substitute for the guaranteed-delivery requirement.
KNOWLEDGE EXTRACTION (interview-ready)
- "Which report types can feed a dashboard?" → Summary and Matrix reliably; Tabular cannot (no grouping); Joined has notable restrictions. State this unprompted when discussing dashboard design.
- "Can a dynamic dashboard be scheduled to refresh?" → No — dynamic dashboards have no single Running User, and a scheduled refresh requires one fixed identity to execute the job as. This is a structural platform rule, not a bug or a permission gap.
- "How do you deliver a guaranteed-by-a-certain-time view of per-region data?" → Either per-region static dashboards (each schedulable) or a dynamic dashboard paired with a scheduled report-email as the time-certain artifact.
- "What's the deeper lesson for a developer?" → Declarative reporting/dashboard tools have real structural limits, exactly like Apex governor limits — a developer who assumes "the admin will just configure it" without knowing these limits will promise something the platform cannot deliver.
THE REDO
From memory: which report types feed dashboards, the exact reasoning why dynamic dashboards can't be scheduled, and the two redesign options.
RETRIEVAL DRILL
- Which two report formats reliably power dashboard components?
- Can a Tabular report feed a dashboard component directly?
- Why, structurally, can't a dynamic dashboard be scheduled to refresh?
- What are the two practical redesign options to meet a "scheduled + per-viewer-secured" requirement?
- Is the missing "Schedule Refresh" option a bug, a permission gap, or a design rule?
INTERVIEW MAPPING
"Only Summary and Matrix reports feed dashboards; dynamic dashboards can't be scheduled" is called out explicitly as a core Admin Basics fact set — this incident turns the bare fact into the reasoning (no fixed running-user identity) that actually proves understanding in an interview.
INCIDENT 8 — THE ESCALATION RULE THAT ESCALATED NOTHING
STAKES
Support promises Tier-1 customers a 4-hour SLA on Cases. The admin builds an Escalation Rule: "if a High-priority Case is unresolved for 4 hours, escalate to the Tier-2 queue and notify the manager." It's demoed once, looks perfect, ships. Three months later, a churned enterprise customer's exit interview reveals their case sat unresolved for 11 hours with zero escalation, zero manager notification — and an audit of the last quarter finds dozens of SLA breaches the escalation rule never caught, despite the rule clearly existing and being marked Active.
THE INCIDENT
Escalation Rule: "High_Priority_SLA"
Rule Entry: Priority = 'High'
Business Hours: (none specified — defaults to 24/7 org-wide business hours)
Age Over: 4 hours
Escalation Action: Reassign Owner to "Tier2_Queue", Notify Case Owner (NOT the manager)
Missed cases pattern: Cases created Friday 4 PM, business hours = Mon-Fri 9-5 configured
elsewhere in the org (Case business hours field), but the escalation rule's own
"age" calculation used the ORG default 24/7 clock, not the Case's actual assigned
Business Hours record — creating a mismatch between "when the SLA clock should
pause" and "when the rule actually measured elapsed time."
Also: "Notify Case Owner" ≠ "Notify the manager" — the rule notified the very
person already failing to act, not anyone positioned to intervene.THE PROBLEM
Two separate configuration failures let SLA breaches through undetected for a quarter. Name both precisely (the business-hours mismatch and the notification-target mistake), explain how Assignment Rules and Escalation Rules actually relate to each other, and redesign the SLA escalation so both classes of failure are closed.
Write: (1) the business-hours mismatch mechanism, (2) the notification-target flaw, (3) the corrected design plus how it interacts with the assignment rule.
HINT LADDER
- Hint 1 (the avenue): (1) Escalation rules measure "age" against a Business Hours configuration — if the rule (or the Case's assigned Business Hours field) doesn't match the support team's actual working hours, the "4 hours" the rule counts and the "4 hours" support actually worked are different clocks. (2) "Notify Case Owner" just re-notifies the person already sitting on the case — the entire point of escalation is routing attention to someone new (a manager, a Tier-2 queue's members) who can actually intervene. (3) Assignment Rules and Escalation Rules are different tools: Assignment Rules decide the initial owner/queue when a Case/Lead is created; Escalation Rules act afterward, based on elapsed time, to reassign/notify if it's not resolved in time — they're sequential, not competing, and a broken escalation rule doesn't undo a correct assignment, it just means nobody ever picks up the ball after the initial assignment stalls.
- Hint 2 (the mechanism): (1) Every Case has (or can have) an associated Business Hours record, and Escalation Rules use Business Hours to calculate elapsed "business time" (so a Friday-4-PM case doesn't silently accrue 65 hours of "age" over a weekend when nobody's working) — but if the org's default Business Hours is 24/7 while support actually only staffs 9-5 Mon-Fri, the rule's clock and the real world diverge, and cases created near the end of a business day get miscounted (either escalating too early against a 24/7 clock that never truly paused, or — as here — appearing to age past 4 business-hours when the wall-clock math the admin assumed doesn't match what Business Hours actually applies). The fix is aligning the Case's Business Hours field/the rule's Business Hours setting with the support team's real schedule, deliberately, and testing across a weekend boundary. (2) Escalation notification should target people who can actually act when the front-line owner has stalled — a manager, a public group of Tier-2 leads, or the Tier-2 queue's members — never just re-notifying the same owner who already had 4 hours and didn't act. (3) Assignment Rules run at Case creation (or specific triggering edits) to route to the right initial owner/queue; Escalation Rules run afterward on a timer, independent of assignment — they layer: assignment gets it to the right desk immediately, escalation is the safety net if that desk goes silent.
- Hint 3 (the skeleton): Fix 1: create/assign a Business Hours record matching actual support coverage, apply it to the Case's Business Hours field (or the escalation rule's applicable hours), and explicitly test a Friday-afternoon case to confirm the weekend doesn't get miscounted. Fix 2: change the escalation action's notification target from "Case Owner" to the manager / a Tier-2 public group / an email alert to a distribution list — someone new, not the person who already missed the window. Bonus: add a scheduled reconciliation report ("Cases open > 4 business hours, High priority") as a second, independent detection layer, exactly like Module 4's "fault path + reconciliation" discipline applied to a declarative admin tool.
THE REVEAL — POSTMORTEM
What actually happened (real class of incidents — the "escalation rule existed but never actually escalated" SLA-breach audit; a common gap because escalation rules are configured once and rarely re-tested against real calendar edge cases):
Failure #1 — the Business Hours mismatch: Escalation Rules calculate "age" in business hours, not wall-clock hours, using a Business Hours record — this is a deliberate feature (so a case shouldn't silently blow past SLA over a weekend nobody's staffing), but it only works correctly if the Business Hours record actually matches the team's real schedule and is properly associated with the Cases in question. Here, the org's default Business Hours was left at 24/7 while support genuinely worked Mon–Fri 9–5, so the rule's internal clock and the team's real clock disagreed — Friday-afternoon cases were the specific edge case that exposed the mismatch, aging through a weekend the rule's math didn't correctly pause for.
Failure #2 — the notification target: the escalation action notified the Case Owner — the exact person who already had 4 hours and didn't resolve it. Escalation, by definition, needs to reach someone new: a manager, the Tier-2 queue's members, or a distribution list positioned to actually intervene. Notifying the stalled owner again is not escalation; it's a duplicate reminder to the same person.
How Assignment Rules and Escalation Rules actually relate: they are sequential, complementary tools, not alternatives. Assignment Rules fire at Case (or Lead) creation/qualifying update to route the record to the correct initial owner or queue based on criteria (region, product, priority). Escalation Rules fire afterward, on a business-hours timer, if the case remains unresolved past a threshold — they exist specifically to catch the case where the initial assignment was correct but nothing happened next. A broken escalation rule doesn't affect the initial routing at all; it just means the safety net that's supposed to catch a stalled case has a hole in it — precisely what happened here for a full quarter.
The corrected design:
- Create/verify a Business Hours record that matches actual support coverage; assign it correctly to the Cases in scope (via the Case's Business Hours field or the rule's applicable-hours setting); explicitly test a Friday-afternoon case across the weekend boundary before trusting the rule again.
- Change the escalation action's notify target from Case Owner to the manager and/or a Tier-2 public group / distribution list — someone with the standing and attention to intervene.
- Add a scheduled reconciliation report ("High-priority Cases open longer than 4 business hours") as an independent second detection layer — the same "don't trust the green light alone" discipline that catches silent automation gaps in any declarative tool, not just Flow.
Why the "obvious fixes" failed (the contrast):
- "The rule is Active, so it must be working" → "Active" only means the rule is enabled, not that its Business Hours math and notification target are correct — an active rule with the wrong clock and the wrong recipient produces zero visible errors while quietly doing nothing useful.
- "Just lower the escalation threshold to 2 hours to be safe" → doesn't fix the underlying Business Hours mismatch or the notification-target flaw; it just changes when the same broken behavior happens.
- "Escalation rules and assignment rules must be conflicting" → they don't conflict at all; they're sequential and independent — the bug was entirely inside the escalation rule's own configuration.
KNOWLEDGE EXTRACTION (interview-ready)
- "How do Escalation Rules calculate elapsed time?" → Against a Business Hours record, not raw wall-clock time — deliberately, so weekends/off-hours don't falsely trigger (or falsely fail to trigger) escalation, but only if the Business Hours record actually matches the team's real schedule.
- "Assignment Rules vs Escalation Rules — how do they relate?" → Sequential and complementary: Assignment Rules route a new/qualifying record to the right initial owner/queue; Escalation Rules act afterward, on a timer, if the case stays unresolved — a safety net, not a competing mechanism.
- "What's the classic escalation-notification mistake?" → Notifying the same Case Owner who already missed the SLA window instead of a manager or a Tier-2 group who can actually intervene.
- "How do you catch a broken escalation rule before an audit does?" → A second, independent detection layer — a scheduled reconciliation report counting SLA-breaching cases — same discipline as fault-path + reconciliation in Flow automation.
THE REDO
From memory: the two configuration failures (Business Hours mismatch, notification target), how Assignment and Escalation Rules relate, and the corrected three-part design.
RETRIEVAL DRILL
- What do Escalation Rules measure age against, and why?
- What was wrong with notifying "the Case Owner" as the escalation action?
- Are Assignment Rules and Escalation Rules alternatives or complements?
- What edge case (day/time) exposed the Business Hours mismatch?
- What's the independent second detection layer that would have caught this sooner?
INTERVIEW MAPPING
Assignment and escalation rules for Leads/Cases are explicitly named in the Admin Basics theme list — this incident tests whether a candidate understands the Business Hours dependency and the assignment/escalation sequencing, not just that "escalation rules exist."
🏆 CAPSTONE — THE ORG WHERE ADMIN AND DEV NEVER TALKED
STAKES
You're brought in as the senior developer for a org health review before a Series C-funded expansion triples headcount. The CTO hands you a single incident report — six weeks of tickets, condensed — and says: "Before we scale this org to 3x the users, tell me what's actually broken, in priority order, and what the underlying disease is. You have until end of day."
THE INCIDENT (multi-part incident report)
TICKET A — A "VIP Case" report shared with the whole Sales org (via a report folder shared
to "All Internal Users") shows Sensitive_Notes__c in full for every viewer, despite the
field being removed from every profile's page layout except Legal's.
TICKET B — A bulk cleanup deleted 200 "duplicate" Closed Won Opportunities via list view;
no validation error fired; Finance discovered a revenue gap three weeks later.
TICKET C — The "APAC Support" team, set up as a Queue with 8 members, reports they can't
see any of the Cases auto-assigned to them by the assignment rule; a sharing rule was
built pointing at a public group with a matching name that turned out to be empty.
TICKET D — A newly added Approval Process on Contract records locks the record on submission;
the nightly Apex renewal-reminder batch has been silently failing on any Contract
currently in approval for the last 5 weeks, and nobody noticed because the batch's
error emails were filtered to spam.
TICKET E — A "Beta_Features_Enabled__c" toggle, built as a Custom Setting, works perfectly
in every sandbox (admins toggle it manually) but has now failed to activate correctly in
production after 3 separate release attempts — each time, someone forgets the manual step.
TICKET F — Ops attempted to load 250,000 historical Contact records via the Data Import
Wizard over a weekend; it was still "processing" Monday morning with no way to tell what
succeeded, forcing a full rollback and a redo with a different tool, costing 4 days.
TICKET G — The VP's "Regional Pipeline" dashboard (built dynamic, run-as-logged-in-user, for
legitimate per-region security) cannot be scheduled to refresh before the 8 AM leadership
meeting; leadership has been manually refreshing it live during the meeting for months.
TICKET H — The Tier-1 "4-hour SLA" escalation rule has a Business Hours mismatch (org default
24/7 vs. actual Mon-Fri 9-5 support coverage) and notifies only the Case Owner, not a
manager; a quarter-end audit found dozens of missed SLA breaches.THE PROBLEM
Write a 2-minute incident-report script for the CTO that: (1) maps each ticket (A–H) to its root mechanism in one line each, (2) states the ONE shared disease underlying all eight tickets, (3) gives a priority order for fixing them (tonight / this week / this quarter) with justification, and (4) gives a verification checklist (5+ items) you'd run before declaring the org "safe to 3x."
THE REVEAL — POSTMORTEM
This capstone has no single hidden twist — it's a synthesis exercise. Write your own script and checklist before opening the answer sheet. The model answer will map every ticket to its exact incident number from this module, name the shared disease, and give a verification checklist built from the disciplines in M0.
KNOWLEDGE EXTRACTION (interview-ready)
- Every ticket maps to exactly one of Incidents 1–8 above; if you can't place a ticket in one line, re-read that incident's KNOWLEDGE EXTRACTION section.
- The "shared disease" question is the entire point of the capstone — a senior candidate names ONE root cause across eight surface symptoms, not eight unrelated bugs.
- The verification checklist is your answer to "how do you know it's actually fixed" — vague reassurance fails here; each checklist item should be a concrete, re-runnable test.
THE REDO
Rewrite your 2-minute script from memory after reading the answer sheet's model version, without looking at it while you write.
RETRIEVAL DRILL
- Map tickets A–H to incidents 1–8 without looking anything up.
- State the one shared disease in a single sentence.
- Give your tonight/this-week/this-quarter priority order.
- Name at least 5 verification-checklist items.
- What's the one sentence you'd say if the CTO interrupted you after 20 seconds?
INTERVIEW MAPPING
This is the "tell me what's wrong with this org" senior-loop question in its most complete form — it tests whether Admin Basics knowledge is a checklist you memorized or a diagnostic lens you can apply to a report you've never seen before.
(End of Module 9 workbook. The sealed answer sheet is 09b_Topic09_Admin_Basics_Answer_Sheet.md — do not open it until you've attempted every incident above in writing.)