Day 26: Write About Your Problem

Problem solving is a very individual process. Everyone has a preferred way of thinking about problems, and as a result they come up with their own methods and strategies to find solutions. Programming is similar in that there are several ways of solving any design or implementation issue. It is also a singular field in which developers spend most of their time solving problems.

This leads to the necessity of cultivating problem solving strategies in the programming field. Most programmers have a crystalized process that is used frequently for solving similar classes of problems. Many of the differences between programmers have to do with how effective their solutions processes are. That is why I think that discussing solution techniques is a very important part of becoming a better programmer.

Writing as an Exercise

One of my preferred methods for problem solution is to use the old pen and paper to write down solution possibilities. Unlike most people who like to draw diagrams, I think that writing words that convey meaning about the problem is the shortest way to understand and finally figure out a solution.

Writing has several advantages, but they are compound when it comes to software development. First, software creation is mainly a symbolic manipulation activity, in many ways similar to writing. The software creation process certainly has its peculiarities, but it  involves describing the problem at a level that can be understood not only by computers, but also by human beings.

Another of the advantages of writing in the initial phase is the opportunity to build up a vocabulary. Playing with words to explain and try to find potential solutions for a problem can help in building this vocabulary in a quick and effective way. Later on, this might be helpful in identifying key elements such as classes, methods, use cases and other components of the system.

Writing During Initial Design

The design phase is the part of the development process where writing plays an obvious role. First, when designing you will want to convince not only you, but probably your boss and other members of the group that your approach is viable. In this phase, you will be interested in creating a formal document such as a technical spec for the work that will come.

Even if a formal document is not necessary, it is a good idea to create something polished and presentable. The reason is that this will become necessary eventually. Even when working on your own projects it will come a time when you need to explain the design to someone else. Having a design document can greatly simplify this communication effort between you and other developers.

Having a technical spec can also be of great help for yourself, because it may serve to clarify issues that would not appear otherwise. For example, if two or more systems need to cooperate for your design to work, a spec can help you clarify the roles for each component of the system. This way, you can refer to the resulting documentation whenever you’re ready to work on the implementation.

Writing as an Implementation Strategy

Another good use for writing is as part of the coding process itself. During this phase, one is interested not only on what will be done, but in the exact details of how some programing task can be accomplished.

A few people have perfected this kind of coding strategy. The best known of them is professor Knuth from Stanford. He was the creator of the literate programming technique, which is based on a simple idea: first write one or more paragraphs describing whatever a section of code is supposed to do and how it will be accomplished, then present the source code that achieves the desired result.

The great advantage is that, by writing first, you are thinking about particular issues that the code will have to deal with. Once you start writing the code itself, the whole strategy will be well developed. While doing this takes some extra time, depending on the complexity of what you’re implementing it may be a net win. I wouldn’t spend this energy to write software that I already know how it is supposed to work. On the other hand, writing complex functionality may benefit from the extra care, and literate programming can be a big win in this case.

Writing for Debugging Purposes

Debugging can also benefit from good, organized writing. The typical problem during a debug session is to track the reason why some unexpected event is happening. That may include things such as a crash, or a value that is printed incorrectly. Whatever the erroneous event you are trying to debug, it is easier to trace the causes if you write down the known reasons.

It turns out that writing a few paragraphs about how a bug happens may be the key to find its solution. The reason is that by writing about the problem you need to put the known facts in a logical sequence, and that sequence is frequently the key to understand how a bug is triggered. Composing a good description of how a bug is happening may provide all the elements necessary to pinpoint where the faulty code is.

For more complicated cases, I like to add some description of the involved code and the methods in the stack trace — if one is available. Then, it is possible to write down the sequence of events happening in the application from the point of view of the code being executed. Of course, I don’t need to do this for every bug, since it is a time consuming process. But for bugs that are difficult to solve, writing seems to be better than spending my time aimlessly staring at code.

Conclusion

Writing is a great way to make concepts clear both for you as well as for other people. As a development tool, writing has been explored by several programmers. For example, it is a major part of techniques such as literate programming. However, you don’t need to change your programming strategies to benefit from writing as a planning method. Just put your thoughts on paper, giving proper names to concepts, will increase of capacity of analyzing problems and uncover bugs even before they hit your code.

Day 25: Learning to Document Properly

Documentation is a polemic topic in programming because there are so many ways of doing it. While everyone agrees at some level that software documentation is important, there is rarely agreement between two programmers on exactly what needs to be documented and how.

Despite the misunderstandings, there are still some common practices that can be incorporated in a daily development workflow. To simplify, let us start discussing some areas of a code base where most everyone agrees that there should be extensive documentation: the public API. We will see why and how such interfaces should be documented, and then move to other cases.

Documenting APIs

If there is a part of a code base that really needs good documentation, that is the external API. This is specially important because, in many cases, the interface of the library is the only part of the code available to users. This is a common situation in languages such as C++, where header files can be distributed independently from the implementation sources. A similar case happens when only the Javadoc files are distributed for a Java library.

When the source code for a library is not distributed, the comments in the public API are the only way to determine how to properly use an interface — in the absence of a more extensive manual. Moreover, even if the complete source code of a library is accessible, API developers need to remember that it is sometimes a hassle to look up the implementation for an API — especially when a code browser is not available. Whatever the situation, looking at the implementation files should not be necessary to understand how to properly use an API.

When documenting libraries, it is very important to provide clear examples of how a method can be called, and if possible, the context in which such a call is correct. Sadly, many libraries come with documentation that provides detailed explanations for each method individually, but fails at explaining how that method can be used along with other classes to achieve a desired result. This is a frustrating experience for programmers, and can make your library harder to use, even if it is well designed and implemented.

Documenting Internal Header Files

Header files that are internal to the project have many of the same challenges presented by external libraries. In a sense, each header file is a mini API that defines how other parts of the library or application will interact with that specific section of the code. For this reason, programmers need to be careful to present a complete picture of what that module or class are trying to accomplish.

On the other hand, many internal header files have low importance when viewed along with other parts of the code base. Because they are mostly used as an implementation detail, several of these internal header files and classes don’t really require further explanation. Depending on the particular phase of the project, these files may be frequently written and modified, and are subject to changes that may render any documentation useless within a short time span. Therefore, providing extensive documentation for some private classes and header files may be completely unnecessary.

A common guideline when working with internal header files is to treat them according with their relative importance to the project. There are some small header files, containing only POD classes, for example, that don’t require any time spent in documentation. These are essentially dependent files, which receive their meaning from other, more important definitions in the project.

More important header files should receive better documentation, however. For example, classes that encapsulate fundamental concepts in the application should be fully document, sometimes with as much care as one would spend when writing external APIs.

Documenting Internal Code

Internal code is the easiest to handle, because developers have complete control over what it will look like. At the same time, it is the most contentious area in code documentation because each software author has a different idea of what needs to be documented or not.

General guidelines are still possible here. It is now understood that there is little value in trying to document “how” something is done. For example, the typical comment:

i++; // increment the counter “i”

should be avoided at all costs: it just adds more noise to the code, without contributing anything. Any C++ or Java programmer will be completely familiar with the meaning of the line of code above. It would be a different situation, however, if the comment tried to explain “why” the expression is necessary:

i++; // incrementing here because the next section assumes? // all counters have been updated

The comment is now useful. Its purpose is clear, and it explains in a few words why the counter needs to be updated at this particular location, and not after the next instructions are executed.

It is easy to understand the difference between “how” and “why” comments once you start asking these questions yourself. Whenever you are about create a new comment, think first: is this describing what happens or why it happens? The first type of comment should be avoided. The second type of comment may be useful enough to warrant its inclusion.

Conclusion

Commenting code is an stylistic decision. Every programmer has a different way to handle comments. Some developers never write comments, while a few others like to create small essays for each section of code (consider, for example, literate programming).

Despite the differences, comments should always be handled with proper care. They are more important when code is made available to external users, who don’t have access to the implementation. They should also appear in sections of the code where an explanation is necessary for a complete understanding — clearly describing why, instead of just how, something is done.

Day 24: Refactoring Code Frequently

Software is not a static entity that exists in a single, defined way. The great advantage of software over traditional engineering building material is that it can be modeled for different uses as necessary. This advantage, however, incurs in a penalty that is easily overlooked: it is necessary to maintain software during its life time so that it maintains the original qualities, even when facing changes in the way it is used.

This flexibility along with its associated maintenance problems can be easily seen in any non-trivial piece of software, be it commercial or open source. An application or library usually starts as a way of solving a particular need. As development progresses, however, the same software is frequently adapted to solve more and more complex use cases. As a result, code needs to adapt itself to these changing requirements, changes that sometimes modify important assumptions upon which the original code was written.

The main challenge faced by developers is to allow a piece of software to evolve in an organic way, so that it will be provide a response to these sometimes conflicting requirements. As an example, the same application that was once able to process a single file with a particular purpose may have, a few years later, be able to handle multiple files, each one with a different type of contents.

An Answer to Changing Requirements

Because of these difficult requirements, a lot of software artifacts just decay due to a lack of proper maintenance. As changes happen to the environment where it exists, a program will start to provide incorrect answers some of the times, or even most of the time. These are bugs that have been introduced by the very same evolution that is reshaping the program to be able to cope with different requirements.

Despite this, it is possible to maintain software properly even through big changes happen to its processing capabilities. Refactoring is the central concept enabling this adaptation on a daily basis. With the help of refactoring, programmers are able to slightly modify the way a piece of software works, so that it can satisfy specific characteristics needed by new requirements. At the same time, refactoring can be used along with unit testing to improve confidence on the quality of the existing code base.

Refactoring to the Rescue

Refactoring surfaced as an automated way of modifying programs, especially code written in an object-oriented fashion. Smalltalk was among the first languages to provide a refactoring facility, so that programmers could make small changes with confidence. It is important to understand that changes allowed by refactoring are usually minimal so that they could be easily automated. Examples include: renaming methods, changing the order of arguments in a method declaration and all method calls, or moving a method from a subclass to its parent class.

The main advantage of refactoring compared to other approaches for code modification is that refactoring is (or can be) completely automated. With the help of a refactoring tool, it is very simple to make changes to a code base that will make the program cleaner, while at the same time maintaining its correctness.

When to Use Refactoring

Another advantage of refactoring is that it doesn’t need to be considered as a task separated from coding. Good software engineers make liberal use of refactoring in their workflows. In fact, some people use refactoring very frequently, in order to provide one or more of the following:

Improving readability: a simple reason to use refactoring is to improve the readability of existing code. Sometimes it is possible to combine two or more statements and make a method much easier to read. Conversely, it is possible to split a statement in one or more, while at the same time improving the readability of the whole section of code. These are powerful uses of the capabilities of a refactoring tool.

Adapting to changes in the underlying code: sometimes the implementation of a new feature will require just a slight change to existing code. For example, that change may require a new parameter to be added to a method; or maybe an existing method will need to be moved to another class. Instead of doing this manually, one can use a refactoring tool to achieve the same results with less work.

Naming methods and variables properly: another good use of refactoring is making sure that methods and variables reflect their true meaning in the program. Sometimes, as a piece of software evolves, the names used in the initial implementation may lose their meaning, either because of business reasons or because other parts of the implementation have evolved too. Refactoring is a simple way to solve this issue, since it guarantees that all other parts depending on these methods or variables (including automatic tests) will be properly updated.

Conclusion

Refactoring is a powerful tool, and it should be part of any programmer’s tool chest. However, like any tool it takes some training to get used to it. Some of this ties with the general idea of properly learning your software environment. Most modern environments (command line or IDE-based) have provisions for running refactoring tools.

Also, refactoring tools are available for many modern languages, the most common ones including Java, Smalltalk, Python, and C++. Therefore, the use of refactoring has just become a matter of mastering the underlying programming system. The frequent utilization of such tools can make our lives easier, and our code even better.