Saturday, March 15, 2014

The purpose of scripting: Using why we script to inform how we script in PowerShell

To properly understand how to write a given script, we have to understand why we are writing it.

That's easy, you say.  I'm writing this script to collect data about my servers.  Or to perform daily maintenance tasks.  Or to build new servers.  Or to automate adding new users to our systems.

But those answers are incomplete.  They don't address the big picture.

I'm writing this script to collect data about my servers so that I don't have to do it manually.  Or to perform daily maintenance tasks more reliably and at a time of day when my staff is asleep.  Or to build new servers quickly, consistently, and cheaply.  Or to automate adding new users to our systems directly empowering my HR staff and freeing up IT support resources for more challenging tasks.

That can be generalized into a standard definition of why we do what we do.

The purpose of scripting is to assist in performing some function by optimizing the resources of the system in which the function is performed.

That sentence is a little dense, but it is important to let it all sink in, because this what we all do professionally, and it is what everything else here flows from.  So let me say that again a little slower.

The purpose of scripting
is to assist
in performing some function
by optimizing the resources
of the system in which the function is performed.

This allows us to analyze our purpose, and to develop a way of thinking about our scripting that will help us make better, easier decisions about our scripts.

The key to understanding what that means and the implications it has on us and our scripts, is the fact that the system is not just the box.  It isn't just the box on our desk or the big box of a datacenter with lots of smaller boxes within.  The system includes the people.  It includes the people for whom the function is performed.  It includes the people responsible for performing the function.  And it includes the people who create and maintain the script.  It includes us.

So when I sometimes say that scripters are lazy, that is just playful shorthand for saying we optimize the resources of the system.

Software developers do this as well, but they balance the requirements differently than scripters, resulting in different design decisions and coding styles.

Here are the four and a half guiding principles supporting the purpose of scripting.

1. The script needs to reliably perform the desired function.
2. The script needs to be easily writeable.
3. The script needs to be easily readable, understandable, and maintainable.
4. The script needs to optimize the resources it uses to run.

These are the requirements you need to balance when designing and writing your script.  You can't do them all perfectly.  You focus on just one at your peril.  You can't balance them the same way on every script; each script is different, each function is different, and each system is different.

But once you understand the principles, and understand that they each only exist to serve the purpose, the reason we are scripting in the first place, it becomes much easier to make the many decisions and trade-offs we make with every script.

1. The script needs to perform the desired function.

The script need to work.  It needs to work well.  It need to work reliably.  That's obvious.

But how well, how thoroughly, and how reliably?  That's different for every script, as the balance with the other requirements is different in every script.

If a script is going to run unattended in the middle of the night on a customer-facing production system, it needs to be well-tested and have lots of built-in error handling.  But if I'm sitting at my desk watching it run, with time to tweak it and run it again if it fails, it doesn't need to be as rigorous.

How much time I spend making the output look pretty varies greatly between output that gets automatically emailed to the CEO and output that I'm personally reading off the screen.

In some cases, the script does not need to perform the entire function.  For an ad hoc report, it might be faster to just have the script give me the raw data, and I can pretty it up more effectively and efficiently in Excel.

2. The script need to be easily writeable.

The whole point of a script is to automate something, to make it less work for me.  If writing the script is a lot of work, that defeats the whole purpose of the script.

Jeffrey Snover and his team and their successors did a wonderful job creating PowerShell with scripters in mind, making it powerful but easy to use, comprehensive but discoverable.  Paradoxically, one of the ways they made it easier for us, was by making it possible to do things the hard way.  This allows us on the ground to decide when and where it is appropriate and necessary to put in the extra work or extra complexity, and where we can do things more simply.

Commands and snippets that are easy to remember, easy to type, forgiving of variations of syntax or runtime environment, and easy to troubleshoot are preferable to those that are not.  Readability is not just about the future reader of the script (as in the next section).  It's also about working with your script while you are writing it.  Effective use of white space can help considerably when you are looking for an errant parenthesis.

Using aliases on the PowerShell command line can be a wonderful time saver.  Writing elegant, dense one-liners is uniquely satisfying.   But when writing scripts, we save more time by not using most aliases, by writing out the commands in more human-friendly language, by adding white space and line continuations that make it easier to work on the script.

Always using the right variable types makes your scripts go faster.  But always using a variable type that is easy to remember, easy to type, and easy to read, makes me go faster.  Which is more valuable and which is the better resource to expend on this project:  200 milliseconds of CPU at runtime or 20 minutes of my time?

In some cases, it is better to spend the 20 minutes shaving 200 milliseconds off of a subroutine.  For software developers, this is usually the case.  For scripters, this is almost never the case.

3. The script needs to be easily readable, understandable, and maintainable.

The system doesn't just include the people writing and using the script.  It also includes the person that has to pull up the script next week or next year and figure out what it does or why it doesn't work anymore or how to adapt it to new requirements or how to borrow chunks of it to adapt for other scripts.

Everything you do while writing your script needs to balance the needs of whoever might someday read or modify your script.

If you are working on the command line or working on an ad hoc script that is going to be deleted as soon as you are done with it, feel free to write it in whatever way is easiest for you to write.  Though I think you will find it easier in the long run to use a single writing style for everything.  My only concessions to these are fewer comments, less consistent capitalization, and simpler variable names.

If you are writing a script that will be read again--even if it's just by you picking up the next day where you left off--you need to write for the reader.

Use comments.  A competent, experienced scripter can look at line of code and figure out what it does.  But that takes time and effort.  It takes less time and effort to add that information to the script while you are writing it.  What is the script assuming about the environment it is running in or its inputs or how it will be used or what the results will be used for?  Add comments explaining all of that.  You will save hours or days of troubleshooting time when the assumptions are no longer valid next year.

Use white space.  Vertical and horizontal.  Use line continuations to break up dense commands.  Make it easy to see the sub clauses of complex statements, to see what goes together in chunks, to see the hierarchy of your code.

Use human-style words and syntax wherever possible.  Use variable names that not only describe their use well, but also help make complete sentences in your commands.  Spell out commands and parameter names instead of using aliases, except for those rare cases where the alias is easier to read and understand than the full command.

Given a choice and all else being equal, use the command or syntax that is more intuitive to the person modifying it later.  You already have a comment stating what you are doing; if it isn't obvious how you are accomplishing that, explain it in the comment.  If you made an unusual choice for a non-obvious reason, explain it in a comment, so that there are not unintended consequences later when someone "fixes" it.  If the need for a section of code or chosen method will become obsolete in the future and can or should be changed when circumstances change, put it in a comment.

If your comments are so numerous or wordy or redundant that they start to interfere with reading and understanding the script, cut them back.  Everything in balance.

(That's the one I'm counting as one and a half if you're wondering.  The three needs covered are too small and overlapping to be discussed or counted separately, but together they are too big to be just one.)

4. The script needs to optimize the resources it uses to run.

Don't use up all of the resources on the computer.  Don't use too much memory, disk or CPU.  Use what you need from what's available, but don't be a pig about it.  Don't use so much resources that you are interfering with other things happening on the server or virtual host or SAN or network.  Rarely is your script the most important thing happening in the system.

But don't be shy about using the resources you have to do the job you need to do.  Moore's Law is forever giving us more power.  Use it.  The whole point of the script is to use those resources instead of more expensive resources.

The server is a resource.  The user is a resource.  You are a resource.  The future is a resource.  Write your script in such a way that you are optimizing your function across all of the resources of the system.

I'll talk about specific examples and scripting styles and decisions in future articles.

No comments:

Post a Comment