Friday, April 23, 2010

Designing Code

Designing code is one of the most essential jobs of a programmer. That is, programmers do more than just design code. They also write it, document it, and read it. Hence, the term writing code. You never hear a programmer say they're going to spend some time designing code. It is writing code that matters. You could design software for several months and it wouldn't make a world of difference in the mind of a programmer. If no code has been written, no progress has been made.

This is where programmers actually are designing, while they are writing code. When they are sitting there with an editor open, they can see what needs to happen in order to solve the problem at hand. In fact, it is even easier for programmers to debug existing code rather than start from scratch and right something completely new. There is something about code that enables us to see structural and behavioral design and that serves as a reference in our mind.

But every programmer has a different logical representation of the system at hand in their own mind. Two perfectly acceptable solutions to a given problem may be completely incorrect to two opposing programmers. How can this be? Even in the same programming language, two wildly differing solutions are available to solve the same problem. The problem is that in software development, the problem at hand is never just the problem given to us. There are too many environments in the computer universe in which software may run.

These problems given to programmers to solve are only the half of it. The stakeholders really don't care that system X doesn't support interpreter Y and therefore cannot use language feature Z. Programmers will find a way around these limitations. But the problem is that unless you are the programmer doing the implementation, you really can't wrap your head around the full details of implementing a full solution. It really comes down to the fundamental software development concept of having a separation of concerns. Stakeholders in software development projects don't necessarily need to know all the low-level implementation details. They just need to be aware that they exist.

This isn't always the case with stakeholders. Imagine you had an environment in which to implement their solution aligned with what the stakeholders sometimes envision; you write the code to solve their problem and there are no implementation surprises. In a scenario like that, writing code to directly solve their problem is a feasible approach. But that will most definitely not happen any time soon, if ever. So in the mean time, we are stuck dealing with implementation details that fall outside the scope of the business problem.

When programmers write code, they have two tightly-coupled problems. There is the problem of making the software do what it was intended to do, the business problem. There is also the problem of the the other, unforeseen implementation details, the implementation problem. When we begin to write code for the business problem, the implementation problem doesn't really exist yet. How can it? These are unanticipated problems. They don't come into being until you can see them. This makes sense to a certain extent because if you start thinking about potential implementation problems too much before some design activities have started, no code would ever be written.

So what is the real benefit to separating these two problems, if any? It doesn't seem very obvious what the exact benefit of separating the business problem from the implementation details is at first. But one problem with not doing so is apparent. Since implementation detail problems do not really exist until the code is being written, they often affect the business problem implementation once they come about. That is, if a programmer spends time designing some ideal code for the business problem, depending on how specific that design is, an implementation detail problem could put the whole design in jeopardy.

Since code is so tightly coupled with the implementation of a running system, a good way to represent the business problem is with a model. A model is not part of the running system but it is an artifact of the process used to create the running system. UML models are a good way to represent any given software system. They can show almost any level of detail one might be interested in seeing. There is, however, a similar problem with models. You have the ability to start getting into implementation details.

This goes to show that modeling your code before actually writing it isn't a magic way to fix your code design. The common problem with creating models is that they tend to evolve too quickly without writing any code. If the level of specificity in your model gets too high, you've not only taken a waterfall approach but you're also taking a risky path by assuming there will not be any seemingly small implementation issues. Models are a good way to start off the business domain model as long as they are general enough. The level of specificity cannot be too high before code is written. Even then, it doesn't need to delve into too much detail just for the sake of it. Model details are better left out until they become a requirement.

Now lets take a step back and think about this for a moment. We now have a business problem that we're going to solve with software. In order to build this software, we need to write code that will implement the solution to the business problem. We know that there are going to be changes because our computing environments are disparate enough that we has humans cannot predict exactly how our code will behave. In order to help distance our business problem from any potential implementation problems, we build a high-level model of the business problem.

We should now be all set to start coding and hope for the best, right? Starting to code is definitely the right answer. Hoping for the best suggests an uncontrolled level of uncertainty. This can be at least somewhat controlled by expecting unexpected implementation details. Remember that your code and your model will always change throughout iterations of the project. The model you build will also have some overlap with the implementation. This is necessary unless the stakeholders are only expecting a model of the software and not the software itself.

Once these implementation detail issues do exist, is it worth modeling these issues? Only if it is separated from the business problem within the model somehow. It might even make more sense to create a completely separate model for implementation details. But only if it adds value. Modeling the nuances of implementing software isn't an exact science because chances are, these same details will become irrelevant in five years.

To sum this all up, remember that there is value in upfront code design centered around the business problem at hand. This doesn't necessarily mean modeling, it could be as straightforward as prototyping a command line interface simply for the purpose of testing how well the abstractions in your domain work. Modeling does offer a visual design aspect that often cannot be gleaned from code itself.