Monday, February 20, 2012

Decision Deferment

We have all seen the carnival ride where the riders are in a circle and the ride spins them around incessantly. The riders see the same distorted, blurred images flash past them over and over again. By the end of the ride the images are familiar, even in their blurred state. Eventually, the experience takes its toll and the riders begin to feel nauseous. Some riders feel this discomfort more than others, but all riders are aware of the feeling. Code can feel this way as well. We pass one test only to see another test fail. We type quickly and powerfully until the test is green. We run the suite and something else has broken. It begins to feel like a maddening experience where the test passes and failures blur together yet we can clearly distinguish a circular pattern. This is a smell and the smell should make us nauseous. It smells of needless complexity. We are hacking our way blindly through the code trying to pin down the one correct answer to the problem while leaving a tightly-coupled, complex, and unreadable wake. Code does not have to be this way. When we build our application out from the top down and defer hard decisions we can let the complexity come to us in small, manageable waves.

The top down development approach tells us that we can, and should, start at the top level of our application. We should express our intent in high level algorithms and relationships that are simple and easy to understand. If there is a hard high level decision to be made, we ought to make it now independent of the lower levels of implementation. We simply write low level interactions as if they existed in the system so that we are uninterrupted in the task at hand. When we feel comfortable with our design we move one level deeper. We view the current state and make decisions, once again, as if lower level implementation were available. Eventually, we reach the bottom and the implementation is agnostic to the higher levels. It simply does what it is told and we have avoided complex coupling between layers of abstraction.

Part of getting the top down approach right is to simply write what we say. If we have employees in our system and we want to aggregate the names of all of our employees we simply do that, exactly as if we were stating it verbally.
What is all_employees? It might not even be in the system! Right, it might not be, but we need it to get the names of all employees. Getting all the employees from some data store or central location is a detail that is outside of this algorithm's scope. Once we have finished with this algorithm we can move downward and collect all employees, again just translating spoken features into code. Coupling the idea of doing what you say with top down development is a powerful remedy to the typical complexity of systems.

If we have taken these two approaches to system building we have set ourselves up for the benefits of decision deferment. Why decide something before it is absolutely essential? After all, if we make the decision later we will be better informed about our system since more of the system will be built out. As the system falls into place we begin to find patterns. These patterns drive abstractions. As our system takes shape these abstractions allow additions to become simpler and better managed. Deferring decisions allow decisions to seemingly fall into place or, at the very least, help pin down a solution set to solve our problem. For example, I was recently pairing on a 'like' algorithm for a music playing application. If users like a song it would play more often. My pair and I spent a long time figuring out important ratios, playing with variables to establish good weights, ect. In the end, we sat down to code and realized that what we really needed was a workflow for the user to like a song and for the system to record and aggregate the user's likes. Thankfully, we did not dive straight into hacking up some weighting algorithm for an attribute that we did not have a familiarity with. Instead, we build the like aggregation first and moved on from there.

When we start to fall into the trap of chasing a solution we leave a trail of nested nasty garbage in our code. This garbage is a smell and we should avoid the chore of cleaning it up later if at all possible. Top down development and writing what we say allow us to realize the opportunities of decision deferment. Remember next time you feel as if you are on spinning ride of failure repetition to step back and avoid needless complexity.

No comments:

Post a Comment