I often get asked why I use functions and when I think they should be used. Below is a guide I wrote explaining my theory on functions.
Why use functions?
Functions are, generally speaking, small single task tools (like a flathead screwdriver or hammer.) They do one thing and they do that one thing reliably well. If you take this approach when writing code you will find it easier to debug and find yourself writing less code. Why less code? Because, you’ll find you are now able to port your functions from one script to another or possibly even in your day to day life.
How you decide to write a function?
I have three basic guidelines for when to write a function.
First, if I find I am repeating the same code block over and over (like a code block that checks several services on a computer.) It makes sense to just write a function to perform the check and then run that against each server. This allows me to trouble-shoot the code more efficiently.
Second, if I find that I can use this code in other scripts. For example if I write a nice recursive parsing block. I may want to reuse that logic in another script.
Finally, if I determine the code is useful outside of this script. This may seem like the previous guideline but it is slightly different. A good example here would be a ping-server function. Not only is this useful in other scripts, but it is also useful in my day to day life.
Do you always design functions with the idea of reuse in mind?
Generally speaking it is a good idea to ALWAYS consider reuse when writing code. This is paramount when working with functions. The sole purpose of functions in life is for reuse. This is major consideration when designing your functions. Consider how and where they will be used. This will help establish what parameters it should have and what (if any) defaults it should have.
What do you put in your functions?
Ideally, because we design code for reuse, it is best to be as verbose as possible. Basic rule of thumb is hardcode nothing, all data should be passed by parameters. Certainly you can have default values for the parameters, but allow the function caller to specific other options without having to modify the function. This comes back to the black box approach. One needs to consider the effect of every change the original function and how that will affect the script as a whole.
In v1 I always try to implement –verbose and –whatif with my own switches. In v2 this is handled for you.
Do you separate the logic from the function?
When designing functions one should think about the looping and processing logic. Generally you will find this is script specific and should be implemented outside of the function. Ideally you would want to restrict logic to the party that requires the logic. For example, if you have logic to process servers, keep that logic outside of the functions. There is no need to implement that logic over and over for each function call. On the other hand, if you have logic that is expressly the domain of the function do not go crazy trying to rip it out just to put in the calling script.
What makes a good function?
Great functions are born out of need, but grow out of use. As you grow in your understanding your functions will grow with you. They are like the friend that is always there when you need them, but like that friend they need attention and care. Below are some features that functions should have:
Well defined parameters:
You function needs to be very clear on what data it expects. You accomplish this by having very specific parameters (this often will include the data type as well.) If you absolutely must have a specific value to process make sure that is clear from the function. A great way to accomplish this is by assigning the parameters default value to (Throw ‘$ThisParam is required’).
Consistent and expected output:
This is absolutely critical. You do not want to guess at what data will come from the function. You want the data to be what is expected. Design the function so that it returns one or more of a single data type (i.e. string, DateTime, or Boolean.) Be very cautious not to pollute the data stream with unexpected data written with write-output or data that wasn’t captured in a variable.
The function should NOT rely on any variables from the script. If the function needs input from outside make it a parameter.
The single most important job of a function is to be portable. If you do not plan to reuse the code you might as well write the code inline. A key factor to portability is to make sure your variable names will not collide with the calling script. A good rule of thumb is to preface them with $my or $func (like $MyServer or $FuncServer.)