1. Introduction
Software evolution and maintenance is a major part of the software development process. For large scale software projects the maintenance costs are often much more than the development costs. In the software evolution process, three techniques are often considered: Reverse engineering, Reengineering and Refactoring. These three techniques are widely used throughout the software evolution and maintenance phase to ensure the software is maintainable and useful throughout its lifetime. In this blog post, I will discuss software maintenance in relation to the three techniques and provide my opinions on them.
2. Change is inevitable
Changes in software are inevitable, and these changes result in maintenance activities that can be classified into several categories [1]:
-
Corrective maintenance: concerned with defects or problems found in software after deployment.
-
Adaptive maintenance: adapt to changes in requirements to ensure the software product meets the user’s needs.
-
Perfective maintenance: support the addition of new functionalities on top of existing system architecture or improve the existing system’s performance and reliability.
- Preventive maintenance: concerned with bug fixes to software after it is deployed to prevent faults occurring in the software.
As a result of these changes, we must continuously maintain the software to ensure it still remains useful and do not end up becoming a legacy system that is difficult to evolve and maintain. Furthermore, Lehman proposed a set of laws of software evolution [2]. These laws describe issues which usually apply to large scale real-world software which are constantly evolved and are used by many people.
3. Reverse engineering
Reverse engineering is the process of analysing a software system in order to gain an understanding of the system in terms of the design and specifications. The aims of this process are to generate detailed documentation for a legacy system to aid code understanding or to learn about design decisions during the initial development of a project. Once the system is reverse engineered, the design and specifications can be used as feedback to improve the existing system or as ideas for creating a new system.
The process usually starts off with manual code reviews or the use static analysis tools on a software system to extract information which is then stored in a datastore. The information from the datastore is then reviewed to generate relevant documentation which then allows us to understand the structure of the system.
Figure 3. Overview of the process of reverse engineering [3]
I have had some experience in reverse engineering, most notably from the system design project. The system design project was a special case of software development: once the project is finished, the development ended and the codebase was not maintained. The result was that it became a legacy system that was difficult to maintain and lacked documentation for future groups to reuse components from the system. In our group we used the idea of pair programming in XP to analyse what the code in different parts of the system were trying to achieve. In some cases, we had to use our initiative and knowledge to understand the components due to badly written and confusing code.
In my opinion, the development of the system design project relates well to Lehman’s second law of software evolution: Increasing Complexity. For example, the idea of adding new functionality to a robot (for a better chance of beating other robots in a football game) means the software becomes more complex over time thus requiring additional work to maintain and reduce the complexity of the system.
4. Reengineering
Reengineering is the process of creating a maintainable system from a unmaintainable, legacy system, it is often seen as a large scale version of refactoring. The process of reengineering involve tasks such as: source code translation (porting the software to a different platform which uses a modern high level language); restructuring or rewriting of software components to aid understanding and documentation. A few advantages of reengineering include:
-
Reduced risk: applying reengineering to the existing system rather than re-developing the software from scratch means we don’t need to define new requirements or specifications which could be ambiguous if they are not clearly defined.
-
Reduced cost: reengineering is a lot cheaper than re-developing software from scratch with a reason that staff do not need to be reallocated to do different tasks or tasks they are not familiar with which results in additional training.
However, reengineering does not apply to all situations. Back to the example of the system design project, we had looked at the different components of the codebase such as: communication – how the bluetooth communicator sends commands to the robot; I2C Multiplexer board [4] – controlling the speed and direction of the motors for the robot and the various vision libraries that can be used to provide orientation for the robot on the pitch. Since there was a lack of documentation and that reading code is more difficult than writing our own, we decided to re-write our own component for the I2C Multiplexer. Another reason for this was that we had used different style motors which required additional tuning of speed constants that differed from the previous year’s group. The result of re-writing the component saved us time and effort rather than having to completely restructure the original component.
5. Refactoring
Refactoring is the process of cleaning up code to improve the internal system structure without changing the functionality or behaviour of the system. The result of refactoring has many benefits such as: improved readability and understanding of code; increased flexibility of a system allowing new functionalities to be added easily.
However, in some software companies where they are under pressure to deliver a piece of software on time, the aim might be to “do the simplest thing that possibly works” which often result in refactoring being ignored with arguments such as “we don’t have time to refactor” or “refactoring might break the system”. My response to the former argument is that refactoring is worth the time and effort which pays off in the long-term maintenance of the project. Without dedicating effort to refactoring, we may have a case in which a lead developer leaves the company causing the entire project to be scrapped – no one else in the team is able to understand the internal structure of the system as layers upon layers of functionality have been added (making the system more and more complex) resulting in eventual failure of the project. For the latter argument, the use of fully developed test suites will ensure that when we refactor the code, the behaviour of the system is preserved. This is done by running the tests on the system after each modification to ensure the restructuring hasn’t affected the system.
From my experience of developing an Android app for my final year project, I realised that refactoring lays the foundation for building a good quality application. I often had to refactor code in order to build more complex features which otherwise led to increased coupling and duplication of code. As an example, during the development of a LocationManager component [5] to detect changes in the user’s location, the functionality was initially added to all other components which required access to location data. This introduced lots of duplicated code for each component that included this functionality which made it difficult to maintain – when the LocationManager component was updated the rest of the components all required updating. After refactoring, the LocationManager component was implemented as an interface allowing other components to implement its features as required.
6. Conclusion
In this blog post I have discussed the techniques of Reverse engineering, Reengineering and Refactoring in general software development and in relation to my experiences in both the system design project and my final year project. Reengineering is especially useful in large-scale software development where the risks and costs of re-designing a system are high whereas Refactoring should be done as good practice in all areas of software development.
References:
[1] Wikipedia. Categories of software maintenance: http://en.wikipedia.org/wiki/Software_maintenance#Categories_of_maintenance_in_ISO.2FIEC_14764
[2] Lehman’s Laws of Software Evolution (Section 2.2.2):
http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=4659256
[3] Software evolution, Reengineering and Reverse engineering: http://opencourseware.kfupm.edu.sa/colleges/ccse/ics/swe316/files/2_17_Software%20Evolution,%20Reengineering%20and%20Reverse%20Engineering.pdf
[4] I2C Multiplexer: http://www.inf.ed.ac.uk/teaching/courses/sdp/i2c-motor-multiplexer.pdf
[5] Android LocationManager: http://developer.android.com/reference/android/location/LocationManager.html