Software Maintainability – Traits of Good Maintainability – Delocalization
What makes software maintainable? Rather than continuing the discussion of how to measure a system’s maintainability, I’m going to jump to an attempt to define it in AS/400 iSeries RPG terms. I don’t think I have all the factors neatly gathered at this point in time, so I will being posting what I have and add further posts throughout the week and in the future as the concept continues to take shape. Don’t consider this complete.
The standards I’m using to judge maintainability are based on the ISO 9126 standard for measuring maintainability of a system:
- Analyzability
- Changeability
- Stability
- Testability
The only one of those possibly not self-explanatory is stability, which in this case means that the software is not overly sensitive to being changed, i.e., small changes do not frequently cause large, unexpected problems.
So, in the following days I will post a list of key traits that impact maintainability and 1) a definition of what each is, 2) how it is manifested in the RPG world, 3) the impact of the trait and 4) guidelines for RPG coding. Today I start with “delocalization.”
Delocalization
Meaning: in order to follow the code, the programmer must jump to another source location in the current source file or another source file, e.g., when an EXSR is encountered the programmer must find the BEGSR and read the code there, and then eventually return to the EXSR.
Manifestation: Subroutines, procedures, calls to other programs are all delocalization. As a practical matter, looking up DDS for screens, reports, files, etc is also a form of this.
Impact: Delocalization has both good and bad results. The good result is that functionality can be neatly encapsulated with well defined coupling serving to create well-defined code impact boundaries. Another good result of delocalization is that well-designed encapsulation can greatly facilitate the programmer’s process of mapping real world concepts to the code.
The bad result is that research has shown that delocalization is a leading cause of errors in program comprehension and maintenance because 1) the programmer must interrupt his or her code reading train of thought to perform code navigation (which can be up to a third of his or her maintenance time) and 2) the mental model being constructed is made more complex, because, for example, the programmer must now consider the new section of code to be callable from any number of unknown locations, which probably must be analyzed, and 3) the control and data flow between the non-localized code must be understood and handled correctly. (that’s just to start with)
RPG Guidelines: At this point I am asking myself, how did I get into the position of attempting to write a definition of when code should placed in a subroutine, procedure or other program? This is not a simple question. A great deal has been written on this subject in software engineering, and I am not going to attempt a complete answer, but here are some key concepts to consider specifically for RPG maintainability:
Concept Mapping – how well does a business process map to the code being considered for modularization? A one-to-one mapping will make future program comprehension much quicker and more reliable. Most maintenance programmers engage at least partially in “top-down” modeling in which they attempt to map their knowledge of the business domain to the code. Recognizing that future programmers will be doing this, and organizing the code accordingly, will facilitate their effort.
Feature Location – similarly, maintenance programmers often incur the task of finding the code that implements a particular feature which is to be modified. The extent to which a feature can be isolated into its own module will facilitate this common maintenance process.
Testability – could the code being considered for modularization be tested in isolation? The ability to put the code in a “test harness” greatly facilitates future testing by maintenance programmers. In other words, could the module easily be temporarily made into callable unit for testing?
Well-defined interface – in legacy RPG code, calling subroutines provided no means of defining an explicit interface. Today the use of procedures and program calls do provide that facility, which is of course valuable and brings needed discipline, self-documentation and “typing” to modules. Internal subroutines, however, have no such language facility. A suggested practice would be to implement an internal code commenting standard to document all subroutine input and output parameters. Use of a good tool, such as Codelyzer, can also provide a description of a deduced interface.
Readability – does the comprehension of control blocks require extensive navigation? For example, to find the corresponding END statement for an IF, are multiple scrolls required? Ideally all IF blocks would fit on one screen page, though that may not always be practical. Creating more modules for the purpose of keeping IF blocks small is probably not a good solution as it just leads to more delocalization, which is, ummm, the problem we’re talking about.
MVC and tiered architectures – following well-established design patterns makes sense because 1) they have been proven in use, and 2) they are quickly recognizable by experienced programmers.
Options – one theory of software modularization holds that modularizing code such that it may possibly be used in the future in currently unplanned, unknown ways, creates an “option”. That option has value and if it can be obtained for little or no cost it is a good investment.
Module naming – Much research in program comprehension has shown that when programmers attempt to understand an unfamiliar program they rely heavily on “beacons” and their knowledge of existing programming practices. Ensuring that subroutines, procedures and programs are named in a manner of some recognizable consistency that facilitates quick grasping of their purpose or general nature will expedite future comprehension efforts.