Self-documenting code: some people don’t believe it actually exists and others swear to have seen it in person. The self-documented application is one of the rarest and most elegant creatures a coder can come across in the wild. This mythological beast has been rumored to clearly define expectations and guide many a lost programmer in their time of need. While sometimes masquerading in a sea of unnecessary comments, self-documenting code occasionally emerges from its den of obfuscation in order to transform even the most complicated applications into clearly defined works of art.
I, too, have seen this elusive creature. It lives deep within the thicket of bad coding practices, but with the right tools at your disposal you can join the hunt and see its true beauty through the forest of code.
By asking yourself the following questions every time you come across a wild section of your application, you, too, can help tame the undocumented beast and turn it into a glorious, self-documented creature!
Question 1: What am I looking at?
If you have to rely on code comments or a separate document in order to understand what kind of animal you are dealing with, then you are not off to a good start. A seasoned code spelunker should be able to clearly identify what kind of creature is before him or her with the help of several visual cues.
- Is the code broken up into small, singular purpose classes and methods, or is everything grouped together into a few unruly behemoths?
When you try to do too much all at once it becomes quite difficult to understand what exactly is going on. If you can break your code down into its individual components it becomes much easier to identify the purpose and functionality of each unique piece. Well-documented applications can easily be identified by how well they manage and separate their concerns into small, manageable units. If you do have a large method that can not be separated, it is amazing what a few line breaks and a little white space can do to help improve readability.
- Is everything where you would expect it to be, or is there no way to clearly tell the head from the tail?
Grouping similar items together and keeping everything in a logical order can go a long way when your code matches up with the reader's expectations for what comes next. Decide on a pattern for where you want to declare public and private variables, your getters and setters, and everything else in your class, and be consistent across your application. Your code should progressively tell a story in an order that makes sense. If you have a method that calls several subroutines it helps when those subroutines are listed right after your parent method. You should never make the reader have to hunt through your code to figure out what happens next.
- Is the code written with the purpose of conveying clarity towards the reader, or is it a demonstration of cleverness and ego on behalf of the creator?
As programmers we take great pride in being able to accomplish difficult tasks in as few lines of code as possible; however, the same task can often be broken down into much easier-to-follow components. When you condense a piece of code down just because you can, it often becomes that much more difficult to read. A well-written application should help guide the reader along through the process just as well as it accomplishes the task at hand.
Question 2: What does it do?
After we have successfully visually identified the creature in question, it is time to make sense of our application’s true nature. Does our code environment encourage wild behavior or domesticated obedience? Will our unruly code eventually try to kill us? By asking yourself these questions you can help soothe the savage beast.
- Are each of the application elements named appropriately, or is our creature in an entirely wrong classification, altogether?
One of the most obvious code smells of a bad application is when the variable names and methods do not accurately reflect their true intentions. Are you using a naming convention like Block Element Modifier (BEM) to clearly identify your variable names and Cascading Style Sheet (CSS) styles? Updating your method and variable names to more accurately reflect their behavior is one of the easiest things you can do to help improve readability and understanding of your application.
- What is its purpose? Does each class in our application have a single job to do, or does it try to accomplish too many things at once?
Each block of code should adhere to the Single Responsibility Principle (SRP) and only be responsible for accomplishing a single task, and do it well. That way we can encapsulate our application’s many different behaviors into their separate areas of concern and better understand what each individual piece is trying to accomplish on its own. If you cannot quickly look at a function and deduce what is going on then perhaps it is time to start breaking it down into its individual components.
- How is your test coverage? Do you have a well defined set of acceptance tests that clearly indicate how the application is supposed to behave?
Each individual test is an instruction manual for your code; it should indicate what the application is supposed to do and let you know when it is not behaving accordingly. Maintaining your application becomes quite difficult without a well-defined set of tests when you need to make changes and you have no idea how the current code is supposed to act. A great indication of a housebroken application is a one that has a mature and thorough regression test suite that clearly defines its expectations.
Question 3: How do I update it?
Unfortunately, identifying and understanding your application’s behavior is only half the battle. Rarely, as developers, are we tasked with solely trying to figure out what the heck is going on; often we are also required to make updates and improvements in an attempt to groom our unwieldy beast of an application.
- If I need to make changes, how confident am I that the application will behave correctly? Have we spent enough time training our code and building out our test suite?
If we are continuously using Test Driven Development (TDD) to let our tests dictate new features we should have a well-defined set of guidelines for how the application is supposed to behave. Any updates should be able to immediately notify us if something is not working as expected. A robust test suite should give us confidence when we need to update the code that we are not going to break anything.
- Have we removed all unused code from our application, or did we leave dead code behind to fester and cause confusion?
Make sure you take the time to clean up after your application when you refactor and certain functions become obsolete. Imagine how difficult it would be to try and read an instruction manual for a product only to realize that the section you just read only applies to a part that is not included with this model. If there is code not being used by your application you should take the time to remove it.
- Lastly, are your new changes consistent with the same, native application style? Did you take the time to update all the requisite tests and make sure your new code plays nice with the old application?
Often it is much easier to just slap on a quick fix and move along, but a little bit of finesse and consistency can go a long way toward making sure your application is sustainable down the road and not just for the immediate future. It doesn’t take much for a well documented and housebroken application to become feral and unwieldy. Make sure you take the time to update your unit tests and maintain your coding patterns or else your code can quickly get out of hand. It is much easier, and more importantly, time and cost effective, to do things right the first time rather than have to retrain your application again at a later date.
What have I learned?
All code is wild in nature. It takes many iterations of development and testing in order to thoroughly understand each application in detail. If you take the time to careful write your code with each of these questions in mind, you, too, can tame the self-documented beast!