Salesforce Interview Prep

Module 8 — ANSWER SHEET (SEALED)

Companion to 08_Topic08_Data_Model.md — open ONLY after you have written your own attempt.

Protocol (file 00): write ≥2 hypotheses + 2 solution attempts before reading. Then compare your attempt against this sheet like an investigator compares a suspect's story to the evidence. The contrast IS the learning. Then REDO from memory, then do the drill.


INCIDENT 1 — THE DELETE THAT TOOK THE BUILDING DOWN

The problem restated

Deleting one Opportunity cascaded through three generations of Master-Detail (OppLineItem, OppContactRole, custom Commission_Split__c) and destroyed 365 records in one click. Explain the transitive cascade, the two safety valves, and the process fix.

Model answer (2-min interview version)

  • Cascade mechanism: Master-Detail cascade delete is transitive — it doesn't stop at one hop. Opportunity → OppLineItem + OppContactRole (both MD, standard), and because OppContactRole is itself the master to the custom Commission_Split__c (also MD), that third generation dies too. Deleting a "master" deletes its entire dependent tree, however deep the MD chain runs.
  • Two safety valves: (1) the recycle bin — 15 days (subject to storage eviction), soft net only, useless if the master is deleted a second time or the window lapses; (2) the platform blocks cascade-deleting more than 200 records of a junction object's children in one operation unless "Enable cascading delete of up to 200 records" (or equivalent) is explicitly turned on — a governor aimed exactly at this fan-out class.
  • The fix: schema-level — ask whether a financially/compliance-sensitive child (Commission_Split) should really be Master-Detail (cascade risk) vs Lookup + DLRS/Apex roll-up (decoupled lifecycle); process-level — a before-delete guard (validation rule/trigger/Flow) blocking parent deletion while dependent financial records exist in a non-void state, and removing raw list-view delete access from profiles that don't need it.

Self-grade checklist

  • Named transitive (multi-generation) cascade, not just "one hop"
  • Named the exact 200-record junction cascade cap and its override setting
  • Named the recycle bin's real limits (15 days, soft net, breaks on 2nd delete)
  • Proposed a before-delete guard, not just "be more careful"
  • Questioned whether Commission_Split should be MD at all

THE REDO — model answer

Cascade chain: Opportunity → OppLineItem + OppContactRole (MD) → Commission_Split__c (MD off OppContactRole)
Safety valves: recycle bin (15 days, soft) + 200-record junction cascade cap (override setting)
Fix: schema review (MD vs Lookup+DLRS for financial children) + before-delete guard trigger/Flow/validation rule

RETRIEVAL DRILL — model answers

  1. Does cascade stop at one generation? → No — it's transitive through every level of Master-Detail.
  2. Exact governor limit? → Cascade-deleting more than 200 records of a junction object's children in one operation is blocked by default.
  3. Setting to raise it, and why off by default? → "Enable cascading delete of up to 200 records" (or equivalent) — off by default because it's a deliberate guard against silent large-scale fan-out.
  4. Two real limits of the recycle bin? → 15-day (storage-subject) window, and no protection if the record is deleted again or the master is deleted after the detail (Incident 6).
  5. Two ways to protect a sensitive MD child? → Don't model it as Master-Detail (use Lookup + Apex/Flow/DLRS roll-up), or add a before-delete guard blocking parent deletion while dependents exist.

INCIDENT 2 — THE ROLL-UP THAT WENT DARK

The problem restated

A roll-up summary froze after converting its source Master-Detail relationship to Lookup. Explain the real conversion rule, why it "succeeded," and the three legitimate fixes.

Model answer (2-min interview version)

  • The rule: Salesforce blocks MD→Lookup conversion while any roll-up summary field depends on that relationship — Setup throws an explicit error naming the field. The conversion only "succeeded silently" because the roll-up field was removed (or repointed) first, clearing the blocker — and the field left behind on the page layout became a frozen static value, not an error, not a deletion, with zero UI indication that it's dead.
  • Three legitimate paths: (1) don't convert — check if the specific Master-Detail relationship has "Allow reparenting" enabled, solving the business need without a full relationship-type change; (2) convert to Lookup and replace the native roll-up with DLRS (Declarative Lookup Rollup Summaries — config-driven, no code, the "bonus points" answer), a record-triggered Flow, or an Apex trigger; (3) redesign — batch reassignment process for rare bulk reparenting events instead of giving up the roll-up permanently.
  • One-liner: roll-up summaries only exist on the master side of Master-Detail — full stop; Lookup never gets a native one.

Self-grade checklist

  • Named the real blocking rule (roll-up dependency blocks MD→Lookup conversion)
  • Explained the field freezes silently, doesn't error or delete
  • Named "Allow reparenting" as a non-conversion alternative
  • Named all three Lookup-roll-up workarounds, explicitly including DLRS by name
  • Noted MD→Lookup and Lookup→MD have separate, different blocking rules

THE REDO — model answer

Conversion rule: MD→Lookup blocked while a roll-up summary depends on the relationship
"Success" only happened because the roll-up was deleted first → field freezes, no error
Fixes: (1) Allow Reparenting setting instead of converting, (2) Lookup + DLRS/Flow/Apex trigger, (3) batch reassignment redesign

RETRIEVAL DRILL — model answers

  1. What blocks MD→Lookup conversion? → Any roll-up summary field depending on that relationship.
  2. What happens to the field's value after conversion (roll-up deleted first)? → It freezes at its last computed value — no auto-delete, no stale flag.
  3. Three Lookup-side roll-up workarounds? → Apex trigger, record-triggered Flow, DLRS (Declarative Lookup Rollup Summaries).
  4. Setting that can solve reparenting without full conversion? → "Allow reparenting" on the Master-Detail relationship.
  5. What blocks Lookup→MD conversion? → The lookup field must be populated on 100% of existing records (Master-Detail cannot be null).

INCIDENT 3 — THE QUERY THAT COULDN'T SEE ITS GRANDCHILDREN

The problem restated

A triple-nested parent-child SOQL subquery throws MALFORMED_QUERY. Explain the rule, why it exists, and two working alternatives.

Model answer (2-min interview version)

  • The rule: parent→child relationship subqueries in SOQL are capped at one level of nesting — you cannot subquery a subquery's children in one statement. Child→parent dot notation has no such cap (commonly cited up to 5 levels for standard relationship chains) because it's a straight join walk, not a nested semi-join the query planner must fold.
  • Why: each parent-child subquery compiles to a semi-join the planner folds into the outer plan; Salesforce's multi-tenant query planner bounds that folding to one level to keep query cost predictable across every tenant. Dot-notation joins don't multiply result shape the same way, so they're allowed deeper.
  • Alternative 1 (restructure): query from the lowest-level object and walk upward with dot notation — SELECT Id, Quantity, Opportunity.Name, Opportunity.Account.Name FROM OpportunityLineItem WHERE Opportunity.AccountId IN :accountIds — no subquery, no depth issue.
  • Alternative 2 (compose): two flat queries — Accounts with one level of Opportunities subquery, then OpportunityLineItems WHERE OpportunityId IN :oppIds — stitched together in Apex into the nested wrapper shape.
  • When to pick which: flatten when the leaf data is what you render; compose in Apex when you genuinely need the 3-level nested shape and a flattened join would be wastefully wide.

Self-grade checklist

  • Named the exact 1-level cap on parent→child subqueries
  • Named why child→parent dot notation isn't capped the same way
  • Gave the flattened dot-notation alternative query
  • Gave the two-query Apex-composition alternative
  • Stated a clear "when to pick which" rule

THE REDO — model answer

-- Alternative 1: flatten
SELECT Id, Quantity, Product2.Name, Opportunity.Name, Opportunity.Account.Name
FROM OpportunityLineItem WHERE Opportunity.AccountId IN :accountIds
// Alternative 2: compose
List<Account> accts = [SELECT Id, Name, (SELECT Id, Name FROM Opportunities) FROM Account WHERE Id IN :accountIds];
// collect oppIds, query OpportunityLineItem WHERE OpportunityId IN :oppIds, stitch in a Map

RETRIEVAL DRILL — model answers

  1. Max parent→child subquery nesting? → One level.
  2. Why no cap on child→parent? → It's a join walk (not a nested semi-join the planner folds), so depth doesn't multiply result-set complexity the same way.
  3. Flattened alternative? → Query from OpportunityLineItem with Opportunity.Account.Name dot notation.
  4. Two-query alternative? → Account+Opportunities subquery, then OpportunityLineItem by OpportunityId IN set, composed in Apex.
  5. Semi-join/anti-join vs this limit?IN/NOT IN subqueries filter by existence in a related object; they're a separate pattern from parent-child data-fetching subqueries and aren't subject to the same nesting rule in the same way.

INCIDENT 4 — THE TASK THAT POINTED AT THE WRONG TYPE

The problem restated

Task.What.Name silently returns null for Case-related tasks, breaking generated links. Explain why, the polymorphic query mechanics, and the fix.

Model answer (2-min interview version)

  • Why it breaks for Case specifically: WhatId is polymorphic — it can point to Account, Opportunity, Case, Campaign, custom objects, etc. — and Salesforce doesn't require a common field surface across those types. Account/Opportunity have Name; Case doesn't (it has CaseNumber). The query is syntactically valid, so for a Case-typed WhatId, What.Name just resolves to null per-record — no exception, which is exactly why it went unnoticed for two months.
  • Correct query mechanics: SOQL TYPEOF What WHEN Account THEN Name WHEN Case THEN CaseNumber, Status WHEN Opportunity THEN Name ELSE Id END branches field selection by actual referenced type in one query. Or in Apex, task.WhatId.getSObjectType().getDescribe().getName() gets the runtime type to branch display/link logic. Or split into per-type queries when per-type logic is heavy.
  • Corrected link building: never hardcode /lightning/r/Account/...; always derive the object segment from the actual runtime type: '/lightning/r/' + actualObjectApiName + '/' + task.WhatId + '/view'.

Self-grade checklist

  • Explained polymorphic = no guaranteed common field surface across target types
  • Explained why it's null, not an error (syntactically valid query)
  • Wrote the TYPEOF skeleton correctly
  • Named the Apex getSObjectType() alternative
  • Fixed the hardcoded URL prefix bug specifically

THE REDO — model answer

SELECT Id, Subject, WhatId, TYPEOF What
  WHEN Account THEN Name
  WHEN Case THEN CaseNumber, Status
  WHEN Opportunity THEN Name, StageName
  ELSE Id
END FROM Task
String objType = task.WhatId.getSObjectType().getDescribe().getName();
String url = '/lightning/r/' + objType + '/' + task.WhatId + '/view';

RETRIEVAL DRILL — model answers

  1. Three polymorphic fields?WhatId (Task/Event), WhoId (Task/Event), OwnerId (User or Queue on some objects).
  2. Why no error for Case? → The query is syntactically valid; the referenced type just lacks the field, so it nulls per-record.
  3. TYPEOF skeleton?TYPEOF What WHEN Account THEN Name WHEN Case THEN CaseNumber WHEN Opportunity THEN Name ELSE Id END.
  4. Apex method for runtime sObject type?Id.getSObjectType() (e.g., task.WhatId.getSObjectType().getDescribe().getName()).
  5. Split-query alternative — when preferred? → When per-type display/logic is heavy enough that a single TYPEOF query becomes unwieldy — bucket WhatIds by type, run one clean query per type.

INCIDENT 5 — THE LEAD THAT CLONED ITSELF

The problem restated

Standard Lead conversion created 1,100 duplicate Accounts because no automatic matching occurred and duplicate rules didn't block it. Explain conversion's default behavior, why duplicate rules failed, and the fix.

Model answer (2-min interview version)

  • What conversion creates: an Account, a Contact, and optionally an Opportunity (if "Create Opportunity" is checked) — with an option in the conversion screen to search for and attach to an EXISTING Account/Contact instead. If that search step is skipped, a new Account is created every time — zero built-in fuzzy matching.
  • Why duplicate rules didn't stop it: likely two gaps — (1) the matching rule used exact-name matching (not fuzzy/domain-based), so slight name variants don't match; (2) the duplicate rule action was Alert, not Block — alerts don't stop anything and are easy to click past during routine conversion.
  • The fix: a blocking duplicate rule on Account with a fuzzy matching rule (name similarity + Website/domain) is the highest-leverage fix; a mandatory/forced existing-Account search step (ideally a custom Database.LeadConvert-based conversion flow that runs the search automatically); and cleanup of existing duplicates via Database.merge() (up to 3 records), which reparents Contacts/Opportunities correctly — never delete.

Self-grade checklist

  • Named exactly what conversion creates and the existing-record-attach option
  • Stated conversion has zero built-in fuzzy matching
  • Diagnosed both duplicate-rule gaps (exact match + Alert-only)
  • Proposed Block + fuzzy rule as the primary fix
  • Named Database.merge() for cleanup, explicitly not delete

THE REDO — model answer

Conversion creates: Account + Contact + (optional) Opportunity; can attach to existing if searched
No auto fuzzy-match by default
Duplicate rule gaps: exact-match rule + Alert-only action
Fix: Block rule + fuzzy Name/Website matching + forced search step + Database.merge() cleanup

RETRIEVAL DRILL — model answers

  1. Three records conversion can create? → Account, Contact, Opportunity (optional).
  2. Auto-match by default? → No.
  3. Why did Alert rules fail? → Alert warns but doesn't block; easy to dismiss/ignore during routine conversion.
  4. Correct fix operation for existing duplicates, and why not delete?Database.merge() — reparents related Contacts/Opportunities/Cases; delete would orphan or destroy those related records.
  5. Max records per merge? → 3.

INCIDENT 6 — THE RECYCLE BIN THAT COULDN'T GIVE IT BACK

The problem restated

A Master-Detail child was deleted, then its master was deleted afterward; undelete of the original child failed to cleanly reattach. Explain the ordering rule, correct recovery sequence, and prevention.

Model answer (2-min interview version)

  • The rule: a detail record deleted while its master is still live sits safely in the recycle bin referencing a valid parent. But once the master is deleted in a separate, later operation, that master-delete cascades only its then-current details as one group — the earlier, independently-deleted detail is not part of that cascade group. Restoring the master does not retroactively reattach a detail deleted before it in an earlier operation; that restore is fragile and can fail or leave an orphaned/broken state.
  • Correct recovery sequence: always undelete the master first, confirm it and its cascaded details, then attempt the independently-deleted detail's restore — understanding this reduces but doesn't eliminate risk; if reattachment fails, fall back to a data backup/export to recreate the record, since undelete alone may not be sufficient.
  • Prevention: a before-delete guard (validation rule, Flow, or Apex trigger) on any Master-Detail parent that surfaces the count of related detail records — active AND in the recycle bin — before confirming deletion, so "this looks empty" is never assumed visually.

Self-grade checklist

  • Explained why the two deletions being separate operations breaks clean undelete
  • Stated master-first as the correct restore order
  • Acknowledged even correct order isn't a full guarantee
  • Named backup/export as the fallback recovery path
  • Proposed the pre-delete count-check guard

THE REDO — model answer

Rule: detail deleted before master (separate ops) → master's later delete cascades only its current details, not the earlier one
Recovery: undelete master FIRST → then attempt detail undelete (fragile, may fail)
Fallback: restore from backup/export if reattachment fails
Prevention: before-delete guard showing related detail-record count (active + recycle bin)

RETRIEVAL DRILL — model answers

  1. Safe restore order? → Master before detail.
  2. Why doesn't master restore auto-reattach an earlier independently-deleted detail? → It wasn't part of the master's own cascade-delete group; the two deletions were separate operations.
  3. Fallback if undelete fails to reattach? → Restore from a data backup/export.
  4. Prevention mechanism? → A pre-delete check (validation rule/trigger/Flow) surfacing related detail-record counts, including recycle bin, before parent deletion is confirmed.
  5. Recycle bin retention? → Up to 15 days, subject to storage-based early eviction.

INCIDENT 7 — THE OWD THAT FLIPPED ITSELF

The problem restated

Converting Master-Detail to Lookup silently flipped OWD from "Controlled by Parent" to Public Read/Write on a sensitive object. Explain why the OWD must change, why it defaults permissive, and the process fix.

Model answer (2-min interview version)

  • Why OWD must change: "Controlled by Parent" isn't a real independent OWD — it's a delegation ("inherit the master's sharing"), valid ONLY on the detail side of an active Master-Detail relationship. Once converted to Lookup, that delegation target no longer exists (Lookup has independent sharing by design), so Salesforce must assign a genuine, standalone OWD value.
  • Why it defaults to Public Read/Write, not Private: documented platform behavior — the platform can't infer the intended restrictive posture and fails open (permissive) to avoid silently breaking existing processes/reports that assumed the previously-inherited visibility. This is the single most consequential silent side effect of MD→Lookup conversion.
  • The fix: immediately set the object to Private + recreate the old access pattern via sharing rules/manual sharing; make an explicit "OWD before/after, reviewed by a second person" step mandatory for every relationship-type conversion going forward; add an automated guardrail (a deployment validation script or scheduled job) that flags unexpected OWD drift between deployments.

Self-grade checklist

  • Explained "Controlled by Parent" as delegation, not a real OWD
  • Explained why it can't survive the conversion
  • Named the documented default (Public Read/Write) and the fail-open rationale
  • Proposed the mandatory review step
  • Proposed an automated drift-detection guardrail

THE REDO — model answer

"Controlled by Parent" = delegation, valid only under active Master-Detail
MD→Lookup removes delegation target → platform must assign real OWD
Default: Public Read/Write (documented, fail-open for continuity)
Fix: set Private + sharing rules; mandatory OWD before/after review; automated drift-detection guardrail

RETRIEVAL DRILL — model answers

  1. What does "Controlled by Parent" mean? → Delegated sharing — the object has no OWD of its own; it inherits the master's.
  2. Why can't it survive MD→Lookup conversion? → It requires an active Master-Detail relationship; Lookup has independent sharing by design.
  3. Documented default after conversion, and why? → Public Read/Write — fail-open, because the platform can't infer intended restrictiveness and avoids silently breaking existing processes.
  4. Mandatory review step? → Explicit OWD before/after check, reviewed by a second person, on every relationship-type conversion.
  5. How to recreate old inherited access after fixing to Private? → Sharing rules or manual sharing recreating the access pattern the "Controlled by Parent" delegation used to provide.

INCIDENT 8 — THE REPORT THAT FORGOT HOW TO COUNT

The problem restated

Migrating telemetry to a Big Object broke a roll-up, a trigger-based aggregation, and reporting/dashboards. Explain why each broke, what Big Objects are/aren't for, and the replacement designs.

Model answer (2-min interview version)

  • Roll-up broke: roll-up summaries are Master-Detail-only; Big Objects cannot be the detail side of a Master-Detail relationship at all — the relationship/roll-up has nothing valid to compute from, and it freezes.
  • Trigger broke: Big Objects write via Database.insertImmediate() (sync, small batches) or Database.insertAsync() (async), not ordinary DML, and Apex trigger support on Big Objects is materially limited relative to standard-object trigger lifecycle — the old "on insert, cascade an update" pattern doesn't carry over.
  • Reporting broke: Big Objects are queried via SOQL on their declared indexed fields only, and classic Report Builder/dashboards don't natively support Big Objects — no direct re-pointing is possible.
  • What Big Objects are for: massive-scale (hundreds of millions to billions of rows), largely historical/append-heavy data (audit trails, IoT/event archives) with async/indexed querying — traded deliberately against live triggers, roll-ups, and ad hoc reporting. Not a drop-in replacement for "a custom object with a lot of rows."
  • Replacements: (1) a scheduled Apex batch job querying indexed fields, aggregating, and writing to a plain field on Account (periodic, not live); (2) real-time detection moved to ingestion time via Platform Events (subscribe to the event stream, not a Big Object trigger); (3) a dedicated summary/aggregate standard object populated by the batch job, purpose-built for dashboards/reports, while the Big Object stays the system of record for raw detail.

Self-grade checklist

  • Explained roll-up break (MD-only feature, Big Objects can't be detail side)
  • Explained trigger break (insertImmediate/insertAsync, limited trigger lifecycle)
  • Explained reporting break (indexed-field-only queries, no classic Report Builder support)
  • Stated what Big Objects are/aren't designed for
  • Gave all three replacement designs (batch aggregation, Platform Events, summary object)

THE REDO — model answer

Roll-up: MD-only feature; Big Objects can't be MD detail → frozen
Trigger: insertImmediate/insertAsync write paths, limited trigger lifecycle → old pattern doesn't run
Reporting: indexed-field-only SOQL, no classic Report Builder support → no direct re-point
Big Objects = massive-scale historical/append data, async/indexed access — not a drop-in custom-object replacement
Fixes: scheduled batch → summary field; Platform Events at ingestion for real-time; dedicated summary object for dashboards

RETRIEVAL DRILL — model answers

  1. Can a Big Object be the detail side of Master-Detail? → No.
  2. Two DML methods for Big Objects?Database.insertImmediate() and Database.insertAsync().
  3. How must you query a Big Object? → Via SOQL on its declared indexed fields.
  4. Ingestion-time replacement for a real-time Big-Object trigger need? → Publish a Platform Event at ingestion time; subscribe to the event stream.
  5. What should carry dashboard/report logic? → A dedicated summary/aggregate standard object populated by a scheduled batch job, not the Big Object itself.

🏆 CAPSTONE — THE ERD NOBODY DREW (model report)

Ticket-by-ticket mapping

TicketMaps toRoot causeFix
A (Commission_Split cascade)Incident 1 (multi-generation cascade)Nobody had documented whether Commission_Split__c was MD off OpportunityContactRole or off Opportunity directly — the "shared Contact Role" confusion is itself evidence of missing ERDConfirm actual relationship via Schema Builder/describe; add a before-delete guard on Opportunity/OpportunityContactRole surfacing dependent Commission_Split counts; document the true chain
B (Total Pipeline roll-up reading zero)Not Incident 2's mechanism — a parallel-field drift incident: the real Master-Detail AccountId relationship was never converted and still computes correctly, but a new unrelated Lookup field (Account_Snapshot__c) was introduced and business process/workflows silently started relying on it instead, while new Opportunities stopped getting AccountId populated the way the roll-up needsProcess/data drift, not a relationship-type bug — the roll-up's real source field was abandoned in practice without anyone noticingData audit reconciling AccountId vs Account_Snapshot__c per record; decide the canonical field; fix data-entry/automation to keep the real MD AccountId populated (or formally migrate the roll-up logic if Account_Snapshot__c should truly become canonical, in which case it requires an actual field/process redesign, not a silent shadow-field takeover)
C (garbage company names in Task dashboard)Incident 4 (polymorphic WhatId)What.Name queried blindly across a polymorphic field; nulls/wrong data for types without a Name field (e.g., Case)TYPEOF query or Apex runtime-type branching; fix any hardcoded URL/display assumptions tied to one type
D (212 duplicate Accounts, missing Opportunities)Incident 5 (Lead conversion) — with an added distinct defectTwo separate defects: "Create Opportunity" checkbox defaults unchecked (a Setup configuration gap causing missing Opportunities) AND Account duplicate rule is Alert-only (allowing the duplicate Accounts)Flip/enforce the Opportunity-creation decision explicitly in a custom conversion flow (never leave it to an easily-skipped checkbox default); change duplicate rule to Block + fuzzy Name/Website matching; Database.merge() cleanup of the 212 existing duplicates
E (Big Object nightly batch throws no errors, field never updates)Incident 8 cousin (Big Object indexed-query requirement)The batch job filters WHERE Account_Id__c = :acctId, but Account_Id__c is not one of the Big Object's declared indexed fields — the query either silently returns nothing useful or the intended filter never behaves as expected against non-indexed criteriaBig Object index fields are fixed at schema-design time and generally not alterable after the fact — likely requires a schema redesign/re-migration of the Big Object including Account_Id__c in its index definition; in the interim, query using the actual indexed field(s) and post-filter in Apex if volume allows
F (Vendor_Contract__c Public Read/Write)Incident 7 (OWD flip on MD→Lookup conversion)The Vendor_Contract__c → Vendor__c relationship conversion (Master-Detail → Lookup, for cross-vendor reassignment) silently defaulted OWD to Public Read/Write because "Controlled by Parent" no longer applied, and nobody ran a post-conversion OWD reviewSet OWD to Private + sharing rules recreating the intended access; add the mandatory OWD before/after review step to the relationship-conversion checklist going forward

Prioritized punch list

  • Tonight (compliance/security risk, stop the bleeding): F — fix the Public Read/Write OWD on Vendor_Contract__c immediately; it's an active audit finding with real financial-data exposure.
  • This week (broken financial/business-critical data): A — confirm the real relationship chain and add the delete guard before finance loses more commission data; E — fix or work around the non-indexed Big Object query so the Account_Total_Calls aggregation resumes; D — flip the duplicate-rule action to Block + fuzzy matching and force the Opportunity-creation decision in the conversion flow (stops the bleeding on new conversions immediately, even before the cleanup batch runs).
  • This week/quarter (data-quality drift, less urgent but compounding): B — run the AccountId vs Account_Snapshot__c reconciliation audit and decide canonical field; C — fix the TYPEOF/type-branching in the Task dashboard (cosmetic but erodes user trust in the platform generally); D cleanup — Database.merge() the 212 existing duplicate Accounts.
  • This quarter (governance): stand up the ERD/data-dictionary discipline and the standing relationship-conversion checklist described below, so none of these six recur.

The ERD / documentation proposal

Every custom object needs a maintained record, in Schema Builder plus a living data dictionary, capturing: (1) relationship type (MD vs Lookup) and cardinality for every custom relationship; (2) current OWD, and whether it's "Controlled by Parent" (and therefore contingent on the relationship staying Master-Detail); (3) every roll-up summary field and its exact source relationship; (4) the cascade-delete blast radius (how many generations deep a delete on this object propagates); (5) for Big Objects specifically, the declared indexed fields and the intended query patterns they were designed for. This turns "nobody remembers why this is Master-Detail" into a thirty-second lookup instead of a forensic investigation.

A standing relationship-conversion checklist (mandatory before any MD↔Lookup conversion ships):

  1. Roll-up dependency check — does anything depend on this relationship for aggregation?
  2. OWD check — what is it before, what will it become after, who reviewed the after-state?
  3. Cascade-delete blast-radius check — how many generations of dependents exist, and do any need protection?
  4. Reparenting-requirement check — could "Allow Reparenting" solve the business need without a full conversion?

The 3 verification steps (before declaring any ticket "closed")

  1. Schema confirmation — re-verify via Schema Builder/describe that the fixed relationship, OWD, or index configuration is exactly what you intended, not just what you assume you deployed.
  2. Data reconciliation — for B, D, and A specifically, run a query comparing expected vs actual state (roll-up value vs live aggregate; duplicate count; dependent-record count) before and after the fix, not just "no error thrown."
  3. Process/documentation update — update the new ERD/data-dictionary entry and the relationship-conversion checklist itself with what this ticket taught, so the next engineer inherits the answer instead of the mystery.

The 2-minute answer (say out loud)

"All six tickets are the same root cause: nobody had documented the data model, so every relationship-type decision and its side effects were invisible until something broke. Ticket F is the most urgent — an OWD silently flipped to Public Read/Write during a Master-Detail-to-Lookup conversion, which is a well-known but rarely-checked side effect; I'm fixing that tonight. A, D, and E are broken financial and operational reporting from cascade-delete blast radius, Lead-conversion gaps, and a Big Object query on a non-indexed field — those get fixed this week with guards, rule changes, and a schema check. B and C are data-quality and cosmetic drift from a shadow field taking over trust silently, and a polymorphic query treated like an ordinary lookup — fixed this week too. The real deliverable, though, is the ERD and the standing relationship-conversion checklist, because every one of these six tickets would have been a thirty-second lookup instead of a months-long mystery if the schema had been documented and every conversion had gone through a roll-up/OWD/cascade/reparenting checklist before shipping."


KNOWLEDGE SPINE — rapid-fire (model answers)

  1. Max Master-Detail relationships per object? → 2.
  2. Does Master-Detail cascade delete stop at one generation? → No — transitive through every MD generation.
  3. Junction-object cascade-delete cap? → Blocked above 200 records unless the setting is raised.
  4. Can Lookup have a native roll-up summary? → No — roll-up summaries are Master-Detail-only; Lookup needs Apex/Flow/DLRS.
  5. What blocks MD→Lookup conversion? → A dependent roll-up summary field.
  6. What blocks Lookup→MD conversion? → The lookup field not being populated on all existing records.
  7. What does converting MD→Lookup do to OWD? → "Controlled by Parent" becomes invalid; the platform defaults to Public Read/Write.
  8. Parent→child SOQL subquery nesting limit? → One level.
  9. Child→parent dot-notation depth limit? → Not capped the same way (commonly cited up to 5 for standard relationship chains).
  10. What does TYPEOF solve? → Branching field selection by a polymorphic field's actual referenced type in one query.
  11. Name two polymorphic standard fields. → WhatId, WhoId (also OwnerId on some objects).
  12. What does standard Lead conversion create? → Account, Contact, and optionally Opportunity.
  13. Does Lead conversion auto-dedupe Accounts? → No — depends entirely on configured duplicate/matching rules.
  14. Correct way to fix existing duplicate Accounts? → Database.merge() (up to 3 records) — never delete.
  15. Recycle bin retention? → Up to 15 days, subject to storage eviction.
  16. Safe order to undelete a Master-Detail parent + child? → Master first, then detail.
  17. Can a Big Object be the detail side of Master-Detail? → No.
  18. How must you query a Big Object? → Via SOQL on its declared indexed fields.
  19. Do Big Objects support standard Apex triggers the same way? → No — limited support; writes use insertImmediate/insertAsync.
  20. What replaces a roll-up when the source becomes Lookup or a Big Object? → Apex trigger, Flow, or DLRS (roll-up); scheduled batch aggregation (Big Object).

INTERLEAVED PRACTICE SET — model answers

  1. Trap hunt: (a) MD cascade fan-out through 3 generations (Incident 1); (b) roll-up frozen after a blocked conversion path was silently cleared (Incident 2); (c) triple-nested subquery hitting the 1-level cap (Incident 3); (d) What.Name nulling for Case (Incident 4); (e) Lead conversion creating a duplicate Account with zero fuzzy matching (Incident 5).
  2. Design (2 min): a junction object Project_Resource__c with two Master-Detail relationships (to Project__c and to Resource__c) modeling many-to-many assignment; note the 200-record cascade cap risk if either parent is bulk-deleted, and add a before-delete guard on both parents.
  3. Conversion bridge: name both conversion-blocking rules (roll-up dependency for MD→Lookup; 100%-populated lookup for Lookup→MD) and the OWD side effect that survives neither direction cleanly without an explicit review step.
  4. Big Object bridge: name the three standard-object features that silently stop working after a Big Object migration (roll-up, trigger lifecycle, classic reporting) and their three replacements (batch aggregation, Platform Events, summary object).
  5. One-card answer (5 bullets + incident map): (1) MD vs Lookup is a contract, not a preference — ownership, cascade, sharing, aggregation all differ (I1/I2); (2) conversions have silent side effects — roll-up dependency blocks, OWD flips to Public Read/Write (I2/I7); (3) SOQL relationship traversal is asymmetric — 1-level parent→child subquery cap, multi-level child→parent dot notation (I3); polymorphic fields need TYPEOF (I4); (4) Lead conversion has zero built-in dedupe — duplicate rules must be Block + fuzzy, and merge (not delete) fixes existing duplicates (I5); delete/undelete order matters and isn't a full safety net (I6); (5) Big Objects trade standard-object conveniences for scale — plan index fields and replacement architecture before migrating (I8).

THE ONE-CARD ANSWER KEY (carry this)

"Master-Detail vs Lookup — when would you use each, and what should I watch out for?" — 5 lines:

  1. The contract: MD = required parent, cascade delete (transitively, through every generation), Controlled-by-Parent OWD, native roll-up summaries, max 2 per object, no reparenting by default. Lookup = optional, no cascade, independent sharing, reparentable, no native roll-up.
  2. Conversion is not free: MD→Lookup is blocked by a dependent roll-up (and afterward, OWD silently defaults to Public Read/Write — the side effect almost nobody mentions unprompted); Lookup→MD is blocked unless the lookup is populated on every record.
  3. Roll-up workarounds on Lookup: Apex trigger, record-triggered Flow, or DLRS (Declarative Lookup Rollup Summaries) — name DLRS for the bonus point.
  4. Delete/undelete discipline: cascade is transitive and capped at 200 junction-child records without an override; recycle bin is a 15-day soft net; restoring a detail deleted before its master is fragile — master-first, and even then not guaranteed.
  5. Query and scale edges: parent→child SOQL subqueries nest one level deep (child→parent dot notation doesn't); polymorphic fields (WhatId/WhoId/OwnerId) need TYPEOF or type-branching; Big Objects trade roll-ups/triggers/classic reporting for massive-scale, indexed-only querying.

Numbers to say cold: max 2 MD per object · cascade cap 200 (junction children) · recycle bin 15 days · SOQL parent→child subquery = 1 level · child→parent dot notation ≈ 5 levels (standard) · Database.merge() ≤ 3 records · MD→Lookup OWD default after conversion = Public Read/Write.

On this page

INCIDENT 1 — THE DELETE THAT TOOK THE BUILDING DOWNThe problem restatedModel answer (2-min interview version)Self-grade checklistTHE REDO — model answerRETRIEVAL DRILL — model answersINCIDENT 2 — THE ROLL-UP THAT WENT DARKThe problem restatedModel answer (2-min interview version)Self-grade checklistTHE REDO — model answerRETRIEVAL DRILL — model answersINCIDENT 3 — THE QUERY THAT COULDN'T SEE ITS GRANDCHILDRENThe problem restatedModel answer (2-min interview version)Self-grade checklistTHE REDO — model answerRETRIEVAL DRILL — model answersINCIDENT 4 — THE TASK THAT POINTED AT THE WRONG TYPEThe problem restatedModel answer (2-min interview version)Self-grade checklistTHE REDO — model answerRETRIEVAL DRILL — model answersINCIDENT 5 — THE LEAD THAT CLONED ITSELFThe problem restatedModel answer (2-min interview version)Self-grade checklistTHE REDO — model answerRETRIEVAL DRILL — model answersINCIDENT 6 — THE RECYCLE BIN THAT COULDN'T GIVE IT BACKThe problem restatedModel answer (2-min interview version)Self-grade checklistTHE REDO — model answerRETRIEVAL DRILL — model answersINCIDENT 7 — THE OWD THAT FLIPPED ITSELFThe problem restatedModel answer (2-min interview version)Self-grade checklistTHE REDO — model answerRETRIEVAL DRILL — model answersINCIDENT 8 — THE REPORT THAT FORGOT HOW TO COUNTThe problem restatedModel answer (2-min interview version)Self-grade checklistTHE REDO — model answerRETRIEVAL DRILL — model answers🏆 CAPSTONE — THE ERD NOBODY DREW (model report)Ticket-by-ticket mappingPrioritized punch listThe ERD / documentation proposalThe 3 verification steps (before declaring any ticket "closed")The 2-minute answer (say out loud)KNOWLEDGE SPINE — rapid-fire (model answers)INTERLEAVED PRACTICE SET — model answersTHE ONE-CARD ANSWER KEY (carry this)