What are the main reasons to choose and invoked workflow in separate file instead of just embedding a new workflow in the same file?
I feel there are issues when passing arguments back and forth and tracing becomes a bit harder with invokes. If I’m creating them for reuse in other parts like a sub routine, that’s one thing. But if I only run them once, it’s simpler just to embedd a new workflow there instaed. No need to fumble with arguments (always need to add them separately besided my variables!) and it’s easier to expand and work with.
Is error handling better when using invokes? I saw it had properties for “UnSafe” and “ContinueOnError”.
I think I would have considered invokes more if there were global variables to use so I didn’t have to use arguments.
That’s interesting question, actually. It’s just my suspicions, but I think that it can affects some way on Robot’s performance. Firstly, if You embed everything in one file, then processing such a big xaml file can be overloading. Also I am wondering, if it can carry weight in memory management. UiPath is .NET App, so maybe dividing workflows into separate files helps Garbage Collector in disposing unused variables, limited to the range of single workflow.
If You use TFS (version control) - its blocking access to files, which are currently editing by someone else, so if You have everything in one file, then You wouldn’t be able to work on Robot with someone else. From the other point of view, if You accidentally delete a file or something like that and You dont use any version control system it would be better for You to have everything separately
But it’s just what I think it could be, correct me if it’s differently.
First things first - if it works for you, I’m the last person to say you should stop what you’re doing.
But, there might be some considerations to take that you didn’t run into (yet) that might make you reconsider. If you’ll still think what you do is best for you - you’re the only one that can decide that and more power to you!
As a starter I can shamelessly recommend checking this topic where I’ve already written some related stuff, but a little bit from a different perspective.
Hopefully you won’t mind if I slice up your post to related things and try to answer it in parts.
Now to the meat:
You might consider upgrading your version if you still see “UnSafe” in InvokeWorkflowFile, unless you took it from docs.
Sidenote: @docteam - argument list there is outdated (sorry, for w/e reason can’t suggest edits there, probably something on my end, I’ll figure it out eventually).
A little clarification that I think might be helpful, is that “UnSafe” was changed to “Isolated” so that it better reflects on what it actually does.
If you see “UnSafe”, what comes to mind is things like “this might not always work, so I’ll put it in Unsafe context”, which is misleading. What it actually does is precisely what’s written in the docs - makes the called workflow run as a separate process (separate address space which gives it more available memory, as @aksh1yadav and @KatKon mentioned). But in no way does it help with actual error handling or anything like that.
In 95% of the cases, keeping it unchecked won’t negatively or positively impact your workflow and in some cases it might make things worse or not work at all, if your arguments are not serializable (reliably - like a DataTable - or at all - like SecureString).
Which brings us to Arguments:
Arguments, unlike local variables which are kept strictly inside that workflow, are the interface of your workflows - they, and only they define (in an ideal world - they might be assets or external file dependencies, but let’s simplify for a moment) what that workflow needs from outside to serve it’s purpose and what result it will give back.
While this is limiting, that limitation is not a side effect, but the purpose - it brings separation of concerns.
Main part that I’ll bring here from that article is this:
The value of separation of concerns is simplifying development and maintenance of computer programs. When concerns are well-separated, individual sections can be reused, as well as developed and updated independently. Of special value is the ability to later improve or modify one section of code without having to know the details of other sections, and without having to make corresponding changes to those sections.
One of the not-often-enough brought up things is that to understand how something works, let alone “as intended”, you need to be able to reason about it. And it is not possible to reason about something which you cannot hold in your brain in whole at the same time.
Even though it might be easier to just add a sequence with some new functionality somewhere, it makes it harder to test, change or comprehend. It might not be apparent while you’re working with something on a daily basis, but if you step away from it for enough time, or worse it was made by someone else… It becomes a Gordian Knot and the only way to get over it is to cut it to pieces.
So if you see that mess that some poor fellow (possibly a past version of someone…) made, you need to take it in pieces to know what to change. Either “in memory”, analysing it by parts, or by actually splitting it to separate workflows (i.e. refactoring/restructuring). One of those you’ll need to do every time, the other is a one time job.
Consider this question:
Before you change or delete any of your Main variables, can you identify which parts of the project will be affected?
If the answer is yes - you are either a genius or mistaken. Even if genius, the next person that will be working with that code might not be.
If the answer is no, or all of it, that means you can’t safely change them, which also means that anything that needs to change them cannot be done.
This is where all the “I changed A and found that F, L and O stopped working” bugs come from - unknown dependencies.
So to make a full loop:
Arguments define the input/output (interface) for individual workflows (components). Knowing dependencies of components allows evaluation of scope and risk of changes to them, as well as testing of individual parts of the project (component testing).
I’d personally disagree with that statement.
You cannot reliably identify reusable parts of what you have done, if it’s just one big blob of activities.
Yes, of course you can identify some of them (logging to application, logging out of it etc.), but a lot will go unnoticed.
Let’s take a simple scenario of making a sandwich:
Get a loaf of bread from the shelf.
Get a sharp knife.
Slice two pieces of bread.
Get a jar of peanut butter from the cupboard.
Get a dull knife.
Open the jar of peanut butter.
Pick up a slice of bread.
Using the dull knife, pick up some peanut butter and spread it on the top of the slice of bread. Repeat for second slice.
If it’s just one blob (because it’s simple), you might identify some stuff - getting things from the cupboard, getting things from a drawer (knifes, presumably). Maybe something more.
Now let’s take a look at a different scenario:
Preparing sausages. It’s similar, but how much?
Get sausage from the fridge.
Get a sharp knife.
Slice sausage into pieces.
Get a jar of mustard from the fridge.
Get a dull knife.
Open the jar of mustard.
Using the dull knife, pick up some mustard and spread it on the top of the slice of sausage. Repeat for all slices.
A fridge and a cupboard work the same - locate knob, pull to open.
Getting knifes works the same.
Slicing works the same as well (just repeat more times/less space between cuts - arguments can take care of that).
Opening jar and spreading X over Y is the same.
With some design work, ALL actions can be done by same instructions. And there’s probably a lot more that this set of actions could cover, not necessarily food related
To make it clear - I’m not advocating to design everything as a fully reusable component from the start. I don’t think that would be a wise investment of time.
But if you design your project from separated workflows, you leave yourself the option to do that much easier later on and it’s much easier to spot from where things might get “borrowed” and turned into components.
Leaving yourself options to improve the project is always good.
Hopefully at least some of it will be useful (and I didn’t just muddy it to hell and back).
This got lengthy, so only shortly about other things:
It’s very similar, might be a little easier to setup properly with invokes. exception.source helps also.
It can be especially overloading for a person that needs to understand how that monstrosity works (and sooner or later these tend to become monstrosities).
Or at least make periodical copies if VCS’s are not an option. It’s still better than nothing
Make that 5 and throw Testable in there from me ;).
Code is designed to be testable, it doesn’t just become testable when you add tests and it actually is possible (quite easy, unfortunately) to make a project that is not testable (in meaningful ways).
IMHO that’s a corner case that usually signifies some other problem than not having a separate workflow. Unless you made a workflow that literally did that (without some uncontrolled looping etc.) which… well… kudos, I guess?
Totally agree. It takes a lot of getting used to and discipline (when to do it and when not to do it), but it’s oh so worth it if done right.
You had to, didn’t you?
Ehh… there goes my sleep again…