Obvious Programming

I strive to write code that is obvious. Code either obviously has no bugs or it has no obvious bugs. There is more to obvious programming than just simplicity, but simplicity is at the heart of it.

Obvious code is mostly devoid of complex expressions, except idiomatic expressions as you often find in code to work with the specific inefficiencies or efficiencies in a language. But, code I write that isn’t part of an idiom has its complex expressions broken apart into a series of simple ones or a series of simple transformations. These are easier to write and understand and there is less ambiguity or confusion about the order of evaluation.

Obvious code can be easily instrumented and should be instrumented. I always leave instrumentation in code I write, disabled by a simple compiler directive or a boolean debug variable. Set the variable to true and all the instrumentation statements (aka debug statements) print to a log file. Production code always has these traces turned off, but in development the ability to turn a trace on for any function or method or feature or entire system allows a developer to leverage the effort already made in debugging problems.

Programmers add to the body of instrumentation as they fix problem. But, they can also use instrumentation while creating new software. I first do tend to add instrumentation after the code is mostly written, but before I run it. Adding instrumentation helps me catch errors in my thinking and coding because it is a different perspective on the code, like documenting it.

Instrumentation helps by giving visibility to the data and the flow of execution of a program. This helps whether the program is single or multi threaded, or in what language it is written, or for what operating system.

If you can’t see a problem you can’t fix a problem. And what is more: building things is much harder in the dark. The instrumentation installed during code creation provides visibility into the process and its data that allows the programmer to see where the process breaks down or the data has a problem. That kind of visibility makes it easier for the programmer to stay in the “flow” of programming and spend more time coding and less time “debugging”.

Coding is already complicated enough. Modern compilers are more helpful in finding a wide variety of problems in code, and static analyzers take it a step farther and are immensely helpful in writing good, clean code. They should be used. Even with modern technology the number of APIs and the details of their use are astonishing. There are many frameworks that can solve part of the problem so you don’t have to write code. If they meet needs and allow for creativity they are a good choice. But, they end up being a black box. The “code” in the project is missing everything done by the black box. That code is tightly coupled with the black box and this can be very complicated because the exact behavior of the black box may not be documented or well-understood or even defined in some cases. It is opaque unless the black box has been made more visible via instrumentation or other forms of visibility.

Why Obvious Programming?

Writing code in an obvious manner is important because among other things, compilers and static analyzers work better with obvious code, and our ability to understand what these tools say to us is improved by writing obvious code. When the code is broken into smaller expressions errors tied to line numbers are intrinsically more specific. A more complex expression that takes the place of many obvious lines of code would be a magnet for error messages. Understanding which part of the expression needs to be adjusted requires time that obvious programming doesn’t.

Writing obvious code makes it easier to instrument the code. Instrumentation is, well, instrumental to obvious programming. Making it easier to instrument also means it is easier for other engineers to understand. Instrumentation is like living comments that run when the program runs, but they also serve as inline comments in the code which the programmer can use to understand what the code is doing. When I instrument code I write fewer static, inline comments, but not none.

Writing obvious code makes it easier for other engineers to read. Minimizing the number of lines of code is a juvenile expression of skill that sacrifices a great deal of value for the pleasure of embracing more complexity and a false sense of being admired. The problem with minimizing lines of code is that it creates fragile code that is very difficult for other engineers to take over and develop further. When problems arise in complicated code without instrumentation the combination creates a perfect storm of ignorance and complexity that is more than enough to utterly kill a software project.

What do you think programmers are made of? If someone has to be me before they’ll understand my code, it’s obviously *their* problem that they can’t understand my code, right? ;-)


The thing is, obvious code looks banal. Nobody looks at it and says, “My, what a smart person the author was. They way they crafted that expression shows the depths of their understanding of the language and the wisdom of their foresight, because surely this expression will stand for all time!” Nobody thinks anything remotely like that when they read obvious code. Instead, they’re probably just reading and understanding the code and getting their job done. The author doesn’t really matter because the code can be understood easily enough.

And, when it comes time to change obvious code it isn’t as hard to figure out where. Sure, you might end up throwing away a chunk of it when it doesn’t apply any more, or when there’s a new and better way to do the same thing, but you’d be throwing away more complex code for the same reasons. The difference is that obvious code cleaves better. Individual expressions don’t need to have surgery done on them, or if they do, they are smaller expressions and the surgeries are more reliable. Whereas, larger expressions are more liable to cross the boundary of a change and the expression will require surgery to separate out the parts that are being saved or changed or deleted.

Obvious programming involves fewer expression surgeries and more adding and deleting of lines of code. Such changes merge better and are arguably lower risk because there is no chance for typos in deletions or errors when transforming complex expressions.

Instrumentation


Obvious code without instrumentation is also a black box. You can only understand its behavior by external observation or the debugger. I consider instrumentation to be an essential part of obvious programming; however, I also leave some trivial functions without instrumentation because they worked the very first time they were written. I write a lot of code that works the first time it runs. I write a lot of obvious code - many days I write about one thousand lines of code that all works together. It is all obvious code, and most of it is instrumented.

Instrumentation is part of obvious programming because it is what makes the process obvious and the data obvious. When the code, data and processing are all obvious, then “bogosity is minimized” (the degree to which things are bogus are at a minimum). This is where it also becomes most obvious how to proceed with any clear task or such as enhancement or problem solving. It’s obvious because obvious is easier and more reliable.

Obvious code fits very well with test-oriented development. Test oriented development works from a failed test toward a working test and along the way the feature is implemented or problem fixed. When it is done the feature is ready and there is a mechanism to regression test it in the future when new features are added. Instrumentation can also illuminate the path from nonexistence through feature-complete. It tells the story of the data and the code at every step (assuming it is instrumented). It evolves with the software so all versions have instrumentation. As the feature is developed it first fails early. As more work is done it fails later and later in its process. The instrumentation is how the programmer sees their own progress, in addition to helping to point out where the problems are.

I don’t think it takes more time to create a project using obvious programming, because a smaller percentage of the project is spent dealing with the ramifications of not writing obvious code.

Essential Requirements

I think obvious programming also requires a focus on the essential requirements of a project. These are a relatively short list of assertions that express what the project will accomplish for users when it is finished. For example:
* There must be a way for a user to sign up via a web page and set a password
* Users must be able to log in using their login name and password
* Users who forget their password must be able to trigger an email message with an authenticated link that allows them to set a new password
* Users who have logged in may create a new widget
* Widgets have settings that can be adjusted by the user who created the widget
** Color
** Size
** Flavor
** Opacity
** background image or movie
** URL of external resource
* Widgets can be sent to other users via a “Share” button
* Users may upload a background image or move or specify one via a URL
* When exported and included in a web page the widget displays its content and when clicked it goes to its specified URL (URL of external resource).
* Widgets can be exported as an HTML5 string via an “Export” button
* Widgets can be displayed in the current page via a “Try it now” button
* Widgets can be deleted by the user, but it requires confirmation via a popup
* There must be a screen that lists all widgets a user has created
*The columns must include:
** checkbox for mass operations
** name
** icon-sized version of the background image or movie
** date added
** date last updated
** export buttons
* Mass operations for delete and export (deletes or exports selected widgets)
* The column titles of the list must be clickable and when clicked the list is sorted by that column.
* Clicking the same column title again reverses the sort order.
* Clicking on a widget allows the user to edit the widgets properties
* When editing properties must be saved before they affect “Exported” widgets or the “Try it now” feature
* There must be a way for a person to close their account
* There must be an administrative report available to an administrative user that shows:
** List of users, number of widgets, disk spaced used by user, date of last use
** Total number of widgets
** Total number of users
** Total disk space used
* The admin user must be able to suspend a user account, which prevents login from working and prevents the user from setting a new password
* The admin user must be able to un-suspend a user account.
* The admin user must be able to delete user accounts that have been suspended.
* The system must be able to be scaled up to one million users by using multiple servers

That is a set of essential requirements for a web-based tool that makes HTML5 widgets that can be embedded in a web page and acts like a link. It shows an image or plays a movie and when clicked it goes to whatever URL it is configured to go to. A user creates these widgets via a web browser. They can create them, list them, edit them, delete them. An admin can suspend users or delete accounts and get various reports of usage.

All in all this is a straightforward project. Larger projects have more of these “essential requirements”.

The goal of essential requirements is to create a transparent social contract between those doing the work and those who wish the work to be done so that when the work is done everyone is happy with the results. The more accurate these essential requirements are and the more condenses there is for them the happier people will be with the results.

Word for word there is no more valuable documentation in a software project than these essential requirements.

Obvious programming fits with this approach because even obvious programming must still follow some larger vision. When that vision is clear and accurate and shared then any code of any kind will be better. But in particular, a clear understanding of the problem and the criteria for success that essential requirements provides means the programmer is less likely to introduce unused complexity in the form of unneeded data-driven code or code that could afford to make more assumptions than it does. The essential requirements define the assumptions.

Of course things change and sometimes one needs to add an abstraction later, but enhancements to obvious code are easier than enhancements to complex code. It is best not to add unneeded abstractions until their essential requirements are clear, or at least, more clear.

Essential requirements allow the programmer to make reasonable assumptions that can significantly reduce the code and complexity needed to deal with the problem. At the same time, it also points out where assumptions cannot be made and more time and attention are called for.

With obvious programming and essential requirements a software project begins with something that approximates the simplest vision that meets all requirements and expands to meet all requirements. Project requirements change. They become better understood and evolve, and interactions between different features finally become clear when they exist and interact with each other. So, it is simplistic to think that the initial essential requirements will be the final ones. It is equally simplistic to think that the initial implementation will be the final one. Code evolves over the life of the project and continues to adapt to remain as closely in sync with the latest requirements as possible. It is chasing a moving target from an engineer’s point of view. But, it is a very common experience in a software project.

Given that requirements and code is going to change over the life of a project, complex expressions will hinder that process in ways that obvious programming won’t. Obvious programming makes it easier to adapt code because the instrumentation makes it easier to identify where changes should occur and the lower complexity of expressions makes the changes easier to make.

Transforming the Power Structure of Software Projects

Finally, obvious programming and essential requirements together transform the power structure of a software project by adding transparency and accessibility. Chances are good that the essential requirements document is the only one that can be read and understood almost equally well by engineers and project managers and executives. The functional specifications which are more “accurate and complete” will often make non engineer’s eyes glaze over. But, essential requirements should be accessible to all.

Similarly, obvious programming is accessible to more programmers even those will little experience with the product or only modest experience with the programming language. The instrumentation provides both visibility while the program is running and when an engineer is studying it. So, it is easier for engineers joining a project to become productive and succeed. It is more typical for people joining a project to remain “inferior” to the original team members for a very long time, if not forever. That kind of class system, where programmers who join later are severely disadvantaged, is unhealthy for software projects and leaves them vulnerable to decapitation when the original engineers leave. Decapitated project have no team members who understand the system well enough to reliably enhance it.

One advantage of projects using obvious programming techniques is that they can level the playing field for people across cultures, gender and even skill levels, because the objet d’art is easier to understand initially, and it provides real information about itself as people work on it. People have fewer impediments to success.