The-Software-Experts |
|
![]() catch your bugs! |
Principles of Code Refactoring
Refactoring grew in an environment of big company information systems which are programmed in Java or C++. These
systems need a good architecture to be able to integrate the sometimes millons of code lines programmed by various
individuals or teams. However the features of the object oriented programming languages tend to substitute more
detailed design rules. They are often regarded as being implicit in this environment. Therefore if you read e.g. Martin Fowlers book
on refactoring and also some of the other literature you will miss clear rules about how the refactored code should
look like. No doubt the recommended methods of refactoring are descibed, but it is left to experience and common sense
of the programmer to apply the appropriate method to the given code. Having worked in a similar environment for some time
I came across situations where a programmer would do a certain refactoring which another one would not have done the same way.
It is left to personal taste and experience how the refactoring is done.
Refactor Software to comply with Design Standards Refactoring should have clear goals. It is no good to just poke around in a code and see what you can do to make it better. There should be e.g. clear design guidlines which a code has to comply to and coding pattern which a code has to use. If you do not have such guidelines and pattern for microcontroller programming you should set them up as soon as possible.
You should try to improve the code to comply to these rules and standards, and stop refactoring when the goal is reached. Of course in the process of doing this, there will be enough places to do things smarter, better and faster.
Some more "Soft Goals" Other goals may be to achieve easier maintenance and improved understandability e.g. by adding comments or regrouping functions into other modules. You can split up functions or extract portions of a function to build a new sub-function. On the other end of the scale you can combine functions, to make one out of two. This may also involve the modification and improvement of interfaces, data structures and ways of programming an algorithm. Most of these items can not be easily described in a design guideline. Therefore I called them "Soft Goals". It all depends on personal experience and sometimes taste of the programmer. A certain amount of freedom is granted here for own ways of improving the code, however it never should violate clear design rules and resource constraints.
No Functional Changes by Refactoring Code refactoring has an improved design and code as its output and never any functional changes. The functionality of the improved code is the same as it was for the old code. Changes of the functionality in the progress of refactoring is strongly discouraged, even if it was found to be faulty. Finish the refactoring, make sure by sufficient tests that you did not add new functionality, that you did not reduce or modify functionality, and then work again on the functionality.
Set up sufficient Tests prior to Refactoring
Prior to refactoring the functional regression tests (repeatable tests) have to be set up. You should
use a suitable test environment for this. These tests will be reused to check the functionality of the
improved code.The focus should be that the development loop from changing
the code to compiling it and finally running the tests should be easy to handle or even be automatic. Further
the loop should be fast in execution and the comparison of the latest results produced by the refactored code
with the initial results should be also automatic.
Apply Refactoring prior to performing formal Tests You can not test quality into software. You have to design it into software. Although refactoring can not substitute a good architecture and design, it should be applied as an additional highly effective method to improve software quality. Refactoring should be applied prior to any formal software tests. Formal tests have the rule that the tester must not change code. He only executes the tests and reports the faults. I experienced great success to have some refactoring phases prior to executing formal tests. These refactoring activities were done in cooperation of the developer and the tester. It could be regarded as an informal test where the tester could bring in all his knowledge, modify and improve the code also from his point of view, without having the overhead of writing test reports etc. It helped to fix bugs, improve the structure and performance, and improve the maintainability and understandability of a code. We applied it in several iterations and eventually later on it made the tests a lot easier. Testing literature recommends informal software tests prior to a formal test to reduce the number of heavy formal test iterations. Refactoring, applied in a systematic and defined way, constitutes such an informal software test in combination with an instant bug fix and code improvement. The involvement of a tester during the refactoring also guarantees that the necessary regression tests which are needed in the process of refactoring are of better quality, since the tester usually has the theoretical background to achieve a good test coverage (boundary checks, equivalence classes, etc.).
Architecture and Refactoring Since refactoring is mostly concerned with individual portions (modules or units) of a complete software, it may not be able to improve the overall architecture of the software. You have to be aware of this! Refactoring comes in at a certain level of your desing. This is the module design level. Thus it can not improve the high level design (or better called architecture). If you missed to introduce a needed layer in your architecture, or if you scattered a certain functionality over several modules instead of providing a dedicated functional block, you are very unlikely to patch the problem by refactoring. You better should go first of all for a redesign of the architecture. It takes a real expert to detect this during the refactoring, stop refactoring and go for the architectural redesign, instead of turning out the programmer's pride to try and fix by a mason's job what would be an architect's job.
Resource Optimizations and Porting Software
In the microcontroller world you sooner or later come across the need to save resources. This could be done in one
of the refactoring iterations I propagated. However I frequently observed that a rework of the code to save memory
or runtime results in the same time in a loss of overall quality and in a loss of the other refactoring goals.
You should employ refactoring if you have certain desing pattern which are made to support resource optimization and
apply these pattern to all code portions where this is possible. This can and should be done in a refactoring. The
other way of saving resources is to reduce code lines. Less code lines means less ROM, RAM and time to execute. This
can be also done by refactoring which is always a friend of making things easier, smaller and cleaner. However you should
refrain from over-optimizing the code. There are ways of messing up a code which make it a bit faster and smaller, but
not maintainable and understandable any more. This contradicts refactoring completely. You should avoid this. Before you
plunge into an adventure like this you should re-think the overall system again. You should see if this is the only way
to save resources. In all cases I came across so far the real problems were at other places in the system which could be solved
more easily instead of messing up the complete code.
|
|