Skip to content

Maintaining Software

Learning Objectives

  • Appreciate some of the reasons for modifying existing code
  • Know what we mean by the term "refactoring"
  • Appreciate the importance of documentation

Introduction

It is inevitable that, sooner or later, we will need to make changes to our code. The reasons for the changes can vary wildly, ranging from tiny changes to fix minor bugs to sweeping changes necessary to make sure our applications are compatible with new technologies. If we treat our code as a one-off project we are going to have long-term problems with it.

This sounds like a potentially tricky process but there is a lot we can do to mitigte that. By providing thorough documentation about both the current state of our projects and the history of the changes we have made we can help developers working on our code and those who want to use it in their own projects.

Refactoring - What, How & Why?

The process of rewriting exisitng code is generally known as refactoring. We don't generally refactor code for fun (although there are some games where we do), we do it to improve some aspect of it or to maintain compatability. For example, we may want to:

  • Fix a bug in our code
  • Replace an algorithm with a more efficient one
  • Modify the architecture to make it easier to extend our code
  • Respond to changes made to an external library which we are using

These are all important reasons but it is far from an exhaustive list.

How we go about refactoring will depend on the nature of the change. Small, quick changes can be done quickly but big changes need as much planning and organisation as starting a brand new project. In fact they often need more, as the requirement gathering phase has to consider actual use cases instead of hypothetical scenarios. Every planning tool we would use in a new project is useful here too.

It is important that refactoring is carefully managed and scope creep avoided. When we are working on a new application we are up against deadlines but they are the only thing which will be affected by delays. When refactoring we will likely be trying to address an issue which is affecting another system, meaning delays have a much more significant impact.

We should also remember the importance of testing when refactoring. We don't want to limit ourselves to testing only the changes we have made, it is important to test everything to ensure we haven't accidentally broken something. This is equally important when bug fixing or making larger changes, and applies to all types of testing.

Documentation

It's very easy as a developer to fall into the trap of assuming that our code is self-explanatory and that anyone could understand it. We have an advantage over other developers though: we have seen every step in our code's evolution, know why things are structured the way they are and were involved in every decision that led to its current state. For someone new to our project that knowledge is missing so we need to get them up to speed.

Documentation plays a critical role in that process. Written records of what our code does, how it does it and how it got into its current state mean everyone can get closer to the developer's level of understanding. Writing good documentation is hard, and there is no one-size-fits-all solution. The structure and format will vary according to the intended audience. The list below gives some examples, but is not exhaustive.

Change Logs

A change log does what the name suggests - it logs the changes made to an application with each update. They are particularly useful within a dev team but many organisations will also make them available in their user-facing documentation. This is especially useful when a lot changes between releases, or when a product has a large number of users with diverse needs. For example, GitHub display a change log on their front page with a high-level description of their updates.

GitHub changelog

They have a single entry per release with a level of detail appropriate for the end user. Maintaining a single entry per release is important but in our change logs the level of detail may be quite different. If we are writing for technical colleagues we may want to go into more detail about specific code changes or perhaps include references to relevant documentation elsewhere such as bug trackers. The important thing is that the content is appropriate for the audience.

Manuals

More complex projects need more complex documentation and much more than just a list of changes. Software is no different from many physical products we own in that having a manual describing all of its features and how to use them can be incredibly useful. Again these are useful both internally and externally, but this time the focus is more on the end user.

Writing a good manual is particularly challenging but many organisations find novel and entertaining ways of doing it. Front-end technologies have a built-in advantage here in that the manual can provide an excellent showcase of what the tech can do - the MkDocs generator used to build this textbook is a great example of this. Something every manual needs to do is give a thorough description of an application or tool's functionality, define any pre-requisites for using it and any limitations it may have. A manual will usually be a user's first port of call when looking for guidance and is often the only place they will look so it is vital that we are able to support them.

React manual

Many modern manuals go beyond simply defining functionality. The React manual shown above, for example, also includes tutorials to help a new user get started and build their first app. Many manuals will go slightly further and include guides to many common tasks. Links to other community resources are common too.

While the most recent version of an application or tool should always be documented it can be useful to maintain documentation for older versions as well. Many tools are incorporated into legacy systems which may not be updated for a long time, if ever, and the developers of those systems still need to be able to look stuff up. This is less of an issue when dealing with a complete application as we can limit access to old versions, but when writing a library for others to use we need to bear these legacy systems in mind. The React Router library is a good example of this. When version 6 was released it made significant changes to the library's structure and syntax which needed to be fully documented, but many users still need version 5 to support older projects.The front page of their website includes links to the documentation for both, although their tutorials and setup guides are directing people towards using the newer version.

READMEs

More or less every project will include a README file somewhere. There is debate around exactly when they were first used but they began to appear in the mid-1970s as a way of bundling instructions with code. A user would examine the files on a disk and see a file with an instruction in its name - README.txt. Opening that file would reveal written instructions to configure and run a program, often trouble-shooting tips as well.

README files are still an excellent way to get important information across to a user in a clear, concise way. There is no fixed length and no standardised way of structuring them; the contents will often depend on what other documentation is provided. At minimum they should describe any pre-requisites for using a tool or application (dependencies, OS version, etc) and any configuration steps, or at least provide a link to where that information can be found.

READMEs have found a particular niche on GitHub where they are the first thing displayed on a repository's page. They still often include all the "traditional" contents of a README but GitHub has features which let us add extra features such as CICD status, test coverage reports and download counters. If we are directing our users to GitHub, either to download our application or for support, it is important to have a good README to simplify the user's experience

Comments

Each of the above methods is an excellent resource for those learning about an application as a whole, but what about the people working on it? User manuals are less useful for a developer trying to follow their colleagues' thought process while adding a new feature, or for figuring out why a particular code block is written the way it is. In this scenario we can document our code in-line using comments. Comments are annotations we add to our code, highlighted in such a way that the compiler ignores them. The exact syntax varies between languages but the result is the same.

Further Reading

Refactoring

Change Logs

Manuals

READMEs

Comments