My students often ask me “How can I tell if my code is too complex?” There are a lot of different answers to that question, but I thought I would share my favorite complexity detection tool with you – a tiny purple stuffed monkey:
The basic concept behind it all is to explain your code to your duck/monkey/other inanimate object, step by step. If it takes you longer than 1-2 minutes to explain a method, it’s probably too complex and should be refactored to something simpler.
Well that sounds great and all, but how exactly do I reduce a method’s complexity?
Good catch – “Just simplify it” is unacceptably hand-wavey and doesn’t really provide much guidance. One good approach for reducing complexity is called the Single Level of Abstraction Principle (SLAP). As might be apparent from the name, SLAP recommends that all statements within a given method exist at the same “Level of Abstraction”.
OK, but what’s a Level of Abstraction?
Let’s start with an example of code that does not follow SLAP guidelines:
Admittedly this is a contrived example, but even this simplistic code takes upwards of a minute to explain to the monkey. The main issue is that we’re bouncing around at various low levels, dealing with streams, streamreaders, lists, strings, etc. and it’s kind of hard to wrap your head around.
Contrast this with a somewhat more “SLAP-ified” version of this class:
Notice how the DoSomeWork method now reads much like a to-do list:
- Open a file
- Iterate over the file until we are out of data
- Read the next line
- Prepare the fields
- Extract the desired data
- Add the data to our list
Explaining the methods we extracted should take no more than a few seconds apiece since they’re mostly descriptively-named one-liners.
OK, Awesome – Surely It Can’t Get Cleaner Than That!
It’s definitely cleaner, but there’s still just a bit more complexity in DoSomeWork than I’d like to see, particularly inside the while loop.
Looking at our while loop, it appears that whatever we’re doing to each line is repetitive, in that we’re performing the same 3 steps for every single line. In that spirit, let’s extract out a ProcessLine method that encapsulates those 3 calls:
Now we can logically drill down into the implementation:
- At a high level, we can see that DoSomeWork opens a file, loops through it, and performs some operation on each line of the file
- One level down is the ProcessLine function, where we can clearly see we need to get some field data, extract the desired value, and add it to a list
- If we really need to get into the gritty details, we have the low-level methods we originally extracted
Managing complexity is a vital aspect of software engineering in general. Identifying complexity becomes easy when using Rubber Duck Debugging, while approaches like SLAP improve code readability by encapsulating low-level concepts within higher-level methods that clearly express the code’s intent in domain-specific language.
So don’t hesitate, Tell it To The Monkey!
PS: To my readers who also happen to be fans of The Simpsons… Yes, that is a Purple Monkey Dishwasher 🙂