If have ever taken a beginner programming class in school you were probably introduced to the idea of stubbing out functions. If not, let me quickly tell you what stubbing is. It is the process of identifying tasks in your design and setting up functions to handle those tasks. We define just the function signature (name, parameters and body) without writing the code statements in them. Once we identify all the tasks and the functions that will handle those tasks, then we go back and “fill in” the functions. In this article we will discuss the process of stubbing out functions a bit more and banter around the merits and drawbacks to this coding practice.
To give a little more explanation behind the concept we will demonstrate with a classic problem… making a peanut butter and jelly sandwich. How do you make one? You typically get two slices of bread, open a jar of peanut butter, spread it on one slice of bread, open a jar of jelly and spread it on the other slice of bread. We then put the two slices together to form the sandwich. Notice here that we have roughly outlined the tasks at hand for making the sandwich. Take note how many steps were outlined here for such a simple problem we do largely without thinking. Each of these tasks could probably be broken down even further. However, for the sake of simplicity we will just stick to these tasks.
With our steps in hand, next comes identifying the functions we need to declare for these tasks, what type of data they may need and then create basic function signatures to handle them.
function makePeanutButterJellySandwich() { // Get 1 slice of bread // Get 1 slice of bread // Get a knife // Open jar of peanut butter // Spread peanut butter on sliced bread 1 using knife // Open jar of Jelly // Spread jelly on sliced bread 2 using knife // Put slices of bread together } // Gets a single slice of bread and returns it function getBread() { } // Takes a jar type (peanut butter or jelly) and opens it function openJar(JarType jar) { } // Gets a knife to spread with function getKnife() { } // Spread the jar type, using the knife onto the bread slice function spreadOnBread(JarType jar, Knife knife, Bread slice) { } // Put together two slices of bread to form the sandwich and return it function putBreadTogether(Bread slice1, Bread slice2) { }
Now notice here that while we have defined the functions and made comments on how they would be used and the type of data they need, we have not defined any of the actual code statements in the body of the functions. The goal here with stubbing is that want to quickly see what functions are needed and the types of data they may take. Once we stub out a function we may realize that it may also need to call other functions and so we stub those out too. Once we think we have all the functions stubbed out, then it is just a matter of “filling in” the functionality for each function.
Well one thing that stubbing has done for me in particular is that it has helped me quickly identify if functions are decomposed enough or not. The goal of functions is to do a single thing and do it well. We are less likely to find ourselves doing too much in a function if we break down the steps into simple enough functions through stubbing without focusing on the actual details. I have watched experienced programmers, who have steered away from stubbing, tell themselves “Ok I need a new function that will do this complex task” and launch into writing a 50 line function that is doing way too much. They “hack at it” for awhile all along ignoring the idea of maybe needing to define yet another function. Hey, I have been guilty of the same thing!
One thing with stubbing I also find useful is the lack of need to refactor at the end. Ok so let’s say that we did write that 50 line function that is doing way too much. Now when you go back to do some optimization you have to try and refactor that 50 line function into individual functions. Wouldn’t it have been smarter to just start with the simple functions to begin with? If each function is simple enough and doing one thing, you will hardly ever find the need to go back and refactor.
Another “accidental” side effect I found with stubbing is that while defining the function quickly at the start and with a singular focus that it made it easy to identify functions that may be called more than once. For example, when we defined each step of our sandwich making we realized that getBread(), openJar() and spreadOnBread() will be called more than once. Here we were thinking about one task (getting some bread or opening a jar) but then when it came to listing out the steps we realized that we could call these tasks for both slices and both jars.
Think back to one of your most complicated software projects that you have worked on. Maybe you had a hell of a time reading from a file and parsing the values and getting it to all play nicely with the UI. What if that very moment you said “Hey, let me step back a second, just identify the tasks, quickly write them out and then from there could implement each task quickly and easily?” The end result would be that the complicated task is suddenly not that complicated anymore. We as humans have very limited capacity to hold all tasks in our mind. Programmers are better at it than most, but beyond 3 or 4 tasks we start to break down in comprehending how they all fit together into the bigger picture.
Even if you don’t take this advice about stubbing out all of your projects, do yourself a favor. When you run into that next major roadblock, step away from it, go grab a drink, come back in 15 with a fresh mind and make yourself do a little stubbing out exercise. I guarantee you will find it much easier to break through the task. I have done it many many times before. The hardest part about doing it is just realizing that you need to do it and identifying when you are in a jam that is going to take too long to hack your way out of.
Thank you for reading! 🙂