Thursday, February 1, 2024

Hardening Child Flows, Part 1: Assertions

 This is part one of a new blog series about hardening child flows. The term hardening originates from metal work, where oil is used to quench hot steel in order to make it much more robust. This is also our goal. Our child flows should be as reliable as possible. More specifically:

  • They should not fail unexpectedly
  • They should handle unexpected input data as well as expected input data an react accordingly
  • They should not fail if a contained action takes longer than expected (timeout & retry policy)
  • They should have a continuance plan for each failed action (e.g. continue anyway, handle, send error message to operator and then terminate, e.g.)
  • Prior to making any sort of writing operation in the database, the input data needs to be checked (assertions)
Today, we will have a look at the last bullet point, assertions, since it's the easiest to grasp from the hardening goals mentioned. 

What are assertions

Think of assertions as conditions in the form of a boolean statement. For example, "is the input value greater or equal to zero?". If the condition is met (true), the flow is allowed to continue. If it isn't met (false), then typically the flow is terminated, but there are other options of course, that would allow your flow to execute anyway, e.g. by use of a default value instead. Assertions should always be the first actions in your flow, giving you the peace of mind, if a run made it through the assertions, the input data is OK which makes writing the rest of your actions in the flow much more easy, because you don't have to crowd your flow with special handling conditions for every possible situation regarding your input values. 

Some programming languages, like Python, have a command called assertion, which is delightfully short and concise:

assert i >=0, "Input value below zero!"

Power Automate, unfortunately, doesn't have such an action built-in. But we can easily recreate it with our own actions. Let's look at some examples

Method 0 (don't use): Terminate on failed assertion

While this is the most straight-forward method, but it has some obvious limitation. Simply letting a child flow fail is not good practice. We can do better!

At least, we managed to avoid having the flow continue with a value outside of the expected range, therefor avoiding further complications inside the subsequent actions...

Btw, don't bother writing a message in the Terminate action. Nobody will see it...


Method 1: Send a message and then terminate

Now at least a pre-defined person will receive a warning before the flow fails. This is the best option, if there is really no better way to handle the failed assertion: The usage of a default value would not be appropriate or the usage of the value cannot be skipped in subsequent actions. 

Just some quick tips: Never hard-code an email address of the person who should receive these messages. People leave companies, it happens :). A smarter way would be to put the person's email in a separate table, either specific to the project or, even better, used by all projects for role-based automated communication. Then create a child flow "Send notification to operator" where all the necessary steps of getting the person's email, compiling the email and sending it are done, allowing you to concentrate in your current flow on the message content. 

Method 2: Limit the value range with compose - don't terminate

If the fact that your input value is below zero can simply be ignored in your flow and a default value (e.g. 0 in this example) can be used instead, then you could use a compose action to set the lower limit of your value. Just make sure to use the output of this compose instead of the trigger input value throughout your flow. 

Of course the major advantage is, that the flow is allowed to continue. This may however mask a potential problem with your data. Beware!

Method 3: Use coalesce() to catch empty input value (null)

The coalesce() Function is a great way to assure a value is not null. Basically it evaluates: 
Is a given value null? 
No? Great, use it as is. 
Yes, it's null? Then use this instead ... 






Final tip: Put your assertions in a scope


Putting all of your assertions in a scope we keep them separated from the rest of your flow, hence improving legibility of your flow.

No comments:

Post a Comment

Insanely fast synchronization from a Dataverse table to a SharePoint List

Don’t be fooled: this challenge is deceptively hard — at least if you want to make it fast, efficient, and robust .  Feature specs Your sour...