Maintaining causality between design regularities and source code

ion level in the implementation Another property of structural source-code regularities is that they are generally prevalent at a particular abstraction level in the implementation. Similar to the work of Buschmann et al. [BMR+96] on pattern-oriented software architecture, we distinguish between three abstraction levels which regularities can occur at: 2.1. STRUCTURAL SOURCE-CODE REGULARITIES 15 • architectural level: regularities which express how the different subsystems of a system are related or may interact. One example of such a regularity is that of a layered architecture, in which an entity X in one layer can only communicate with an entity Y if Y is in a layer directly above or below the layer of X; • design level: regularities which encode how the different entities within a subsystem should interact. Typical examples of such regularities are the different conventions and dependencies underlying the implementation of design patterns; • language level: low-level regularities expressing a regularity in a particular part of a subsystem. Examples of these regularities are programming idioms such as the correct use of a double dispatch protocol, accessors for fields, . While the above abstraction levels are often associated with other software artifacts such as for instance UML diagrams which express the design of a system or the description of the architecture of a system using an architectural description language, we restrict ourselves solely to the source code of a system. As such, the abstraction level of a structural sourcecode regularity expresses the level of abstraction at which a regularity manifests itself in the source code of a system. Although it might seem at first sight that the scope of a regularity and its abstraction-level are correlated, this is not always the case. While regularities at the architectural level tend to be global, this does not imply that structural regularities at the language level are local. For instance, naming conventions, which are typically found at the language level, can describe a constraint which is globally applicable as well as dictate how program entities in a specific class hierarchy should be named. Similarly, regularities expressing the use of programming idioms can govern a global property (e.g. all field accesses must happen using a getter method) as well as specify the usage of a specific instantiation of an idiom (e.g. all “accept methods” in an instantiation of the Visitor design pattern [GHJV95] must be implemented by a double dispatch protocol). Applicability of structural source-code regularities Structural source-code regularities can be characterized according to their applicability. While certain regularities are quite universal and can be applied to almost any software system, there also exist regularities the applicability of which is limited to a given development team or one specific application or domain. • Environment-specific regularities: group constraints which are applicable to a wide range of software systems, independent of a single development team or the application-domain of the system. They generally encode knowledge concerning frequently occurring errors, language idioms, and so on. While they are not always applicable, their applicability is generally restricted to a certain implementation language, programming paradigm or implementation platform; • Team-specific regularities: these regularities express the coding conventions and naming conventions used by a specific developer or team of developers; 16 CHAPTER 2. STRUCTURAL SOURCE-CODE REGULARITIES • Application-specific regularities: describe the dependencies, conventions, inheritance constraints, . typical for a singular system or a particular application domain. Such regularities describe the constraints which must be adhered to when implementing an application-specific or domain-specific concern in a certain software system. For instance, a set of regularities which express that in a graphical framework the actions for drawing figures need to be implemented by means of the Command design pattern, are considered to be application-specific regularities. Note that the applicability of a regularity is strongly correlated with how often it is reused throughout different software projects. Environment-specific regularities often only depend on the implementation language used in a project or the platform which the application must run on. Therefore, throughout numerous projects, often the same set of structural source-code regularities will occur. Team-specific regularities represent the coding style and good practice patterns a team of software developers should adhere to. As such, they too are often reusable over multiple projects. Structural source-code regularities which dictate how certain concerns in a specific application should be structured or how they relate, encode knowledge which usually is not portable to other projects. Note however that even application-specific regularities often share large similarities throughout different software systems. Although the same instance of a regularity is not applicable to two software systems, these similarities between structural source-code regularities can lie at the basis of a kind of “pattern language” which describes how certain types of regularities can be documented. 2.1. 3 Taxonomy of structural source-code regularities based on functionality The properties of structural source-code regularities we discussed in the section above provide a characterization of the different kinds of regularities. In this section we discuss a taxonomy of structural source-code regularities found in literature based on the function the regularities serve in the development process. This taxonomy was presented by Chowdhurry et al. [CM93] and was later also adopted by Bokowski [Bok99]. Although in their work they use the term constraints over source code, this taxonomy is equally applicable to structural source-code regularities. This taxonomy divides regularities into three categories: stylistic regularities, implementation regularities and design-level regularities: • stylistic regularities: this category encompasses all regularities which are concerned with the use of names in the system or any other constraint that is semantics-neutral and governs a syntactical property of the source code. Such regularities are used to make programs more readable by enforcing a uniform look and coding style; • implementation regularities: this category consists of regularities which are used to detect infractions against frequently occurring bugs, pitfalls and programming errors. For instance, the regularity that all classes in Smalltalk which implement the = operation must also understand a message hash, is an implementation regularity. Other examples of implementation regularities are for instance the constraints which must be adhered to for an application to run in a specific environment; 2.1. STRUCTURAL SOURCE-CODE REGULARITIES 17 • design-level regularities: rules governing the correct use of class hierarchies, components, frameworks, design patterns, . Design-level regularities are further subdivided into coding conventions, inheritance constraints and usage constraints: • coding conventions: regularities which structurally ease readability and maintainability of the source code. For instance, a possible coding convention in Smalltalk is that all accesses to instance variables must occur via a getter method; • inheritance constraints: these regularities express contracts between a class and its subclasses. E.g. a constraint like “all subclasses of ClassA must override a method methodA”; • usage constraints: such regularities express how certain classes or collections of classes are to be used. E.g. in an instantiation of the Factory design pattern [GHJV95] all clients must use the factory to create a product. 2.1. 4 Importance of regularities in the software development process In his seminal paper “No silver bullet” [Bro87], Brooks states that software development is inherently hard due to the complex nature of software systems and the lack of unified engineering principles which are prevalent throughout other engineering disciplines. As Minsky observed [Min96], the proper and meticulous use of regularities in software systems can be considered as a kind of engineering principle which aids in dealing with the complexity of software engineering. Regularities can be considered to be a kind of pattern which is present in the source code of a system and which describes how a certain concept, functionality or concern in the system is represented in the source code. One motivation for introducing such regularities in the source code is to improve the communication of the intention of one developer to another. Rigorous use of naming conventions and other stylistic regularities to create a uniform coding style makes the source code of the system more readable [Bec97]. When a certain concern is characterized by a number of regularities, and these regularities are upheld throughout the implementation of the concern, it becomes easier for a developer to identify and understand the implementation of the concern in the source code. However, as we discussed earlier, not all regularities are stylistic in nature. During the development of a software system, certain constraints, or regularities, must be upheld in order for the system to behave properly. Especially in libraries, frameworks, . it is imperative that certain regularities are not violated in order for the system not to show incorrect behavior [Bok99, Min91]. For instance, in a framework, certain usage constraints and inheritance regularities govern the correct instantiation of the framework. If these regularities are not upheld, an instantiation of the framework might behave erratically. Similarly, certain development platforms like J2EE impose different regularities to be respected in s