- Names
- Simplicity
- Warranted Assumptions
- How to Program by Intention
- "No Comment"
- Summary
HOW TO PROGRAM BY INTENTION
There are several techniques that can be used to enhance the communication of intent by your code. We explore them and how to use them. Some of these include: use of metaphor, test first, refactor, make assumptions, and let the compiler tell you what your next step is.
Common Vocabulary
Everyone involved with the project should use a common vocabulary to talk about the domain and the system. This can be based on the reality of the situation or it can be metaphorical if that eases understanding.
Having a common vocabulary helps us with two things:
-
Understanding the domain Because the activity of developing a complex software system involves people with different backgrounds and areas of knowledge it can be difficult to have everyone understand everyone else. However, to be an effective team we need to achieve a common understanding of the problem domain and the specific problem that the system is to solve. The difficulty here is that the technical members of the team will very likely not have a deep knowledge of the business. The opposite problem is making sure that the business people involved are able to have a clear understanding of what the system does, and to a large extent, how it works. Using a metaphor helps us bridge these two gaps by providing a description of the domain and the system that all parties can understand. It does this by providing a description in terms that everyone understands.
The original XP project at Chrysler is a prime example. The people at Chrysler understood factory and assembly line terminology. The programmers used that as a metaphor for the payroll system they were developing. An employee's paycheck was the product, constructed from dollar parts. It passed through various workstations, each of which performed some operation such as converting hour parts to dollar parts, taking a deduction, adding a bonus, etc.
-
Choosing names If we choose names from the common vocabulary, everyone knows what we mean. Having a common vocabulary means that everyone is using the same terms to mean the same thing. A great deal of confusion and miscommunication can be avoided, and a great deal of time and money can be saved. Not only should the terminology be used by everyone involved, it should also make sense in a fairly obvious way.
Test First
One of the biggest effects of working a test first is that we have a large suite of programmer tests that make it possible for us to refactor mercilessly. Also, by writing the tests first, we have time to think about what we need to do before we have to think about how to do it.
Another benefit to writing the test first is that when the test passes, we're done. This has at least two advantages:
-
it helps you work faster, because when the test passes we're done and can move on to something else, and
-
because we stop when we have the test passing, it's easier to avoid overengineering a more complex solution than is required. This improves the overall quality of our code, as well as making it smaller and more understandable.
Make Assumptions
This is closely related to writing tests first, but isn't limited to writing tests. You can make assumptions during implementations as well. By making assumptions as we work, we first decide what we need and what it should be called, and then create it. This lets us think about what we want to accomplish before worrying about the technical details of how to accomplish it. It also allows us to focus on the task at hand. If we made some assumptions that turn out to be invalid (i.e., what we assumed existed isn't there yet), we will find out when we try to compile. At that point we can deal with it; we have finished what we were doing when we had to make the assumption. The alternative is to deal with our assumptions as we make them. If we do that we risk losing track of what we were originally doing. Keep a notepad or stack of blank cards next to the workstation for jotting down notes and To Do items.
Refactor
By refactoring as we see the need for it, we can change what we have already done to make our intention clearer. Refactoring was discussed earlier in this chapter as well as in Chapter 2.
Let the Compiler Tell You
This technique helps us to defer work until it is required, by allowing us not to worry about keeping track of the assumptions we make. When we make an assumption which turns out to be false (i.e., the class, method, etc., that we are using in our code does not yet exist) the computer will inform us. In some cases (e.g., Java and C++) the compiler will report that something is missing and we will have to correct the issue (by stubbing the missing classes or methods). In other cases (such as Smalltalk) will we receive notification at runtime that a class, method, etc., is missing.
For example, consider the following test which was the first written for a project:
public void testEmptyListSize() { MovieList emptyList = new MovieList(); assertEquals("Size of empty movie list should be 0.",0,emptyList.size()); }
If this is the first code written in a project, compiling will produce the following message:
MovieList cannot be resolved or is not a type.
So we create the required class:
public class MovieList { }
Compiling again results in:
The method size() is undefined for the type MovieList
Next, we add a stub for the method:
public int size() { return 0; }
By leveraging the compiler like this, you can stop worrying about the assumptions you made that have to be dealt with. . . let the compiler tell you. This is also a way to avoid doing more than you absolutely need to.
Do the Simplest Thing
I've said it before, but it's worth repeating: Strive for simplicity. If code starts out simple it's easier to keep it simple than to make complex code into simple code. You may not always do the simplest thing, but your code will be simpler and clearer if you take the time to figure out what the simplest thing would be. An earlier section discussed simplicity in more detail.
There is a difference of opinion on this point. Some advise always doing the simplest thing that could possibly work. I agree that forcing yourself to do the simplest thing is preferred when you are just starting out. Get into the habit of being simple. Once you have that, actually doing the simplest thing is not as important as long as you are aware of what it is.