When you are first learning how to program a computer, your goal is to just make everything work correctly. There are mental hurdles you must climb to write your first program: What programming language should you use? Where do you type everything in? Where do all these semicolons go? Why does it say syntax error when everything looks correct? By the time everything actually compiles and runs, it feels like you’ve accomplished a great feat, and the task is complete. In reality, the first time your code builds and runs is just the beginning of what it means to build well-designed software.
Your first ever programs may get the job done, but their design will probably be a mess. When you build things in the real world, your mind grasps how the different parts of a design fit together much easier than when you’re studying the design of a software project. If I showed you a building made out of Legos, you would have no problem seeing all its individual parts, and understanding how each piece fits together with its neighbors. Building in the real world is both concrete and sensory. We can utilize all of our senses to understand the inner-workings of a design that is before us.
Software is not concrete, and it is not sensory. You can’t touch your code, and when you see your code, you only see symbols that represent some abstract design. The way the pieces of a program fit together must be kept in your head. When reading a book, you first read the words on the page and build a mental image from the words. When you’re reading a program, you must also read the words on the screen, but also reconstruct the mental image of the pieces of the software. You must see all the actors, map out the dependencies, recognize the contact points, and decigher all the interactions of your code. If you’re building a bridge, it’s easy to see if a support-beam is coming into contact with too many other pieces of the bridge. If you’re building software, seeing these connection points is an exercise in your ability to hold the structure clearly in your mind.
The more you program, the more you understand that the simplest way to hold a software design in your head is by making it simple. A program that is simple is easy to understand and easy to work with. Good programmers can look at a piece of code and recognize when it’s complex. Great programmers can look at a piece of code, recognize when it’s complex and then proceed to make it simple.
An airplane pilot I knew once told me that the most dangerous pilots are not the novices, and not the veterans, but the pilots just in between. They are the pilots who no longer have the fearful respect of a novice, nor the wisdom of an expert, but instead have the cockiness of an intermediate. They’ve got enough flying time under their belt that they don’t know how much they don’t know, and are dangerous because they believe themselves to much better than they really are.
A dangerous developer thinks they’re writing good code, perhaps because their variables are well named and their methods are short, but in reality their designs might be a tangled web of gunk. Seeing only superficial qualities like method length is how you miss the hidden complexities of your program. On the surface your code might be easy to read, but impossible to comprehend.
How can we defeat complexity in our programs? Many developers will reach for a design pattern, a codified best practice, or some other precscribed cookie-cutter recipe. Yes, applying a design pattern might help you, but rather than ask yourself which design pattern you should be using, ask yourself instead how easily can you visualize the design of the code in your head. As a starting point, some basic questions I like to ask are:
- How many moving pieces (objects, functions, actors) are in this code path?
- How much do the moving pieces know about each other?
- How much state is there to manipulate?
- How many branches are there into and out of my code?
- How many dependencies, both internal and external are there?
These questions are merely ways to assess code complexity - they are by no means exhaustive, nor are they always the right questions to ask. Complexity falls out of a system that is not understood, and is wound too tightly on itself. A program that is easily understood is often indicative of a system that is simple. If you cannot understand the design of your software, you have no control over it.
If I could ask one thing of you it would be this: practice seeing complexity. Not just in software, but all around you. Unlike many other things in this world, software changes easily. You have the power to make your software simple: See the complexity, know the complexity, and then destroy it.