Day 20: Spend More Time in Maintenance Mode

Software maintenance is traditionally thought as a process that will happen in the distant future, and will be needed only during bug fixing operations and to keep an existing piece of software current with new versions of a compiler or operating system. Maintenance is also viewed by many as an afterthought that doesn’t need to be dealt with until something happens and there is a clear requirement to modify an existing application.

The hidden truth, however, is that maintenance is an ongoing process that happens during the whole life cycle of the application. It is not only necessary after the system has been delivered but, on the contrary, it happens even after the first line of code has been created.

From the point of view of a developer, any code that has been created, doesn’t matter how long ago, is in maintenance. Of course, if it is fresh in your mind, the task becomes easier. Just edit the file in question to update a function, for example. However, in many situations the process may not be so easy, even if the software has not been released yet.

Keeping the content of a program in your head is just one of the challenges of becoming a programmer. Today’s software is huge, and all that complexity must be addressed on a daily basis. Even the best programmers will have some difficulty to remember exactly what a method or class does in particular.

That is why it is so important to write code as if it were in constant maintenance mode. The first thing that this implies is that you need to make it easier to understand what a piece of code is doing. That’s because, most of the time, the maintainer of the functionality will be yourself. Without good practices it is easy to forget what a class or method is supposed to do, for example.

Entering Maintenance Mode

The number one goal of maintenance is to make things obvious. Despite this, a lot of code we have is still created in “write-only” mode: a programmer creates something that may work (or not) but the result is so difficult to understand that it is easier to rewrite the whole thing than modifying it.

A major flaw of many developers is writing code that is hard to read and modify. Just notice that if something is hard to read, it will be difficult to understand, even if it was written by yourself. This is even harder to deal with in software that was written by somebody else, but don’t forget that it can happen on your own code.

For example, clever algorithms may be completely clear for you when they are fresh in your memory. However, after a few months (or even days), what was a clear strategy may now seem as a complicated mess. It is important to look at the future readers of your program as a real audience that you need to address.

Try to answer the following question as you write code: would someone else understand what this class or function represents? Even though it may seem easy to answer such a question as you are in the middle of a coding session, what about doing the same thing 6 months from now? If there is any double about the answer for this question, spend more time trying to make the code as clear as possible. Remember that it is better to spend a little bit more time describing what you mean now than spending several hours later to understand what is going on.

The same idea is valid for fixing bugs. Why not spending a few minutes checking the code that you just wrote, while the algorithm is fresh in your mind, instead of doing it when a client is waiting for your answer? Catching mistakes in code is so much easier to do right after you wrote some code that it doesn’t make any sense to wait any longer.

Performing Regular Maintenance

The other maintenance technique you can adopt is to be proactive in everything you write. Sometimes there is no time to identify every problem in a program. However, it is still possible to spend at least a few minutes every day doing maintenance work. I would recommend to use any available down time to perform regular maintenance tasks, such as:

  • creating test cases
  • removing dead code
  • fixing code formatting
  • checking for simple mistakes
  • improving the build system

These are tasks that, over the lifetime of a project, will amount to a huge difference in terms of software quality. If only a few bugs can be removed by this simple process, it can result in a dramatic reduction in future maintenance costs.

Conclusion

Maintenance is considered to be an afterthought by many developers. However, instead of pushing this task to the future, it makes more sense to embrace maintenance activities during the life cycle of the project. It is easier to perform such changes when the code is still fresh in your mind. It is also much cheaper than doing this after a major bug has appeared in the application.

Avoiding Feature Bloat

When designing a new system, it is frequently necessary to make decisions about specific features to be included. Less frequently, however, one also needs to decide to remove features that are not necessary or cannot be supported by the development group.

Such decisions are important because they shape how the application evolves over time. Simplifying an existing system is sometimes even more important than adding features: a system that has a lot of unnecessary features may be hard to evolve and maintain. It may also hide an uncommon number of bugs.

Unnecessary features are poisonous for maintainability. Not only it costs more to maintain. Feature bloat also causes the system to keep bugs for a long time, simply because the affected code is not frequently exercised.

I have found over the years that, after an initial design, it is always helpful to determine what can be removed from the list of requirements of a project. This gives designers the great opportunity to consider what is really important and what is merely useful but not necessary for the central functionality of the product.

What to Remove

A number of approaches can be taken when considering such feature reductions. First of all, imagine what would be like to live without a feature. If you think a functionality is optional, just remove it from an initial release. In general, the first release of a piece of software should contain only the most fundamental functionality.

Having non-fundamental functionality in an initial release is a sign of a number of weaknesses. First of all, it probably means that you should have released the product earlier. Doing so would have resulted in additional feedback, which is always useful to improve the application. If the core functionality is not good enough, then there is no point in releasing additional features.

Releasing early is also a good way to get feedback from users on what they really want. Many users will gladly tell you what they think of a product and how it can be improved. This is the best type of feedback, because it comes from someone that has a true interest in using your software.

All things being equal, it is better to have users that will start with a software that contains only the core functionality, and have them tell you about the additional features they need. If there is interest in your product they will get back with ideas for improvement that can be introduced into the next version. On the other hand, if the core functionality of the product is not important for users, then you will probably have no real need to continue testing and improving those features.

Conclusion

Designing a complete product with lots of features may seem attractive at first, but it usually doesn’t work because you don’t know in advance the customers’ real needs. It is generally better to remove features that are not part of the core functionality of your system. This can not only save precious development time but also allow you to focus on what really matters in an application.

Day 19: Avoid Singletons

Design patterns exist to solve common problems found on the development of object oriented software. As such, the singleton pattern exists to satisfy the need for code that is available to the whole application. It also helps when we want to enforce that only one instance of an object exists during the lifetime of the application.

The problem with the use of singletons is that the requirements above contradict a lot of the paradigms of object orientation. First of all, if some code is available to the whole application without creating objects, it can just be viewed as a structured piece of code, which could be easily implemented as a set of functions.

If one doesn’t need to create or destroy objects, then there is no need to use an object in the implementation, unless you’re using a language such as Java that has only objects.

As seen above, singletons don’t pass the ‘smell’ test of a pattern for the creation of solid code. Despite this, singletons were once a celebrated design pattern. A lot of singletons can be found everywhere in modern code. It is necessary, however, to be aware of some of the problems you will find when using them:

  • Singletons are hard to test: Since it is difficult to have any control about how the singleton is created, testing of a singleton in a controlled setting becomes very complicated. Sometimes, a singleton assumes a lot about the programming environment to work properly, and as a result it is not possible to create one of them in isolation.
  • Hard to reuse: for the same reason given above, singleton’s are hard to integrate to other code. They become non-reusable because they have to assume so much about the environment of any single program.
  • Allow indiscriminate use of resources: another big problem is hat there is no simple way to localize the use of a singleton. Once it is available, it can be used by the whole application, by definition. This has implications in the areas of safety, as well as in avoiding mistakes in the use of this shared resources.

Alternatives

Although singletons are in use in so many places, it is possible to avoid them in many occasions. Here are a few tactics that you can use to get rid of unnecessary singletons.

  • Convert into standard classes when possible: sometimes it is not so much work to convert a singleton into a traditional class. Just make sure that the initialization is done only when the first object is created. If there is a single resource that needs to be shared, for example, a network connection, you can use a static member variable to store that resource. In this way, you can have multiple objects that are still referring to a single resource.
  • Use static member functions in some cases: sometimes a singleton is improperly used when just a few static methods would work. This is a case when it is easy to remove the singleton, because there is no central resource that is shared. While static methods are not the best thing in the world, they at least are easier to test than a monolithic object.
  • Determine where the singleton is used. Sometimes it is applicable to just a small part of your app. When that happens, create a class that may used only by that part of the application. For example, if you using C# that might be a class visible only to the package. In this way, you avoid the indiscriminate access provided by singletons.
  • Factor the singleton into two or more classes: sometimes the options above are not available. But it is still possible to reduce the functionality of the singleton and provide them with two or more classes. A few of these classes can be non-singletons, which are easier to test. While this won’t solve the whole problem, it may simplify development and testing of the application.
  • Make the singleton as small as possible: As a last resort, use the tactic above to remove all the functionality that is not strictly necessary from the singleton. Even if you cannot test the singleton in isolation, you make it very small and easier to understand. This also remove the temptation of adding even more functionality to an existing singleton.

Conclusion

The singleton pattern has a number of deficiencies that make it difficult to integrate with other programming practices. Therefore, it makes sense to restrict its use only to situations when it is strictly necessary. You should investigate some of the ideas above to reduce or eliminate the use of singletons in your code, and as a result, improve the testability of the application.

Further Reading

The Gang of Four book is the original source for everything design patterns. You should check carefully the rationale for using singletons.