Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
Interview prep

Flow interview questions and answers

Entry criteria, bulkification, fault paths and Flow versus Apex.

12 questions · 4 of them scenarios

Junior round

Junior Flow questions

Flow junior

When do you use a before-save record-triggered flow instead of after-save?

Testing Whether you reach for the cheap one by default, or use after-save for everything because it can do more.

junior

Before-save when all I need is to set a field on the record being saved, because it happens before the record is written and costs no extra DML. After-save when I need anything else — creating or updating a related record, sending an email, calling Apex. The rule I follow is to try before-save first and only move to after-save when the requirement genuinely reaches outside the record.

mid

Before-save updates the record in memory on its way to the database, so there is no second save and no second pass through the automation. That makes it dramatically cheaper than the after-save equivalent, which had to issue its own DML and re-enter the order of execution to change the same field. What it cannot do is the whole reason after-save exists: no related records, no callouts, no email, no Apex actions, and on an insert there is no record Id yet because the row does not exist. So the split is not about preference — anything that only touches Trigger.new-equivalent fields belongs in before-save, and everything else has no choice.

Deep dive on this
Flow junior scenario

A flow that works on one record fails on a data load with a query limit error. Why?

Testing Where you look first when something works by hand and fails in bulk.

What they tell you

A record-triggered flow on Case loops over related Case Comments and does a Get Records inside the loop to look up a related Contact. It works when a user saves one Case. A 200-record load fails with too many SOQL queries.

junior

A Get Records inside a loop runs once per iteration, so one Case with ten comments is ten queries and two hundred Cases is far past the 100 query limit. Saving one record hides it because ten is under the limit. The fix is to move the Get outside the loop, fetch everything the flow needs in one go, and use a collection to look up what each iteration wants.

mid

Working by hand and failing in bulk almost always means something is inside a loop that should be outside it. The platform bulkifies elements across the records in a transaction — one Get Records at the top level costs one query for all 200 Cases — but it cannot do that for an element inside a loop, because each iteration is a separate execution with its own filter. So the fix is one Get before the loop, retrieving the Contacts for every Case in scope, then loop over the collection in memory. I would also check the flow for Update elements inside loops, which is the same bug against the 150 DML limit and usually sits right next to it.

Ask before you answer
  • Is the Get Records element inside the loop, or before it?
  • How many related records does a typical Case have, and is the loop iterating those?
Do not say this

Add an entry condition so the flow only runs for a smaller set of Cases.

That reduces how often the bug fires without removing it, so the same load succeeds this month and fails when volumes rise. The query in the loop is the defect, and it stays there waiting.

Deep dive on this
Flow junior

Which flow types are there, and how do you pick one?

Testing How you frame the choice — by what starts the flow, or by a memorised list.

junior

I pick by what starts it. A screen flow when a person walks through steps. A record-triggered flow when a record is created or changed. A scheduled-triggered flow when it runs on a timetable over a set of records. A platform event-triggered flow when a message arrives. An autolaunched flow when something else calls it — Apex, another flow, a process. The trigger is the decision; everything inside is the same builder.

mid

The trigger picks the type, and then two consequences follow that people hit later. Context is one: a screen flow runs as the user, with their sharing and field access, while a record-triggered or scheduled flow runs in system context and will happily update records the user could never see. That is a security decision made by choosing a flow type, which is worth saying out loud in a design review. The other is entry points — an autolaunched flow is the reusable unit, callable from Apex with Flow.Interview or from another flow as a subflow, so anything I expect to use twice gets built as one of those rather than duplicated into two triggered flows.

Deep dive on this
Mid-level round

Mid-level Flow questions

Flow mid

What does the platform bulkify in a flow, and what is still on you?

Testing That bulkification is partly automatic here. Plenty of people assume it is all automatic, or none of it.

mid

The platform combines the same element across the interviews running in one transaction. Two hundred Cases saved together means 200 interviews, and one Get Records at the top level of the flow costs one query across all of them, not 200. Same for Create and Update. What it cannot combine is anything inside a loop, because those executions are sequential rather than parallel — so a Get or an Update inside a loop is one query or one DML per iteration, per record, and that is where the 100 and 150 limits go.

senior

Knowing the platform does half the work changes how I read a flow. The pattern that survives volume is: get everything once at the top, loop over collections in memory, add the records I want to change to a collection variable, and do one Update after the loop with that collection. That flow costs a fixed number of queries and one DML no matter how many records the transaction carries. The version that fails is structurally identical except the Update sits inside the loop, and it will pass every manual test anybody runs. Two things I watch for beyond the obvious. Related-record lookups are often better expressed by traversing the relationship in a formula than by a Get at all, which removes the query rather than moving it. And the 2,000 executed elements per interview is a real ceiling on a loop over a big collection — a flow looping 500 times through four elements is already at it, and that is a design to rethink rather than a limit to work around.

Deep dive on this
Flow mid

What happens when a flow element fails and there is no fault path?

Testing Which side of the failure you describe — the user's, or the administrator's.

mid

The transaction rolls back and the flow's error handling takes over: a screen flow shows the user an unhelpful message about an unhandled fault, and an email goes to whoever the flow's error recipient is, which by default is the last person to modify it. So the user sees nothing they can act on and a developer gets an email about a record they have never heard of. A fault connector on the element lets me decide instead — show a real message, log a record carrying $Flow.FaultMessage, or take a different route.

senior

The default behaviour is worth being precise about, because it produces two bad outcomes rather than one. The user gets a generic fault message and their work is gone, since the rollback took the whole transaction with it. And the notification goes to the flow's last modifier by default, which means error handling in an org is quietly assigned to whoever touched the flow most recently — I have seen a leaver's mailbox be the only place a failure was reported for months. So on anything user-facing I put a fault path on the elements that talk to the database, show a message the user can act on, and write a log record with $Flow.FaultMessage and the record Id so somebody can find it later. The judgement call is whether to swallow the error or re-raise it: for a screen flow, degrading gracefully is usually right; for a record-triggered flow doing something financial, I would rather the save fail loudly than succeed with half the work done.

Deep dive on this
Flow mid

Where do you draw the line between Flow and Apex?

Testing What you say when the honest answer is "it depends" — whether you can name what it depends on.

mid

Flow for anything an administrator should be able to change without a release, which is most field updates, most record creation, and most approval-shaped logic. Apex when the requirement is something Flow cannot do or does badly: a callout with retry handling, a batch job over 2,000,000 records, complex collection work, or logic that needs real unit tests. The tiebreaker I use is who will maintain it — a flow nobody in the business can read has lost the main advantage of being a flow.

senior

I think about it as a testing and change-cost question rather than a capability one, because the capability line keeps moving and the other two do not. Apex gets version control, code review, and tests that fail a deployment when someone breaks it. A flow gets flow tests, which are real but thinner, and a diff that is genuinely hard to read in a pull request. So anything with money, entitlement or compliance in it I would rather have in Apex where the test asserts the rule. On the other side, a flow that an administrator maintains removes a developer from a loop they add no value to, and that is worth accepting a weaker test story for. Where I have seen it go wrong is the middle: a flow with 40 elements and eight decisions is past the point where anyone can reason about it, and past the point where Flow's advantages exist at all — it should have become Apex several requirements ago, and the reason it did not is that each requirement was small.

Deep dive on this
Flow mid scenario

A scheduled flow did not run last night. How do you find out why?

Testing What you check before assuming the platform failed.

What they tell you

A scheduled-triggered flow runs nightly over Opportunities past their close date. It ran for three weeks and then produced nothing on Monday morning. No error email arrived and the flow is still active.

mid

First I would separate two possibilities, because no error email is consistent with both: it ran and its entry conditions matched nothing, or it never ran. Scheduled Jobs in Setup tells me whether the job exists and when it next fires, and querying FlowInterview tells me whether an interview started and died. If it never ran, the usual cause is a deployment — saving a new version or deploying the flow can drop the schedule, and the flow stays active while nothing is scheduled, which is exactly this symptom.

senior

The absence of an error email is the most informative part of the report, because a failing flow emails somebody. Silence points at either nothing matching or nothing running, so I would establish which before touching anything. The CronTrigger rows behind Scheduled Jobs are the fastest check, and if there is no entry then the schedule is gone and the question becomes what changed on Friday — a deployment is the usual answer, and that is worth confirming because it means every other scheduled flow in that deployment is in the same state. If it did run, then the entry criteria are the suspect and I would run the same filter as a report to see the count. Two things I would add afterwards rather than just fixing it: a scheduled flow that finds nothing and a scheduled flow that never fires should not look the same from outside, so writing a heartbeat record on each run makes the difference visible in a report; and the deployment process needs a check for it, because a schedule that silently does not survive a release will do this again.

Ask before you answer
  • Did it run and find no records, or not run at all — is there a scheduled job entry for it?
  • Did anything about the flow get saved recently, and does the schedule still show the same start time?
Do not say this

Deactivate and reactivate the flow to reset the schedule, then wait for tonight.

That very often makes it work again and destroys the evidence. You lose the ability to tell "ran and matched nothing" from "was never scheduled", and those have completely different fixes — one is the entry criteria, the other is a deployment that dropped the schedule.

Deep dive on this
Flow mid

When do you break a flow into subflows?

Testing The reason you give for splitting. Reuse is the obvious one and rarely the real one.

mid

When the same logic is needed in two places, which is the reuse case, and when a single flow has grown past what somebody can read on one screen, which is the comprehension case. The second one comes up more often. A subflow is an autolaunched flow with input and output variables — the same unit Apex calls through Flow.Interview — so it also gives me a boundary I can test on its own: the parent passes values in, the subflow does one job, and I can reason about each half without holding the other in my head.

senior

I split on responsibility rather than on size, because a flow cut in half at an arbitrary point is harder to follow than the original. The signal I look for is a section that could be described in one sentence without mentioning the rest — validate this order, calculate this discount, notify these people. That becomes a subflow with named inputs, and the parent reads as a sequence of intentions. The costs are real and worth naming: debugging crosses a boundary, so the debug log is now two flows deep; a change to a subflow's interface breaks every caller silently until you look; and a subflow called inside a loop is still inside a loop, so splitting does nothing for bulkification and can make it harder to see that a Get Records is running once per iteration against the same 100 query budget. What I would not do is split for reuse that has not happened yet — one caller and a speculative interface is two things to maintain instead of one.

Senior round

Senior Flow questions

Flow senior scenario

A nightly load went from 20 minutes to four hours after a flow was added. How do you approach it?

Testing How you separate the load's problem from the flow's problem before proposing either fix.

What they tell you

A record-triggered after-save flow was added to Account last month. The nightly integration load of 80,000 Accounts now takes four hours and occasionally fails. The flow updates a field on the Account and creates a task when a rating changes.

mid

An after-save flow updating the triggering record causes a second save, and the second save re-runs every trigger and flow on the object — so 80,000 records became 160,000 passes through the automation layer. Moving that field update into a before-save flow removes the second save entirely, which is usually most of the four hours. The task creation has to stay after-save, but I would check the entry criteria first, because a condition that matches every record is doing 80,000 DML operations for a rule meant to fire occasionally.

senior

I would measure before changing anything, because four hours across 80,000 records could be the flow, the load's batch size, or lock contention, and the fixes do not overlap. Assuming it is the flow, the structure of it tells me a lot: the field update on the triggering record is a second save per record and that is the expensive half, so splitting the flow in two — before-save for the field, after-save for the task — is the change with the best ratio of effort to result. Then the entry criteria, because a flow that runs on every record to do something for a few is spending the whole cost for a fraction of the value, and tightening the condition is free. What I would raise separately is whether this rule should apply to integration data at all. Bypassing the integration user is a common answer and it is a change to what the org means, not a tuning knob, so it belongs with whoever owns the process rather than in a performance ticket. And I would want the load itself instrumented afterwards, since the reason nobody noticed for a month is that nothing was watching the duration.

Ask before you answer
  • Does the flow update the triggering Account itself, and is that field one the load also writes?
  • How many of the 80,000 records actually match the rating-changed condition?
Do not say this

Add a bypass so the flow skips records created by the integration user.

That restores the load time and quietly means the business rule does not apply to most of the data flowing into the org. It is sometimes the right answer, and it is a decision about correctness that should be made deliberately rather than as a performance fix.

Flow senior

You inherit an org with 40 active flows on Opportunity. What do you do first?

Testing Willingness to spend time understanding before consolidating. The instinct to rebuild is the expensive one.

mid

I would map before changing anything: which of the 40 are actually firing, on what criteria, and in what order. Trigger Order values tell me the ones somebody thought about; the rest are unordered and running in whatever sequence the platform picks. Then I would look for the cheap wins — after-save flows doing nothing but setting a field on the triggering record can become before-save, which removes a whole extra save each, and duplicated criteria across two flows usually means one of them is dead.

senior

The first job is finding out what is real, because in an org like this a meaningful share of those 40 are inactive in practice: criteria that no longer match, flows built for a process that was retired, and two flows doing the same thing where one was never switched off. Debug logs on a real save tell me which ones actually run, and that list is usually much shorter than 40. Then I would consolidate by object and by timing rather than wholesale — one before-save flow and one after-save flow per object is the target shape, because it makes order explicit rather than emergent, and each merge is independently testable. What I would resist is a rewrite. A single new flow replacing 40 is one deployment that changes every Opportunity behaviour at once, with no way to attribute a regression, and the business knowledge encoded in those criteria is not written down anywhere else. Merging two at a time is slower and it is the version that can be reversed. And I would fix Trigger Order early even before merging, because unordered automation means today's behaviour is not guaranteed to be tomorrow's.

Deep dive on this
Flow senior scenario

Two flows set the same field and the winner changes between saves. How do you fix it?

Testing That unordered automation has no correct behaviour to restore — only one to choose.

What they tell you

Two after-save record-triggered flows on Opportunity both set Priority__c. Neither has a Trigger Order value. Users report the field showing different values for what looks like the same edit, and it is not reproducible on demand.

mid

Without Trigger Order the sequence is not guaranteed, so what users are seeing is genuinely non-deterministic rather than a bug with a reproduction. The immediate step is to make it deterministic — set Trigger Order on both so at least the behaviour is stable and describable. But the real fix is that one field should have one owner: the conditions from both flows belong in a single decision in one flow that owns Priority__c, and the other stops touching the field. Otherwise I have stabilised a design that will produce the same class of bug again.

senior

The thing I would establish first is what the field is supposed to say, because there is no correct current behaviour to restore. Two flows with overlapping criteria means two people encoded two rules for one field at different times, and the answer is a product decision rather than a technical one — I would take both sets of conditions to whoever owns the process and get one rule out of it. Then the implementation is straightforward: one flow owns Priority__c, with the merged conditions in a single decision, and it is a before-save flow if it only touches the triggering record, which also removes the extra save both flows are currently causing. I would set Trigger Order across every flow on the object while I am there, because unordered automation is a latent version of this bug on every field, not just this one. And I would look for the second-save interaction, since two after-save flows each writing the record can re-trigger each other, and that turns an ordering problem into a limits problem at volume.

Ask before you answer
  • Do the two flows have overlapping entry criteria, or is one meant to supersede the other under a condition nobody wrote down?
  • Is either of them also updating the triggering record, so a second save is re-running both?
Do not say this

Set Trigger Order on both so the important one runs second and wins.

It makes the symptom stop, which is worth something, and it encodes a race as a design. Two flows writing one field means the rule for that field lives in two places, and the next person adding a third condition has to find both and reason about ordering to get it right.

Flow senior

Where do flows sit relative to triggers in the save order, and why does it matter?

Testing How you use the save order — as a diagram to recite, or as a way to predict what re-runs.

mid

Before-save flows run early, alongside the before triggers, before the record is written. After-save flows run after the record is committed to the database and after the after triggers, in the same region as the old workflow field updates. That ordering is why an after-save flow updating the triggering record causes a second save, and the second save re-enters the before and after triggers — which is how a handler written to read Trigger.new once ends up running twice.

senior

I use it to answer two questions rather than to recite the list: has this record got an Id yet, and can this step re-enter my code. Before-save has no Id on insert and cannot re-enter anything, which is what makes it cheap. After-save has an Id and can re-enter, so anything it writes back to the triggering record starts a second pass through the whole trigger and flow layer — and that second pass begins with most of the transaction's 100 queries and 150 DML statements already spent, which is why a limit error usually surfaces there rather than on the first pass. The practical consequences are that mixing Apex triggers and after-save flows on the same object means two teams' code can re-enter each other in an order neither controls, and that debugging it starts from the log rather than the diagram: counting the entries for a handler tells me how many passes happened, and the diagram only tells me what should happen in one.

Deep dive on this