Whenever it comes to building a scripted IT Automation solution that goes beyond the scope of a few commands in a single script file, you need more than sufficient technical knowledge and scripting skills: apart from that you need to approach the task in a way that helps you to resolve complexity and to plan ahead. With this post I want to raise your awareness for some General Principles for the Design of Scripted IT Automation Solutions that help you to master the situation. Here we go…
Nip it in the bud!
Don’t underestimate the beginnings. A quick-and-dirty approach isn’t a bad way per se, meaning that up to a certain point it’s a good way to go (at least from benefit-cost perspective). Exceeding that “certain point”, though, at worst could lead to chaos: If you follow the quick-and-dirty approach you’ll succeed to some degree and end up fixing/updating, adapting, flanging new features and partly rewriting your solution over time. The day will dawn when you want to rebuild the entire solution from scratch as it has evolved into something hardly manageable. It’s difficult to walk in mud, so to speak. As things usually turn out there’s no time/budget for tasks like this, thus you must drink as you have brewed.
And the moral of this story, never ever default to quick-and-dirty to begin with. It’s very, possibly even the most important to mind the early stages because later there will be hardly more advantageous moments to put things (back) on the right track. So, be sure to make time for thinking/planning, and through the end! Else you might draw the short straw.
Whenever you’re confronted with a new scripting challenge, howsoever minor it seems, ask yourself:
- Is the requested solution a feature of the scripting language?
- Have co-workers or I ever created a solution like the requested one?
- Could co-workers or I eventually re-use the solution or parts thereof?
Don’t get me wrong regarding the first question, but quite often people tend to rebuild existing commands or features due to lack of knowledge or preparation time. So, be sure to make time for figuring out that you’re really dealing with something nonexistent!
If the answer to the second question is “Yes” you definitely should figure out whether it’s worth the effort to adopt and adapt that solution. Here, likewise, the point is that you don’t want to reinvent the wheel.
If the answer to the third question is “Yes” you should opt for quick-and-dirty by no means, meaning that you should design the solution for repeated application or rather reusability.
As time goes on, you’ll get used to re-use your own work as partial solutions over and over again. At the end of the day you’ll realize that quick-and-dirty hardly ever would have been a suitable approach.
Apart from the above mentioned questions you should try to get to the bottom of the requested automation solution. Too often it turns out that an original request just covered the tip of the iceberg rather than the big picture. The big picture is exactly what you need, thus be prepared to clarify that. Ideally, you carry out a scoping date to get the big picture and write down a scope statement that exactly defines the requested scripting solution.
Divide and conquer
Facing a more sophisticated scripting challenge gives rise to the question how to tackle that task from scripting perspective. How to handle complexity? In computer science, there is an algorithm design paradigm called “divide and conquer” (D&C) that works by recursively breaking down a given problem into sub-problems, solving the (plain) sub-problems and combining these to solve the original (complex) problem. I highly recommend you to adopt the D&C approach as general scripting principle, in other words you break down a scripting challenge into as many separate scripting tasks as possible; after that you solve these scripting tasks individually; and finally you combine these partial, self-contained solutions to a complete solution. To put it another way, write for each single task a function; organize/bundle functions in libraries/modules; leverage these functions to solve the problem.
The basis for making many sub-solutions work together as a good “working team” is consistency and micromanagement if you will. Essentially, it’s about establishing a scripting framework that allows for and ensures information flow between the individual tasks. You need to define a set of rules, let’s call it a scripting policy that covers important details such as:
- How functions deal with input
- How functions pass back results (output)
- How functions handle errors
- How functions support testing and debugging/troubleshooting scenarios
- Naming conventions
- You name it
Again, avoid to reinvent the wheel! Be sure to check what your preferred scripting language has to offer with regard to your scripting policy and leverage this features. Blueprint a mandatory function template that incorporates all your rules and use it consequently to embed the effective “payload script code” safely within your scripting framework.
I must admit though that establishing such a scripting framework for the divide and conquer approach will blow up your solution. You need to do it anyways. However, you can keep it within reasonable limits if don’t overdo things!
While preparing a dish experienced chefs are distinguished from amateur cooks by frequent tasting. Put simply: the pros expect failure and therefore continuously taste to identify a need for improvement/adjustment as quickly as possible. They retain total control. I highly recommend you to adopt the chef’s approach as another general scripting principle, in other words leave nothing to chance.
Given that, according the D&C principle, you write a function for each individual task you should do this in fear of losing control if you will. It’s about micromanagement! Mind each damned detail and ask yourself what could go wrong with it. No twilight zones allowed. Validate incoming information, test for connectivity, whatever. Got it? Better double-check each detail than rely on a fortunate series of events.
Always script with testing and debugging scenarios in mind. Take care to be one step ahead and insert by default some debug messages to put out parameter values that were given to a function. Someday, trivialities like these will make your (or a co-worker’s) day.
Design for Change
This one is about separating the data from logic. Never ever mingle logic and data. This is essentially the key to flexibility and instant reusability. Script logic should only “know” how to process data while the actual values should derive from separate resources like SQL tables, XML/JSON/CSV files, you name it. Not even dream of hard-coded values!
With separation of data and logic it’s almost a no-brainer to set up the solution for another environment.
To conclude, there’s definitely more to tell but in the end it’s all about thinking end-to-end and micromanage. Mind the details!
If you miss or think different about something feel free to contribute your ideas by submitting comments.