This page is for anyone preparing for a Power Automate round, whether you build flows inside a business team or work as a Power Platform developer. Most interviews start with flow types, triggers and connectors, then test expressions and how you handle errors with run after and scopes. They move on to approvals, SharePoint, Excel and Dataverse, and finish with environments, solutions and a flow that failed without anyone noticing. Each question shows what the interviewer is really checking, the shape of a strong answer and a short answer you can say out loud. Licensing rules change, so check the current terms before you quote them.
Search all questions by round, difficulty and level, or save the ones you want to practise.
Cloud flow: runs in the cloud and talks to systems through connectors and their APIs.
Desktop flow: built in Power Automate for desktop; drives the screens of a Windows machine the way a person would.
Choice: a connector whenever one exists; a desktop flow only for apps with no API, usually started from a cloud flow.
"A cloud flow runs in the cloud and works through connectors, so when I say create a SharePoint item or send an Outlook email, it's calling that service's API. No machine of mine has to be switched on. A desktop flow is built in Power Automate for desktop and runs on a Windows machine. It automates the screen itself: opening an old desktop app, clicking buttons, reading fields, like a person would. My rule is simple. If there's a connector or an API, I use a cloud flow, because it's faster and it doesn't break when someone moves a button. I only reach for a desktop flow when the system has no API, like an old finance app. Even then, I usually start it from a cloud flow, so the trigger, approvals and error handling stay in the cloud and the desktop part only does the clicking."
Choosing a desktop flow for a system that already has a connector, or not knowing a desktop flow needs a Windows machine to run on.
Automated: starts on an event in another system, like a new email or a new list item.
Instant: starts when a person asks, from a button, a selected item or a Power Apps screen.
Scheduled: starts on a clock, like every weekday morning.
"The difference is what starts the flow. An automated flow starts on an event somewhere else, like a new email arriving in a shared mailbox or an item being created in a SharePoint list. An instant flow starts when someone asks for it: tapping a button in the mobile app, selecting a SharePoint item and running the flow on it, or pressing a button in a Power Apps screen. A scheduled flow runs on a clock, say every weekday at seven in the morning. For examples, I'd use an automated flow to save invoice attachments from the accounts mailbox into a document library. An instant flow could let a manager pick a request and send it for approval. And a scheduled flow could email each team a morning list of overdue tasks. Picking the right one matters, because a scheduled flow that checks a whole list every few minutes wastes runs when an event trigger would start on exactly the right item."
Building a scheduled flow that scans a whole list every few minutes when an event trigger would do the job.
What: a cloud flow another flow calls with Run a Child Flow, passing inputs and getting outputs back.
Why: shared logic fixed once, and big flows kept readable.
Setup: both flows in the same solution; child starts with the manual trigger and ends with a respond action.
Gotcha: the child must use its own fixed connections, not ones provided by the run-only user.
"A child flow is a cloud flow that another flow calls, a bit like a function. The parent uses the Run a Child Flow action, passes inputs, and gets outputs back. I use them when the same logic shows up in several flows, like looking up someone's manager or writing to an error log, so I fix it in one place. They also keep a big flow readable. For the setup, both flows have to live in the same solution. The child starts with the manual trigger, where I define its inputs, and it ends with a respond action that returns the outputs to the parent. The gotcha people hit is connections. In the child's run-only settings, its connections need to be set to use a fixed connection rather than one provided by the run-only user, or the parent's call fails."
Copying the same twenty steps into ten flows, then fixing a bug in only some of them.
Trigger condition: an expression in the trigger settings that must be true or the flow never starts.
Why not a Condition: a Condition still starts a run on every edit and fills the history.
Changed vs is: edits to an already approved item still pass, so track the change or use a processed flag.
"I'd use the trigger for when an item is created or modified, and add a trigger condition in its settings. A trigger condition is an expression that has to return true, or the flow doesn't start at all. That beats a Condition as the first action, because with a Condition the flow still runs on every edit, fills the run history with noise and uses up runs. For a choice column, the text sits under the column's Value property, so my condition checks that it equals Approved. But changes to Approved isn't the same as is Approved. If someone later edits the description of an approved item, the condition is still true and the flow runs again. To catch only the change, I'd either use the action that gets the changes for an item since the trigger fired, which needs versioning on, or keep a flag column the flow sets once it has handled the item."
@equals(triggerOutputs()?['body/Status/Value'], 'Approved')
Putting a Condition as the first action and calling that a filter, while the flow still starts on every edit.
Stop: turn the flow off before it sends more emails or updates.
Who changed it: a trigger condition that ignores edits made by the flow’s service account.
Flag: a status the flow sets, so its own update no longer matches the condition.
Prove it: edit one item and check exactly one run appears.
"That's the classic loop: the flow's own update counts as a modification, so the trigger fires again. First I'd turn the flow off to stop the damage. Then there are a few fixes. The cleanest is a trigger condition that ignores changes the flow made. If the flow's connection runs as a service account, the condition checks that the Modified By email isn't that account. Another way is a status column: the flow only starts when the status is Submitted, and its own update sets it to Processed, so the second trigger doesn't match. A third is to check which columns actually changed and exit early if it was only the ones the flow writes. Whichever I pick, I test by editing one item once and making sure exactly one run shows up."
Adding a delay at the start of the flow and hoping the loop dies out.
Cause: variables belong to the run, not the iteration, so parallel iterations overwrite each other.
Per-iteration values: use Compose or the loop item directly; each iteration gets its own output.
Counts: work them out after the loop, like the length of a filtered array.
Last resort: set the loop back to sequential, or replace it with Select and Filter array.
"Variables belong to the whole run, not to each iteration. When the loop runs in parallel, several iterations set the same variable at nearly the same moment, so a Set variable that adds one to the old value can read a stale number and the total comes out low. If the loop also stores something like the current ID in a variable and a later step reads it, one iteration can pick up another's value, which explains the double processing. The fix is to keep per-iteration values out of variables. Inside the loop I use Compose, or refer to the loop's current item directly, because each iteration gets its own copy. For counts, I drop the counter and work it out after the loop, like the length of a filtered array. If I truly need the variable, I set the loop back to sequential. And I'd ask whether the loop is needed at all, because Select or Filter array often does it in one step."
Leaving concurrency on and adding delays until the numbers happen to look right.
Standard: everyday services like SharePoint, Outlook, Teams and Excel Online.
Premium: needs a standalone Power Automate license; Dataverse, SQL Server and HTTP are common examples.
Custom: a connector you build around your own API; treated as premium.
Check: one premium action makes the whole flow premium, so confirm current terms with the admin early.
"Standard connectors cover the everyday services, things like SharePoint, Outlook, Teams, OneDrive and Excel Online, and many Microsoft 365 licenses already let people use them in flows. Premium connectors need a standalone Power Automate license, either for the user or for the flow itself. Common examples are Dataverse, SQL Server and the HTTP action. A custom connector is one we build ourselves around an API that has no connector, by describing its endpoints and how it signs in, and custom connectors count as premium too. The practical point is that if one action in a flow is premium, the whole flow needs premium licensing. So before I design, I list the connectors I'll need and who will run the flow. Licensing terms do change, so I confirm the current rules with our admin rather than quote them from memory."
Not knowing HTTP or Dataverse are premium, and finding out after the build that nobody can run the flow.
Connections: each action runs as the account behind its connection, usually the maker.
Instant flows: run-only users can be asked to use their own connections.
Leaver risk: a disabled account means failing connections and a stopped flow.
Prevention: solutions, co-owners, and connection references tied to a service account.
"Each action uses a connection, and a connection is signed in as a specific account. For most automated and scheduled flows, that's whoever created the connection, usually the maker. So an email the flow sends comes from that person, and the flow can only see what that person can see. For instant flows, you can make run-only users supply their own connections, so it acts as whoever pressed the button. The danger is the leaver. When their account is disabled, their connections stop working and the flow starts failing, often before anyone realises why. To avoid that, I build team flows inside solutions, add co-owners, and use connection references signed in with a service account, not a person. If it's already happened, an admin can change the owner, and I rebuild the connections under the service account."
Running business-critical flows under a personal account with no co-owner and assuming someone will notice when they break.
Dynamic content: a picker of outputs from earlier steps; each token is an expression underneath.
Expressions: needed to change, check or combine values, or reach fields the picker hides.
Habit: read a run’s raw outputs first to see the real shape of the data.
"Dynamic content is the picker that shows outputs from earlier steps, like an item's Title or an email's subject. It's handy, but underneath, each of those tokens is just an expression pointing at a field in some step's output. I write an expression myself when I need to change or check the value, not just pass it along. For example, formatting a date, joining a first and last name, taking the first item of an array, checking whether a field is empty, or reading a field the picker doesn't show, like something nested inside a JSON response. A good habit is to open the raw outputs of a real run, so I can see the exact shape of the data before I write the path to it. I also watch the designer, because if I pick a field from an array, it often wraps my action in an Apply to each without asking."
Treating dynamic content and expressions as unrelated, or never having opened the raw outputs of a run.
UTC: utcNow returns UTC, so convert before showing a date to a person.
Functions: addDays for the maths, convertFromUtc with a time zone name and a format.
Format: MM is month and mm is minutes; test in a Compose first.
"Flows work in UTC, so utcNow gives me the current time in UTC. To add days I use addDays, which takes a timestamp and a number of days. The catch is the time zone. If I just format the UTC value, a flow running late in the evening can show tomorrow's date for someone ahead of UTC, or yesterday's for someone behind. So I convert with convertFromUtc, giving it the Windows time zone name and a format string. The format is case sensitive: capital MM is the month and small mm is minutes, which is a classic bug. I'd put this in a Compose step first, so I can see the result in the run history before using it in an email or a file name."
convertFromUtc(addDays(utcNow(), 7), 'W. Europe Standard Time', 'dd-MM-yyyy')
Formatting utcNow for a local user without converting the time zone, or mixing up MM and mm.
Variable: initialised at the top level, then set, incremented or appended; for values that really change.
Compose: holds a value once, can sit anywhere, each loop iteration gets its own output.
Debugging: Compose shows its exact input and output in run history.
"A variable has to be created with Initialize variable at the top level of the flow, not inside a loop or a scope, and after that I can set it, increment it or append to it. So I use a variable when a value genuinely changes during the run, like a counter or a list I build up. Compose just holds a value once. It needs no setup, it can sit anywhere, including inside a loop, and each loop iteration gets its own output. I use it for most things: building a message, a calculated date, a cleaned-up value I want to reuse. It's also my main debugging tool, because the run history shows exactly what went in and what came out. And inside parallel loops Compose is safer, because every iteration shares one variable, while Compose outputs don't clash."
Using a variable for every value by habit, including setting one from inside a parallel loop.
Safe access: write ?[ instead of [ so a missing field gives null instead of an error.
Defaults: coalesce returns the first value that isn’t null.
Empty checks: empty covers blank strings and empty arrays, often inside if.
Check the shape: a field may exist under another name, like a choice column’s Value.
"That error usually means the expression reads a field with plain square brackets, so when the field is missing it throws instead of returning nothing. The fix is the question mark operator: I write question mark, open bracket, instead of just the bracket, at each level of the path. Then a missing field gives null and the flow carries on. Next I decide what null should become. coalesce returns the first value that isn't null, which is perfect for a default, like a team inbox when there's no manager. For blank strings or empty arrays, the empty function is better, often inside an if. I also look at the raw output of a real run, because sometimes the field is there but shaped differently, like a choice column whose text sits under Value. And I keep one Compose per tricky value, so I can see what it resolved to."
coalesce(body('Get_item')?['Manager']?['Email'], 'team-inbox@contoso.com')
if(empty(body('Get_item')?['Notes']), 'No notes', body('Get_item')?['Notes'])
Setting later steps to run even after failure so the error disappears, instead of handling the empty value.
Default: an action runs only if the one before it succeeded.
Statuses: is successful, has failed, is skipped, has timed out; more than one can be ticked.
Skipped: steps after a failure are skipped, so cleanup steps often need that box too.
"By default an action only runs if the step before it succeeded. Configure run after lets me change that. For any action I can choose which outcomes of the previous step let it run: is successful, has failed, is skipped or has timed out, and I can tick more than one. That's the basis of error handling. For example, after a Create item step, I add a Send failure email action set to run only if Create item has failed or has timed out. The one people miss is skipped. When an earlier step fails, the steps after it are skipped, so a cleanup step further down often needs is skipped ticked too, or it never runs. I also rename my actions, because run-after settings and error messages show those names, and Compose 3 tells nobody anything."
Saying the only way to handle an error is to let the flow fail and look at run history later.
Try: a Scope holding the real work.
Catch: a Scope set to run after Try has failed or has timed out; logs and alerts.
Finally: a Scope set to run after Catch in every state, for cleanup.
Stay honest: end the error path with Terminate set to Failed, or the run shows green.
"I use three Scope actions. The Try scope holds the real work. The Catch scope comes next and is set to run after Try has failed or has timed out, so it only runs when something went wrong. Inside it I capture the error, alert the right people, and maybe set the list item's status to Error so a human knows to look. The Finally scope is set to run after Catch whether it succeeded, failed, was skipped or timed out, so it always runs, for cleanup or a run summary. The part people forget is the ending. If Catch handles the error nicely, the run shows as succeeded, and nobody scanning run history sees a problem. So the error path ends with a Terminate action set to Failed. If I have a Finally, that Terminate goes after it, behind a check on a flag Catch sets, because Terminate stops the run on the spot."
A Catch scope that swallows the error, so run history shows green while the work never happened.
result(): returns one entry per action in the scope, with name, status and error.
Filter: keep entries whose status is Failed and take the first.
Alert: flow name, failed action, message and a link to the run built from workflow().
Destination: a shared channel or support mailbox, not one person.
"In the Catch scope I call the result function on the Try scope. It returns an array with one entry for each action directly inside the scope, each with its name, status and error details. I pass that into Filter array and keep only the entries whose status is Failed. The first one gives me the action's name and its error message. Some connectors put the useful detail in the outputs body rather than the error object, so I fall back to that. For the alert, I add the flow's name and a link straight to the failed run, which I build from the workflow function, since it gives me the environment, the flow ID and the run ID. The alert goes to a support channel or shared mailbox, not one person's inbox. And the error path still ends by failing the run, so the history stays honest."
Filter array
From: result('Try')
Where: item()?['status'] is equal to Failed
Compose (failed action)
first(body('Filter_array'))?['name']
Compose (error message)
first(body('Filter_array'))?['error']?['message']
An alert that says only that the flow failed, with no action name, no message and no link to the run.
Meaning: 429 is throttling, 503 is a service briefly unavailable; both are usually temporary.
Policy: set per action; the default retries with growing waits; can be fixed interval or none.
Root cause: fewer calls, lower loop concurrency, filtering at the source.
Still handle: when retries run out, the action fails and run-after logic takes over.
"Both are usually temporary. A 429 means we're being throttled and a 503 means the service is briefly unavailable. Most actions have a retry policy in their settings, and by default they already retry these kinds of errors with exponential backoff, waiting longer between each attempt. For a fussy API I might switch to a fixed interval or change the number of retries. What I don't do is retry everything. A 400 or a 401 won't fix itself, so the answer there is to fix the request or the credentials. For throttling, the better fix is less load: lower the concurrency on the loop that calls the API, filter records at the source so fewer calls are made, or spread the work over time. And when the retries run out, the action fails, so my run-after handling still has to catch it and alert someone."
Wrapping the call in a loop that retries forever with no wait, including on errors that can never succeed.
First to respond: one answer decides it.
Everyone must approve: approved only if all approve; one rejection ends it.
Custom responses: your own options, waiting for one response or for all.
Result: a Condition on the outcome, with responses and comments available for the next steps.
"There are four kinds. Approve or reject, first to respond, where the first approver's answer decides it. Approve or reject, everyone must approve, where it's approved only if all of them approve, and one rejection ends it as rejected. Then custom responses, where I set my own options, like Approve, Send back or Escalate, and choose whether to wait for one response or for all of them. The action pauses the flow until the approval completes, then gives me the outcome and each response, with comments and who answered. After that I add a Condition on the outcome and branch: set the item to Approved, or email the requester with the approver's comments. Approvers can answer from email, from Teams or from the approvals page, and the flow gets the same result either way."
Checking the outcome against the wrong text, like Approved when the value the action returns is Approve.
Limit: a cloud flow run can’t last longer than thirty days.
Timeout: set on the wait action as an ISO 8601 duration, like P3D.
Escalate: a branch that runs only if the wait has timed out: remind, then go to the manager.
Long waits: store status on the item and let a scheduled flow carry on later.
"The limit is that a cloud flow run can't go on for more than thirty days, so if an approval is still waiting then, the run times out and the flow never sees the answer, even if someone approves later. I design so nothing waits that long. I often split the step into Create an approval and Wait for an approval, because that gives me the approval's ID straight away to store on the item. Then I set a timeout on the wait action in its settings, as an ISO 8601 duration, like P3D for three days. After it I add a branch set to run only if the wait has timed out. That branch sends a reminder, or escalates by creating a new request for the approver's manager and noting it on the item. If a process genuinely takes weeks, I record the status on the item, let the run end, and have a scheduled flow pick it up later, instead of one run sitting open for a month."
A single approval step with no timeout, found a month later as a pile of failed runs.
Cause: Get items returns the first hundred items by default, with no warning.
Filter at source: a Filter Query using internal column names, plus a row limit and view.
Pagination: turn it on in settings with a threshold, which has a license-based ceiling.
Large lists: page through in batches by ID or date.
"Get items returns only the first hundred items by default, and it doesn't warn you, so the flow succeeds while quietly ignoring the rest. I'd fix it two ways. First, don't fetch everything. I put a Filter Query on the action so SharePoint does the filtering, for example only items where Status is Pending, and I use internal column names, which can differ from what users see if a column was renamed. I can also set Top Count and pick a view to limit the columns. Second, if I really need more than a hundred, I turn on pagination in the action's settings and set a threshold. That has a ceiling that depends on licensing, so for very big lists I page through in batches, by ID or by date. The Excel action for listing rows has the same trap, with its own default."
Filter Query: Status eq 'Pending' and Created ge '2026-01-01'
Assuming Get items returns every row because the run shows green.
Tables: the connector only sees data formatted as a table.
Rows: listing rows has a default limit; paginate or filter.
Dates: serial numbers unless the DateTime Format option is ISO 8601.
Locking and keys: open files and parallel writes clash; updates need a unique key column.
"The first one is that the connector only works with data formatted as a table, so a plain range of cells is invisible to it. Second, listing the rows in a table returns a limited number by default, so I turn on pagination or filter the rows. Third, dates come back as serial numbers, a five-digit number, unless I set the DateTime Format option to ISO 8601. Fourth, locking. If someone has the file open and editing, or several runs write at once, actions can fail or clash, so I keep write concurrency low and let retries handle short conflicts. Updating or deleting a row also needs a key column with unique values. Honestly, for anything shared and busy, I push to move the data into a SharePoint list or Dataverse and keep Excel as the report, because it was never meant to be a database for flows."
Blaming the flow for missing rows or odd dates without knowing the table, row-limit and date-format rules.
Trigger: added, modified or deleted row; change type Modified; scope usually Organization.
Filtering: Select columns by logical name, Filter rows in OData; choices compare the number, not the label.
Lookup: entity set name with the GUID in brackets, using the trigger’s underscore value field.
Duplicates: check for an existing payment before creating one.
"I'd use the Dataverse trigger for when a row is added, modified or deleted, with change type set to Modified and the scope usually set to Organization. Two settings do the filtering. Select columns takes the status column's logical name, so the flow only fires when that column is updated, not on every edit. Filter rows is an OData condition, and for a choice column it compares the number behind the option, not the label. To create the payment, a lookup can't take a name. I set the Account field to the table's plural entity set name with the GUID in brackets. The trigger gives me that GUID in a field that starts with an underscore and ends in value. Last, I guard against duplicates by checking whether a payment already exists for the invoice first, because another update to the status column can fire the flow again."
Trigger: When a row is added, modified or deleted
Change type: Modified
Table name: Invoices
Scope: Organization
Select columns: cr123_status
Filter rows: cr123_status eq 100000002
Add a new row (Payments)
Account: accounts(@{triggerOutputs()?['body/_cr123_account_value']})
Filtering a choice column by its label text, or pasting a record’s name into a lookup field.
Less data: only rows changed since the last successful run, filtered in the query.
Safe writes: upsert on a key so reruns don’t duplicate.
Lean loop: shape once with Select, write in parallel, few actions per row.
Honesty: at real volume, suggest a dataflow or data integration tool; always log a summary.
"First I'd question the full sync. I'd move only rows changed since the last successful run, using a modified date or a change flag in SQL, and store that run time so the next run starts from there. I'd filter in the query itself rather than pulling everything into the flow. Each write would be an upsert on a key, so a rerun updates rows instead of duplicating them. Inside the flow I'd keep per-row work light: shape the data once with Select, and let the loop only do the write, with concurrency on because the writes don't depend on each other. Every action counts against the platform's request limits, so fewer actions per row matters. If it truly is twenty thousand changed rows a night, I'd say honestly that a dataflow or a data integration tool may suit better. Either way, the run ends with a summary: rows read, written and failed."
An Apply to each over every row with five actions inside, no filter at the source, and no safe way to rerun.
Solution: build in a development environment, inside a solution.
Per-environment values: environment variables and connection references, no hard-coded URLs.
Deploy: managed solution into test and production, mapping connections and values on import.
Repeatable: pipelines or a build pipeline with the Power Platform CLI and source control.
"Everything starts in a solution in a development environment. Inside it, the flow uses connection references instead of direct connections, and environment variables for anything that changes per environment, like the SharePoint site address, a list name or an approver's mailbox. I export the solution as managed and import it into test, then production. On import I map the connection references to that environment's connections and set the environment variable values, so the same flow points at the test site in test and the live site in production. Managed means nobody edits it directly in production; changes go back through development. To make it repeatable, I use pipelines in Power Platform, or a build pipeline in a DevOps tool with the Power Platform CLI, and keep the unpacked solution in source control. And production connections belong to a service account, not to me."
Exporting single flows by hand and then editing the site address directly in production.
What: an admin policy for the tenant or chosen environments that groups connectors.
Groups: business, non-business and blocked; a flow can’t mix business with non-business.
Effect: a violating flow can’t run, and existing flows can be suspended when a policy changes.
Response: find the connector, look for an approved option, or raise a request with a reason.
"A DLP policy is set by admins, either for the whole tenant or for chosen environments. It sorts connectors into groups: business, non-business and blocked. A flow can use connectors from the business group together, or from the non-business group together, but it can't mix the two, and it can't use a blocked connector at all. The point is to stop company data flowing into places like a personal storage service or a social media account. So if I build a flow that reads from SharePoint, sitting in business, and posts to a connector in non-business, it won't be allowed to run, and if a policy changes later, an existing flow can get suspended. When that happens, I don't ask for the policy to be switched off. I find the connector that caused it, look for an approved alternative, or raise a request with the admins and explain the business need."
Moving the flow to another environment or a personal account just to get around the policy.
Before: the manual steps, who did them and what went wrong.
Build: trigger, main steps and how failures were handled.
Proof: a check against the old way and what changed afterwards.
Lesson: one thing you would do the same or differently.
"At my last company, the HR team collected new joiner details from a form, then copied them by hand into a SharePoint list, emailed IT for a laptop and emailed the manager a checklist. It took a coordinator most of a morning every week, and details got missed. I built an automated flow on the form response. It created the list item, started an approval for the IT equipment and sent the manager a checklist with the start date formatted properly. The main steps sat in a Try scope, with failures posted to a support channel. To know it worked, I ran it alongside the manual process for two weeks and compared every record. After that the coordinator stopped copying completely, and missing laptops stopped coming up in joiner feedback. My lesson was to sit with the person first, because half the rules weren't written down anywhere."
A story that ends at "I built it and turned it on" with no check that it did the job.
Signal: how the problem surfaced, ideally before users noticed.
Cause: what the run history showed and the real root cause.
Repair: the fix plus the backlog of affected records.
Prevention: the monitoring or design change that followed.
"I had a flow that moved approved purchase requests into the buying team's tracker. A manager asked why her request was approved weeks ago but never ordered. There were no failed runs at all, which was the clue: for her request, there was no run. Someone had renamed the Approved choice to Approved by manager, and my trigger condition still checked for the old text, so it simply never fired. I fixed the condition, then found every request with the new status that had no tracker entry and added them, with the buying team checking the list. Then I changed the design. I agreed with the list owners that changes to that column come through us first, and I added a weekly scheduled flow that compares approved requests with tracker entries and posts any gap to our support channel. Since then, a mismatch shows up in days, not weeks."
Blaming the business user who renamed the choice and changing nothing about how the flow is monitored.
Found: personal connections, default environment, unclear names, hard-coded values.
Agreed: what the flow should do, confirmed with the maker and the business.
Changed: solution, service account, environment variables, error handling, clear names.
People: credit the maker and hand over with a short support note.
"A team lead had built a flow that chased overdue invoices. It worked, but it ran under his personal account, sat in the default environment, had actions called Condition 2 and Compose 7, and had the list address typed into ten places. When he moved teams, nobody knew how it worked. I didn't rebuild it straight away. First I sat with him and wrote down, in plain words, what it did, and had the finance team confirm that's what they wanted. Then I moved it into a solution in a proper environment, switched it to a service account connection, replaced the typed addresses with environment variables, renamed the actions and added error handling. I kept him as a co-owner and wrote a one-page note on how to support it. The key was respect: he'd solved a real problem, so I framed it as making his flow safe to depend on, not fixing his mistakes."
Mocking the original maker or rebuilding from scratch without first agreeing what the flow is supposed to do.
Specifics: two or three records that should have changed, and when.
Three stories: never triggered, ran but did nothing, or failed and was swallowed.
Repair: fix the cause, then find and correct every affected record with the business.
Guard: fail loudly when nothing is processed, and compare counts regularly.
"First I'd get specifics: two or three records that should have been updated, and roughly when. Then I'd find the runs for those times. There are usually three stories. One, the flow never ran for those records, because a trigger condition or filter excluded them, so I compare the records' values with the condition. Two, it ran but did nothing: a Condition went down the empty branch, Get items only returned the first page, or a loop ran over an empty array. Three, something failed and was swallowed by a Catch or a run-after path that never marked the run as failed. The inputs and outputs of each step show which. Once I know, I fix the cause, then find every affected record and correct it, with finance checking the list. Last, I add a guard: the flow fails loudly when it processes nothing it should have, plus a regular count check, so green really means done."
Rerunning the flow over everything and hoping it catches up, without knowing why the records were missed.
No workaround: don’t borrow someone’s premium license or hide the call elsewhere.
Explain: SQL Server is premium, so licensing is a decision for the budget owner.
Options: a standard-only interim route, buy and deliver, or another approved tool.
Record: a short note with each option, its risk and an honest date.
"I wouldn't look for a way to dodge the license, like running the flow under someone who happens to have premium, because that's a compliance problem and it breaks the day that person leaves. First I'd tell the manager plainly that SQL Server is a premium connector, so either the users or the flow need premium licensing, and that's a decision for whoever owns the budget, not me. Then I'd offer options. If the data can reach a standard source, like a SharePoint list filled by an export the database team already runs, a standard flow could cover the gap for now. Or the license is bought and I deliver once it's assigned. Or another tool the company already has does the job. I'd write it up in a few lines so the choice and the risk are on record, with an honest date for each option."
Quietly building it on a colleague’s premium account to hit the date.
Default: no direct edit; it adds an unmanaged layer on top of the managed solution.
Fast path: fix in development, test the failing case, deploy the solution.
Stop-gap: turn the flow off and handle items by hand if even that is too slow.
Emergency: if agreed, edit, record it, copy it to development and remove the layer.
"My default is no, because a direct edit in production puts an unmanaged change on top of the managed solution, and the next proper deployment either hides my fix or clashes with it. But today is a real need, so I'd move fast through the normal path. I'd make the fix in development, test it with the exact case that failed, then deploy the solution through test to production, which for a small fix doesn't take long. If even that's too slow, the safer stop-gap is to turn the flow off and have the team handle the few items by hand while I fix it properly. If my lead and the business agree an emergency edit really is needed, I'd do it, write down exactly what I changed, put the same change into development that day, and remove the unmanaged layer so production matches source again."
Editing production directly and never bringing the change back into development.
ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your resume and notes are never stored on our servers. It stays out of screen share on every plan; only you can see it.