combating architectural degeneration: a survey

14
Combating architectural degeneration: a survey Lorin Hochstein, Mikael Lindvall * Fraunhofer Center for Experimental Software Engineering, University of Maryland, 4321 Hartwick Road, Suite 500, College Park, MD 20742, USA Received 21 July 2003 Available online 10 March 2005 Abstract As software systems evolve over time, they invariably undergo changes that can lead to a degeneration of the architecture. Left unchecked, degeneration may reach a level where a complete redesign is necessary, a task that requires significant effort. In this paper, we present a survey of technologies developed by researchers that can be used to combat degeneration, that is, technologies that can be employed in identifying, treating and researching degeneration. We also discuss the various causes of degeneration and how it can be prevented. q 2004 Elsevier B.V. All rights reserved. Keywords: Software architecture; Architecture recovery; Architecture compliance 1. Introduction All software systems undergo changes throughout their lifetime as features are added and defects are repaired. As these systems are changed, they increase in complexity and bear less and less resemblance to the original system. In particular, as time passes the structure of the system can deviate substantially from the structure as originally designed. When this occurs in an uncontrolled manner and the system is no longer maintainable, the system has degenerated. In this paper, we discuss the problem of degeneration and survey the available technologies that can be used to combat degeneration. In Section 2, we discuss the problem of degeneration itself. In Section 3, we survey some technol- ogies developed by the research community that can be used to identify degeneration, repair degenerated systems, and provide further insight into the process of degeneration. In Section 4, we discuss the possibility of preventing degeneration by identifying possible causes of degeneration and existing technologies that can address these causes. In Section 5, we provide our conclusions and discuss future work. 2. Software systems degeneration The software architecture is fundamental to the long- term health of a system and can quickly degenerate making the system hard to maintain and evolve. There are several documented examples of this phenomenon. In 1998, Netscape made the decision to release their web browser to the community as open-source. Shortly after making this decision, the development of the next version of the browser started. The existing source code was used as the basis for the next version. After several months of work, the developers decided that the existing code was too difficult to work with and decided to re-write the browser from scratch. The effect was that the new web browser (called Mozilla) did not reach version 1.0 until 2002. Godfrey and Lee examined the architecture of the new browser in 1999 while it was in development and found that its architectural structure was very complex [1]. At the time, Mozilla comprised over 2,000,000 Source Lines of Code (SLOC) of CCC and C code [1]. Godfrey and Lee’s in- depth examination of Mozilla led them to conclude ‘that either its architecture has decayed significantly in its relatively short lifetime, or it was not carefully architected in the first place. For example, the top-level view of Mozilla’s architecture resembles a near-complete graph in terms of the dependencies between subsystems’ [1]. This example is doubly tragic. First, the existing code base was thrown away because it was too hard to work with, 0950-5849/$ - see front matter q 2004 Elsevier B.V. All rights reserved. doi:10.1016/j.infsof.2004.11.005 Information and Software Technology 47 (2005) 643–656 www.elsevier.com/locate/infsof * Corresponding author. Tel.: C1 301 403 8972. E-mail addresses: [email protected] (L. Hochstein), mlindvall@ fc-md.umd.edu (M. Lindvall). URL: http://fc-md.umd.edu.

Upload: lorin-hochstein

Post on 26-Jun-2016

216 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Combating architectural degeneration: a survey

Combating architectural degeneration: a survey

Lorin Hochstein, Mikael Lindvall*

Fraunhofer Center for Experimental Software Engineering, University of Maryland, 4321 Hartwick Road, Suite 500, College Park, MD 20742, USA

Received 21 July 2003

Available online 10 March 2005

Abstract

As software systems evolve over time, they invariably undergo changes that can lead to a degeneration of the architecture. Left unchecked,

degeneration may reach a level where a complete redesign is necessary, a task that requires significant effort. In this paper, we present a

survey of technologies developed by researchers that can be used to combat degeneration, that is, technologies that can be employed in

identifying, treating and researching degeneration. We also discuss the various causes of degeneration and how it can be prevented.

q 2004 Elsevier B.V. All rights reserved.

Keywords: Software architecture; Architecture recovery; Architecture compliance

1. Introduction

All software systems undergo changes throughout their

lifetime as features are added and defects are repaired. As

these systems are changed, they increase in complexity and

bear less and less resemblance to the original system. In

particular, as time passes the structure of the system can

deviate substantially from the structure as originally

designed. When this occurs in an uncontrolled manner and

the system is no longer maintainable, the system has

degenerated.

In this paper, we discuss the problem of degeneration and

survey the available technologies that can be used to combat

degeneration. In Section 2, we discuss the problem of

degeneration itself. In Section 3, we survey some technol-

ogies developed by the research community that can be used

to identify degeneration, repair degenerated systems, and

provide further insight into the process of degeneration. In

Section 4, we discuss the possibility of preventing

degeneration by identifying possible causes of degeneration

and existing technologies that can address these causes. In

Section 5, we provide our conclusions and discuss future

work.

0950-5849/$ - see front matter q 2004 Elsevier B.V. All rights reserved.

doi:10.1016/j.infsof.2004.11.005

* Corresponding author. Tel.: C1 301 403 8972.

E-mail addresses: [email protected] (L. Hochstein), mlindvall@

fc-md.umd.edu (M. Lindvall).

URL: http://fc-md.umd.edu.

2. Software systems degeneration

The software architecture is fundamental to the long-

term health of a system and can quickly degenerate making

the system hard to maintain and evolve. There are several

documented examples of this phenomenon.

In 1998, Netscape made the decision to release their web

browser to the community as open-source. Shortly after

making this decision, the development of the next version of

the browser started. The existing source code was used

as the basis for the next version. After several months of

work, the developers decided that the existing code was too

difficult to work with and decided to re-write the browser

from scratch. The effect was that the new web browser

(called Mozilla) did not reach version 1.0 until 2002.

Godfrey and Lee examined the architecture of the new

browser in 1999 while it was in development and found that

its architectural structure was very complex [1]. At the time,

Mozilla comprised over 2,000,000 Source Lines of Code

(SLOC) of CCC and C code [1]. Godfrey and Lee’s in-

depth examination of Mozilla led them to conclude ‘that

either its architecture has decayed significantly in its

relatively short lifetime, or it was not carefully architected

in the first place. For example, the top-level view of

Mozilla’s architecture resembles a near-complete graph in

terms of the dependencies between subsystems’ [1].

This example is doubly tragic. First, the existing code

base was thrown away because it was too hard to work with,

Information and Software Technology 47 (2005) 643–656

www.elsevier.com/locate/infsof

Page 2: Combating architectural degeneration: a survey

1 The Software Engineering Institute maintains a list of definitions for

software architecture and a related bibliography at http://www.sei.cmu.edu/

architecture/definitions.html.

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656644

and a new code base was developed. One can only imagine

the effort involved in such an operation, and it must have

been a significant investment. Second, despite this huge

effort, the new code base seems to have quickly degener-

ated, making it hard to work with and the investment less

valuable. Judging from the analysis conducted by Godfrey

and Lee [1], it is probably only a matter of time until

Mozilla has to be rewritten from scratch again, unless the

degeneration can be stopped before the code is completely

unworkable.

Another less dramatic example is found in the study by

Eick et al. who analyzed the source code across several

versions of a huge (100,000,000 SLOC), 15-year-old

telecom software system. They referred to the degeneration

problem as code decay, which they defined as the property

that the code is harder to change than it should be. The

authors sought to characterize and measure code decay,

which is a phenomena that is hard to quantify, but one that

most developers agree does make systems harder to

maintain over time. The authors analyzed change data and

were able to confirm that the system had decayed using a set

of code decay indices. They identified, for example,

inappropriate architectural constructs and violations of the

original design principles as two potential causes of decay.

Even if the result was not as dramatic as in the Netscape

case (scrapped software), the identified problems still

caused enough problems with maintaining and evolving

the system that something had to be done about it [2].

It is no surprise that large systems can easily become

hard to understand because of their size, but even small

systems can quickly decay and become unmaintainable.

Lindvall et al. studied, for example, how an unmaintainable

12,000 SLOC Java system was reengineered to better

encompass future changes. The reengineering activity was

expensive and sparked the creation of a process to monitor

and track changes impacting the software architecture. The

process helps assure that changes do not violate the

architectural design causing the architecture to degenerate

and again become unmaintainable [3]. In a follow-up study,

they used the monitor and tracking mechanism to show that

the reengineered system quickly tended to degenerate as it

evolved over time. New programmers, who made function-

ally correct changes to the system without following the

intention of the initial architecture, caused the degeneration.

The result was, for example, that new components were

added in a way that violated the principle for communi-

cation between components as defined in the architectural

specification, causing the maintainability of the system to

rapidly decrease. Thanks to the monitor and tracking

process, these violations were quickly corrected [4].

These are all recent studies of degeneration, but it is a

problem that has long plagued the field of software

engineering. In 1969, in one of the earliest papers on

software evolution, Lehman noted that ‘the main problem of

large systems is unintentional interaction between com-

ponents, that require changes to the components for their

elimination’ [5]. Lehman later formulated his observations

about software evolution into a collection of ‘laws.’

Lehman’s second law of evolution captures this phenom-

enon in a close analogy with the second law of

thermodynamics: ‘As a program is evolved, its complexity

increases unless work is done to maintain or reduce it’ [6].

Commenting on Lehman’s observations, Brooks noted in

1975 that ‘all repairs tend to destroy the structure, to

increase the entropy and disorder of the system. Less and

less effort is spent on fixing original design flaws; more and

more is spent on fixing flaws introduced by earlier fixes.

As time passes, the system becomes less and less well-

ordered’ [7]. Brooks drew the conclusion that all systems

will eventually require a complete redesign as a conse-

quence of this degeneration.

More recently, researchers have discussed degeneration

using a variety of different terms. In one of the earliest

papers on software architecture, Perry and Wolf describe the

problems of architectural erosion and architectural drift.

Architectural erosion occurs due to direct violations of the

architecture, whereas architectural drift occurs due to

modifications that are not direct violations but are never-

theless inconsistent with the architecture [8]. Parnas

discusses a more general phenomenon of the decline in

value of software over time, which he refers to as software

aging. Parnas identifies degeneration as one of the causes of

software aging: changes to the software that cause the

structure of the program to degrade [9]. Van Gurp and

Bosch use the term design erosion to describe the

phenomenon in which the design of a system becomes

less and less suitable to incorporating new features over

time [10]. In this paper, the term degeneration is used in a

broad sense encompassing the definitions above.

3. Software architecture

Many definitions of software architecture exist; however,

there is no universally accepted definition [11].1 The lack of

a standard definition often leads to confusion making a

discussion of the definition of software architecture

necessary.

Although there are many definitions of software

architecture, common themes and ideas run through these

definitions. Many of the definitions include the concept of

components or modules and the communication mechan-

isms among components. For example, Perry and Wolf [8]

define software architecture by describing three classes of

architectural elements: processing elements, data elements

and connection elements. Data elements contain the

information that is processed, used and manipulated by

Page 3: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656 645

the processing elements. The connection elements are the

parts of the system that connect the pieces of the system

together. Bass, Clements, and Kazman [12] describe

software architecture as the structure or structures of the

system comprised components and externally visible

properties and relationships among them. This definition

implies that the internals of each component do not play a

role in software architecture. Crispen and Stuckey [13]

define software architecture as partitioning and coordination

strategies. The partitioning strategy defines how the system

is divided (or composed) of components. The coordination

strategy defines how the components of the system interface

with each other. This definition for software architecture

incorporates the ideas of constraints and rationale for how

components are put together and how the communication

should occur. Including the ideas of constraints and

rationale is common in many definitions of software

architecture. Booch, Rumbaugh and Jacobson [14] also

include constraints in their definition of software architec-

ture. In several definitions, the concept of parameterized

components that can be altered to form new applications is

included. Bhansali [15] calls this type of architecture

generic software architecture. The idea of a generic

architecture seems to lead toward the concepts of archi-

tectural styles and design patterns as described in [16] (Also

Technical report, CMU-CS-94-166, January 1994) and [17],

respectively.

3.1. Definitions used in this paper

The definitions used in this paper are based on common

elements from the definitions of software architecture

described in the previous section. Software architecture

deals with the structure and interactions of a software

system. The most basic building blocks of the structure of

software architecture are components and the interrelation-

ships among the components. Constraints and rules describe

how the architectural components communicate with one

another.

Architectural components can be viewed at several

different abstraction levels and vary based on the size of

the system. At the highest level, the components are the

subsystems, which in the case of a very large system, can be

a complex system and can have subsystems of their own.

Subsystems are often formed by lower level components,

which are, in the case of an object-oriented system,

collections of classes. In order for the architectural

components to form a system, they must communicate

with each other, creating interrelationships or connectors.

The division of a system into smaller building blocks is

based on the philosophy of ‘divide and conquer’ which lets

the implementer divide the problem into smaller and less

complex pieces.

When viewed at the highest levels, a system’s archi-

tecture is referred to as the macro-architecture of the

software system. At lower levels of abstraction, it is referred

to as micro-architecture. Architectural styles guide the

structure and interactions of the system when describing the

software architecture of a system at the macro-architectural

level. Design patterns describe the structure and/or inter-

actions of a system at a micro-architectural level.

Garlan and Shaw define [16] several architectural styles.

One example is a client–server architectural style in which

the application is divided into two major subsystems that

communicate with each other. Other examples of architec-

tural styles include layered and piped architectures. Each

architectural style brings not only certain properties to the

system, but also implicit and explicit implications and trade-

offs that translate into design rules and guidelines. The

client–server architectural style, for example, divides the

system into a ‘front-end client’ and a ‘back-end server’

allowing for a separation of concerns on a high level. The

layered architecture allows encapsulation of low-level

functions and the establishment of an abstract machine

that builds on the services lower layers offer.

Design patterns methodically name, explain, and evalu-

ate important and frequent designs in object-oriented

systems. Design patterns ‘solve specific design problems

and make object-oriented designs more flexible and elegant,

and ultimately reusable. They help designers reuse success-

ful designs by basing new designs on prior experience.

A designer who is familiar with such patterns can apply

them immediately to design problems without having to

rediscover them’ [17]. Design patterns are sometimes

referred to as micro-architectural styles and contribute to

an organized system structure. In each system, several

design patterns can be used, and they can coexist with each

other and with different architectural styles. By selecting a

design pattern, certain architectural implications and trade-

offs follow. Some of these tradeoffs and implications are

described at a high level in [17].

4. Technologies for combating degeneration

In order to structure the discussion according to the

different aspects of degeneration, we borrow the medical

metaphor employed by both Parnas [9] and Eick et al. [2]

and discuss degeneration in terms of diagnosis, treatment,

research, and prevention. Diagnosis refers to identifying

when an existing system is degenerating. Treatment refers to

repairing a system that has been identified as degenerating.

Research refers to studying how systems degenerate to

better understand the process. Prevention refers to taking

measures during initial development so that degeneration

does not occur. Prevention requires an analysis of the causes

of degeneration, which we will defer to the next section.

4.1. Diagnosing degeneration

We say that a system is degenerating when the actual

architecture of the system deviates from the planned

Page 4: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656646

architecture of the system. Other examples of commonly

used terms to refer to the actual architecture are: as-is, as-

built, as-implemented, and concrete. The planned architec-

ture is often referred to as as-designed, ideal, intended and

conceptual. To conclusively determine if a system has

begun to degenerate, the actual architecture of the system

must be extracted from the source code and other system

artifacts and compared to the planned architecture to

identify any architectural violations.

4.1.1. Identifying the planned architecture

The planned architecture is defined by architectural

requirements, implicit and explicit architectural guidelines

and design rules and implications stemming from the use of

architectural styles and design patterns [18]. The details of

the planned architecture are identified by interviews with

architects and developers, and document analysis. Once the

architecture of the system has been defined, the impli-

cations, trade-offs, guidelines, and design rules are derived.

These guidelines are commonly iterated and updated as

more is learned about the planned architecture [18]. Specific

tools supporting this activity, other than regular tools for

documenting architectures using, for example UML, could

not be identified in this survey.

4.1.2. Recovering the actual architecture

The process of extracting the actual architecture from an

existing system is known as architecture recovery [19].

Architecture recovery is a subset of reverse engineering,

which seeks to recover information through static and

dynamic analysis of a legacy system. There are several

different categories of reverse engineering tools that can be

used for architecture recovery: filtering and clustering tools

(to identify components using function call dependencies),

architectural cliche identification tools (to identify com-

ponents using more sophisticated code analysis), and design

pattern identification tools (to identify design structures

which commonly occur in source code).

4.1.2.1. Filtering and clustering tools. Filtering and

clustering tools perform a relatively simple analysis of the

source code to extract use and dependency information (e.g.

call graphs, file dependencies). This information is placed in

a repository that represents a model of the system. Using a

tool, the developer can manipulate the model to generate

different views of the system to make it easier to understand

its architecture. Through a user interface, the developer can

remove units from the model that are considered unim-

portant (filtering), and can aggregate units (functions,

classes, files) into a cohesive set of modules that form a

logical high-level unit (clustering) [20]. Through an

iterative process, the developer can organize all of the

modules into a visually appealing representation. Typically,

graphical and scripting interfaces are provided to assist the

user in this aggregation activity step. Some tools and

techniques try to automate the process of clustering by

searching for ‘good’ decompositions. Note that the cluster-

ing process does not actually reorganize the source code, but

only manipulates the extracted model.

Rigi is an example of a filtering and clustering toolset

that has been used as the foundation in many other toolsets.

The Rigi system was designed to support the comprehension

of large information systems through visualization. It is

mainly used for understanding software systems by

extracting dependency information from source code,

manipulating this information, and generating different

views. As such, Rigi is well-suited to extracting the software

architecture. A developer uses Rigi to decompose the

system into a collection of hierarchical subsystems. These

subsystems can be visualized through interactive views,

which are graphical snapshots of different aspects of the

system. The rigiparse program parses C, CCC, or COBOL

source code, extracts dependency information among

modules (files, functions, and classes) and stores this

information in text files in a format called RSF (Rigi

Standard Format). Through an iterative, semi-automatic

process, a developer interacts with Rigi through the rigiedit

program, a graphical interface in order to decompose the

system into subsystems. Rigi assists the developer by

suggesting candidate subsystems that exhibit high degrees

of cohesion and low degrees of coupling. Rigi can also

suggest alternate subsystem candidates. Once the developer

is comfortable with a suggested subsystem grouping, the

modules are collapsed into a single node representing a

subsystem, and the process continues, resulting in a

hierarchical representation of the system. Rigi also supports

a scripting interface called Rigi Command Language (RCL)

so tasks can be automated (e.g. grouping files together based

on filenames), and new algorithms can be added

(e.g. clustering, pattern identification) [21,22]. Two other

architecture recovery toolsets use the Rigi graphical inter-

face (rigiedit): Dali, and Shimba. However, these toolsets

do not use rigiparse. Instead, they use their own tools to

extract dependency information from source code.

The Dali workbench is a collection of tools intended for

extracting and viewing architectural information. Dali is

based around a repository that keeps track of architectural

information as a set of relations in an SQL database. All

tools interact only with the SQL database. Dali is designed

to support multiple types of relationships between entities

(e.g. static dependencies, dynamic calls, build dependen-

cies, directory structures) by supporting multiple tools that

gather relationship data. Dali has been used to analyze

systems in C, CCC, and Fortran. However, since tools

interact only with the database, it should be a straightfor-

ward task to accommodate tools into Dali that can parse

other languages. The Rigi graphical interface rigiedit is used

to generate architectural views, which are diagrams

displayed as labeled boxes connected by lines, with colors

indicating the nature of the dependency. Dali adds to Rigi

the idea of fusion: generating architectural views by

combining different types of relationship information.

Page 5: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656 647

Extracted views are generated based on the output of a

single tool (e.g. function call dependencies). Refined views

are generated from extracted views by defining higher-level

modules from lower level ones (e.g. variables, functions,

classes, and files), and displaying only the interaction

among higher-level modules. However, these refined views

can provide only one perspective of the system, since they

are generated from the output of only one tool. Fused views

are generated by combining different refined views.

For example, a user may generate a static view of a C

program based on function call dependencies extracted with

a static analysis tool, and a dynamic view based on function

calls made during a particular execution using information

gathered by a profiler. The user can then generate a fused

view, where both the static dependencies and the actual calls

are shown in the same diagram [23].

Shimba is another environment based on the Rigi tool.

Shimba differs from Dali in that Shimba also computes a

host of metrics on the data. Shimba computes metrics from

the object-oriented Chidamber and Kemerer metrics suite

[24], which include measures of inheritance, communi-

cation and [25] complexity. Shimba contains a tool called

Jextractor [26] that can analyze Java byte code and extract

dependency information in Rigi Standard Form. Shimba is

capable of displaying static dependency graphs visually,

which can be annotated with the desired metrics [27].

PBS [28] is an implementation of the Software Bookshelf

[29], a web-based system for extracting and storing

information about a legacy system to assist in reengineering

(the original prototype implementation of the Software

Bookshelf was based on Rigi). PBS is a collection of

interoperating tools that can analyze C source code, help

organize elements into higher-level components, and

provide visualization of the system’s architecture [30]. Cfx

parses C code and generates fact tables. Fbgen is a fact base

generator that takes the output of Cfx and converts the fact

to tuples in Rigi Standard Form. Grok is a scripting tool that

allows the user to manipulate relations among software

entities that were generated by fbgen. The user can execute

grok scripts that come with PBS to compute dependencies

and to abstract low-level entities such as global variables

and functions into file-level entities, or can define their own

scripts. Lsedit is a Java-based graphical viewer that can

generate views of the architecture, referred to as ‘software

landscapes.’ These views are hierarchical, allowing nodes to

be collapsed or expanded. Cedit is a Java application that

assists the user in classifying entities that have not yet been

assigned to a subsystem. PBS cannot automatically do

clustering, but given a decomposition it can help classify the

unassigned orphans [31]. An initial decomposition can be

achieved by building a conceptual architecture from

available documentation, and then using this as a model to

analyze the actual architecture (see below for case studies

using PBS to perform architecture extraction).

Bauhaus [83] is a static analysis toolkit that supports a

wide range of features, including dependency analysis,

source code metrics, dead code detection, clone detection,

defect detection, program slicing, and other forms of

pointer-based and flow-based analysis. Bauhaus is interest-

ing from an architecture recovery perspective because it

possess a number of different techniques for identifying

architectural components by means of clustering. A user can

identify components by applying these techniques in the

context of an iterative, semi-automated process [84].

Bauhaus’s techniques for identifying components fall into

three categories: connection-based, metric-based and graph-

based. Connection-based techniques associate group entities

into clusters by analyzing the direct relationships between

the entities. Metric-based techniques iteratively apply

metrics to identify which entities should be grouped into

clusters, and are more general than connection-based

techniques. Graph-based techniques identify clusters by

performing analysis on the graph that represents the

relationship among entities. Unlike connection-based tech-

niques, graph-based techniques use the entire graph in their

analyses. Within each iteration of the component identifi-

cation process, the user can apply combinations of these

techniques to form clusters.

Automatic clustering. Arch is a tool that automatically

groups procedures into clusters, based on properties such as

name similarity, data sharing, and procedure call graphs. It

also identifies mavericks, procedures that appear to be

located in the incorrect module because they share more

information with procedures in other modules than with

procedures in their own module. The system is based on

heuristics, which are tunable by the user. Arch works on

programs written in the C programming language by

employing the cxref [32] cross-reference extractor to

retrieve non-local names used by functions [33].

Bunch is a tool capable of organizing C, CCC and Java

functions into modules. Bunch takes a module dependency

graph (generated by another tool) and partitions the graph in

an attempt to optimize a modularization quality function,

which rewards modules with high cohesiveness and

penalizes a high degree of coupling among modules.

Bunch allows the user to choose which algorithm to use to

perform the optimization, as well as allowing the user to

manually indicate clusters that are known in advance [34].

Another approach to clustering is that of Cimitile and

Visaggio. They use the relation of dominance among nodes

in the call-directed graph of a system to recognize clusters.

The call graph is transformed into a dominance graph to

identify dominant procedures and relative sets of dominated

procedures [35]. They implemented their approach in

a prototype tool developed in Prolog to analyze Pascal

code [36].

Maqbool and Babri [37] present a new weighted

combined algorithm for measuring distance between

different clusters in order to determine whether or not they

are similar enough and should be aggregated. When a new

cluster is formed, then the algorithm again is performed on

the new set of clusters until a stop criterion is reached.

Page 6: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656648

The performance of the new algorithm was examined by

applying the algorithm onto two test systems and by using

various similarity measures. The test systems were Xfig,

75,000 LOC of C-code, and Bash, 38,000 LOC of Unix shell

script. The results were that the new algorithm showed

better results, especially when used in combination with the

unbiased Ellenberg measure (also defined by the authors).

Summary on filtering and clustering tools. Filtering and

clustering tools are now relatively mature as a research

technology. See Armstrong and Trudeau for an in-depth

comparison of Rigi, Dali, PBS, as well as two other tools not

surveyed here (CIA, and SniFFC, which do not support

clustering) [30]. Koschke and Eisenbarth provide a frame-

work for evaluation of various clustering techniques [38],

which can be used to design experiments related to

clustering techniques. Girard, Koscheke, and Schied

compare five different techniques that are related to

clustering based on the tools’ identification of atomic

components in three systems written in C. The result was

that ‘same module heuristic,’ i.e. clustering based on

occurrences in files provided the most similar clusters

as compared to experts’ clustering [39]. Anquetil and

Lethbridge [40] use a similar approach by basing clustering

on file names and their components.

4.1.2.2. Architecture recognition tools. Architecture recog-

nition tools recover information about architectural com-

ponents and connectors through a sophisticated analysis of

the source code beyond extracting file and function-call

dependencies. The advantage of these tools is their detection

of components and connectors that filtering and clustering

tools and consistency checking tools cannot recognize. In

particular they can identify inter-process communication

mechanisms such as remote procedure calls, pipes, and

shared files. Architecture recognition tools try to recognize

these mechanisms in the source code by matching them

against a stored collection of patterns, known as architec-

tural cliches [41]. These collections of cliches are organized

by architectural style (e.g. a pipe connector pattern would

only be searched for if the architecture style was pipes and

filters). Therefore, the user must have some knowledge of

the architectural style of the system before using an

architecture recognition tool.

One example of an architecture recognition tool is

Architecture Recovery Tool (ART), which is part of the

Code and Architecture Analysis Tool (CANTO) environ-

ment. ART uses a catalog of patterns to try to recognize

connectors in an Abstract Syntax Tree (AST) representation

of the source code. The AST is generated by a front-end tool

that analyzes C code. ART can perform analysis at a system

level, or at a program level. When analyzed at the system

level, the software is viewed as a collection of processes

interacting through inter-process communication mechan-

isms. ART searches for connectors such as shared files,

shared memory, remote-procedure calls, pipes, and invoca-

tions (executions of new processes). When analyzed at

the program level, ART analyzes for function call

dependencies (similar to filtering and clustering tools such

as Rigi), and also looks for connectors which represent the

spawning of new tasks such as the creation of a new process

or thread [42]. MITRE Software Architecture Recovery

Tool (ManSART) is a similar architecture recognition tool

that preceded ART [43]. Based on the relatively few number

of papers found, architectural recognition tools do not seem

to be a very active area of research.

4.1.2.3. Design pattern identification tools. With the rise in

popularity of design patterns [17] several researchers have

developed approaches for locating undocumented design

patterns in object-oriented code. Design pattern identifi-

cation tools are useful when no documentation of a planned

architecture exists, since these patterns encapsulate a set of

design rules that can be used to detect degeneration in later

versions. Design pattern identification tools extract coupling

information among classes (i.e. function call dependencies)

and perform searches to determine if a collection of classes

is participating in a design pattern. Design patterns are

similar to architectural cliches in that both are commonly

occurring architectural structures. However, architectural

cliches are more concerned with individual connectors and

components, whereas design patterns are concerned with the

organization of collections of components and connectors.

Pat is a prototype system that identifies design patterns in

CCC code. In Pat, design patterns are specified in OMT

notation, which are then translated into Prolog facts.

A commercial CASE tool (Paradigm Plus) is used to

translate CCC header files into OMT notation, which is

then translated into Prolog. A Prolog query is used to detect

instances of design patterns. Pat can currently identify the

following patterns: adapter, bridge, composite, decorator,

and proxy. Because it only uses CCC header information,

Pat is not capable of identifying behavioral patterns.

In experiments with four different software systems, Pat

successfully identified all design patterns, though it also

detected spurious design patterns (false positives). Kramer

and Prechelt observed a precision rate (the percentage of

detected patterns that were true patterns) of 14–50% [44].

Antoniol et al. developed a system that uses an

intermediate representation language called Abstract Object

Language (AOL) to find design patterns in CCC source

code. They specify design patterns in OMT notation and

translate both the OMT representation and the source code

into AOL. A parser generates an AST representation of the

code and the design patterns. The AST is then annotated

with metrics computed from the source code. A pattern

recognizer does a search for design patterns, using the

metric information to reduce the search space. The search is

a conservative approach to minimize the number of false

negatives at the expense of detecting false positives. This

system can identify the patterns adapter, bridge, composite,

decorator, and proxy (i.e. the same set of patterns as Pat).

Antoniol et al. evaluated the tool’s performance on

Page 7: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656 649

6 different public domain systems and eight different

industrial systems. They had precision of roughly 55%,

with all true patterns detected [45].

Niere et al. incorporated pattern identification function-

ality into the FUJABA CASE tool [46]. Their tool can

identify structural and behavioral design patterns in Java

programs. Design patterns are specified in a format similar

to UML collaboration diagrams. The approach combines

bottom-up and top-down analysis of an Abstract Syntax

Graph (ASG) representation of the source code. The

bottom-up analysis searches for subpatterns in the ASG.

Subpatterns are relationships among classes that occur in

several different design patterns (e.g. reference, association,

generalization). If a subpattern is found, then the ASG is

annotated with information identifying the subpattern and

the classes involved. In top-down analysis, a design pattern

is hypothesized and searched for based on annotations

generated by top-down analysis. The analysis is interrup-

tible, so that a user can view intermediate results and reject

false positives. They evaluated the tool’s performance on

the Java Abstract Window Toolkit (AWT) and the Java

Generic Library (JGL). When using only structural

information, the tool identified 14 design patterns in a

subset of the AWT, four of which were genuine patterns and

ten of which were false positives. When they also analyzed

method bodies, the tool correctly identified the four true

design patterns and had zero false positives, but analysis

took over twice as long (100 vs. 41 s) They claimed similar

results for analysis of JGL [47].

Keller et al. [41] developed an environment called

SPOOL that combines automatic pattern identification with

visualization tools to assist in the detection of false positives

and to gain insight into the rationale behind the pattern-

based design. SPOOL analyzes CCC code and stores the

relationships among source code elements (e.g. files,

classes, functions, variables) as a text representation of a

UML model. SPOOL’s design repository contains a

collection of design patterns that can be identified.

SPOOL has query mechanisms that can automatically

recognize patterns in the extracted UML model, and display

the design patterns by identifying which classes participate

in the pattern. In addition, they also provide an interface that

allows users to manually identify design patterns. Keller

et al. also suggest a semi-automatic approach to pattern

recovery similar to FUJABA, but this approach has not yet

been implemented in SPOOL. Keller et al. argue that a fully

automatic approach to design pattern identification is

impractical because of the high incidences of false positives

that invariably occur when attempting to search for design

patterns. They emphasize the importance of visualization

for assisting a user in identifying the true design patterns and

for understanding the rationale behind the design. SPOOL

was evaluated in case studies on two large telecom systems

(470,000 SLOC and 290,000 SLOC), as well as on the

ETCC toolkit [48]. In these case studies, they tried to

identify three patterns: template method, factory method,

and bridge. The bridge pattern was chosen because they

believed it to be a difficult pattern to identify without human

assistance. SPOOL identified 3243 template methods in the

first telecom system, 1857 in the second telecom systems,

and 1000 in ETCC. It found 247 factory methods in the

first telecom system, 168 in the second system, and 44 in

ETCC. It found 108 bridge patterns in the first system, 95

in the second, and 46 in the third. They manually verified

that four out of the 108 bridge patterns detected in the first

systems were real bridge implementations, and two other

ones provided much of the bridge functionality. The authors

state that the template method and factory method structures

unambiguously represent the design patterns, so that manual

verification of these detected patterns is unnecessary. [49].

Gustaffson et al. used a pair of tools known as Maisa and

Columbus to identify design patterns. Maisa (Metrics for

Analysis and Improvement of Software Architectures) can

compute metrics and identify design patterns given a set of

UML diagrams. Columbus is a reverse engineering tool that

can extract a UML class diagram from source code written

in C and CCC. The UML class diagram is converted into a

set of Prolog facts, which are fed to Maisa. Maisa translates

the problem of locating design patterns into a constraint

satisfaction problem to do an efficient search. They did not

provide any experimental results [50].

Balanyi and Ferenec [85] have developed a system for

pattern identification that also uses Columbus as a front-end.

To represent the patterns to be identified, they developed an

XML-based language called Design Pattern Markup

Language (DPML). Columbus parses CCC code into an

abstract semantic graph, and a pattern described in DPML is

parsed into a standard DOM tree. The pattern identification

algorithm attempts to match the DOM tree to the abstract

semantic graph by trying to associate classes in the design

pattern to classes in the source. Unlike other pattern

identification systems that can only identify structural and

behavioral patterns, this system can identify creational

patterns as well. Through function body analysis, it is able to

capture more information about relationship between

classes than other pattern identification systems. They can

capture composition, aggregation, association, inheritance

relationships, call delegation, object creation, and object

redefinition (overriding). This system was evaluated on four

open source programs: Jikes, a 75,000 SLOC Java compiler,

Leda, a 86,000 SLOC algorithm and data type library,

StarOffice Calc, a 1,270,000 SLOC spreadsheet program,

and StarOffice Writer, a 1,513,000 SLOC word processing

program. In Jikes, the system correctly identified

2 prototypes, 8 proxies, 16 strategies, and 5 templates.

In Leda, it correctly identified 3 strategies. In Calc, it

successfully identified 73 bridges, 30 strategies, and

94 template methods. In Writer, it successfully identified

2 builders, 9 factories, 2 prototypes, 80 bridges, 5 proxies,

37 strategies and 101 template methods. The system also

identified several false positives. In Jikes, it falsely

identified 72 proxies. In Calc, it falsely identified

Page 8: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656650

19 builders, 13 adapter classes, and 1 iterator. In Writer, it

falsely identified 14 builders, 32 adapter classes, 4 proxies,

and 5 visitors. The system also found many instances of

adapter objects, but these were not verified to determine

accuracy.

Tonella and Antoniol use a very different approach in

identifying patterns. They do not search for matches against

a pre-existing collection of design patterns. Rather, they use

a technique called concept analysis to identify organiz-

ational patterns in the source code that are repeated several

times. They classify these repeated patterns as design

patterns. They applied their technique to a 21,000 SLOC

CCC program and found several recurrent patterns, one of

which turned out to be the well-known adapter pattern [51].

Summary of design pattern identification. Design pattern

identification is a relatively young but active area of

research. Considering the encouraging results so far, we

hope to see pattern identification functionality incorporated

into architecture recovery tools and evaluated in industrial

case studies.

4.1.3. Checking compliance

Compliance checking tools automate the task of check-

ing for degeneration by verifying that the actual architecture

of the implemented software system conforms to the

planned architectural design and by identifying any

violations that occur. This is an improvement over filtering

and clustering tools, where a manual inspection is required

to locate architectural violations.

Also under the category of compliance checking are tools

that check an implementation against a set of design rules

(e.g. conformance to a design pattern) rather than against a

specific architectural design.

4.1.3.1. Verification against a planned architecture. Fiutem

and Antoniol developed a tool that can check if a system

conforms to a design specified in the OMT [52] modeling

language. The tool translates both the (CCC) source code

and the design into an intermediate ‘Abstract Oriented

Language’ (AOL), and compares the two AOL represen-

tations to identify discrepancies: missing and extra coupling

links [53].

Murphy’s Reflexion models are useful for confirming that

a system matches the user’s tacit knowledge of the

architectural design. The user defines a conceptual model

of the architecture, which can be defined at higher or lower

levels of abstractions. The user also defines a mapping

between the conceptual model and the source code (e.g. a

system in the conceptual model may map to a set of functions

in the source code, or a collection of files in a particular

directory). A tool is used to compare the conceptual model to

the source code (written in C), identifying missing and extra

couplings [54]. Koschke and Simon [86] have extended

Murphy’s Reflexion models to support hierarchical archi-

tectural models. Their extensions allow the user to describe

the conceptual architectural model at different levels of

detail.

4.1.3.2. Verification of implementations of design patterns.

Hedgehog, developed by Bundy et al., checks a collection of

Java classes involved in a design pattern to ensure that the

design pattern has been correctly implemented in the code.

This is useful because design patterns prescribe how

different software items relate to each other, and it can be

hard for newcomers to follow these prescriptions. Prescrip-

tions violations are not always obvious and tool support

might be needed in order to detect such violations. The

design patterns are described in a special-purpose language

called SPINE. Hedgehog currently checks for design

patterns whose properties are specified by the structural

relationship among classes (i.e. static coupling among

classes through inheritance and method access). Hedgehog

cannot check behavioral design patterns. [55].

Another tool that detects violations in design patterns is

Pattern-Lint. Pattern-Lint analyzes CCC source code to

determine if a collection of classes properly implements a

design pattern. Violations of design patterns are illustrated

graphically by displaying correct and incorrect coupling

among classes. Patterns are internally represented in

Pattern-Lint using Prolog [56].

4.1.4. Summary

Architecture recovery tools are essential for diagnosing

architectural degeneration. They allow the actual architec-

ture to be recovered so that it can be evaluated for

conformance to the planned architecture. They also assist

in generating architecture documentation, which can serve

as the planned architecture for detecting future degener-

ation. No existing tool can fully perform architecture

recovery without human intervention, and this may never

be achievable. Therefore, processes are required to

accompany these tools. In our survey, we identified a

number of design pattern identification tools, but we did not

find any tools to help detect architectural styles.

4.2. Treating degeneration

So far, we have surveyed tools and techniques that can

help diagnose degeneration. Once we have identified

degeneration in a system, we would like to be able to treat

the problem. We would also like to ensure that this

treatment does not have any undesirable side effects.

Since degeneration is a result of deviation from the planned

architecture, treatment will consist of modifying the code so

that it is brought into conformance with the planned

architecture. While modifying the code, we must take care

to ensure that these modifications do not alter the behavior

of the system (no side effects). The act of restructuring code

without affecting system behavior is known as refactoring

[57]. By using automated test cases, one can assure that the

behavior is not altered due to a refactoring act [58].

Page 9: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656 651

4.2.1. Architecture improvement process via refactoring

Krikhaar et al. proposed the use of refactoring to combat

degeneration. They describe the phenomenon of software

“.deteriorating from an architectural point of view over

the years, as a result of adaptations made in the software

because of changing system requirements” [59], and they

propose a two-phase process for combating this phenom-

enon. In the architecture impact analysis phase, the

architecture of the system is extracted from source code

and modeled in relation partition algebra. This model is

manipulated to evaluate the impact of different architectural

changes. In the software architecture transformation phase,

the proposed changes are implemented in the source code.

These changes are achieved through the execution of

‘recipes’, sequences of simple source code manipulations.

Recipes are very similar to refactorings as described by

Fowler. Indeed, Fowler’s book on refactoring [25] is

essentially a list of such recipes. Krikhaar et al. mention

the importance of tool support for performing recipes,

claiming that the process of manually altering the code is

tedious and error-prone.

4.2.2. Refactoring support in development environments

We agree with Krikhaar et al. that good tool support is

important for performing refactoring. These refactoring

tools can support the activity of refactoring by automating

some of the work involved in modifying the code.

Refactoring tools are useful because rewriting code can be

very time-consuming, and refactoring introduces the

possibility of inserting errors. The more automated and

systematic the refactoring process, the better.

Currently, there are several commercially available Java

development environments with support for automated

refactoring [60–63]. One of the first tools to support

automated refactorings was the Smalltalk Refactoring

Browser [64]. The Refactoring Browser integrates refactor-

ing support into a Smalltalk development environment and

can automatically apply 18 different refactorings to source

code (see Table 1).

Table 1

Refactorings supported by the Smalltalk Refactoring Browser

Method refactorings Variable refactorings Class refactorings

Add method Add variable Create new class

Rename method Rename variable Rename class

Remove method Remove variable Remove class

Push down method

into subclasses

Push down variable

into subclass

Pull up method from

subclasses

Pull up variable from

subclass

Add parameter to

method

Create accessors for

variable

Move method across

object boundary

Change variable refs

to accessor calls

Extract code as

method

4.2.3. Refactoring to introduce design patterns

One application of the Refactoring Browser is to

introduce design patterns into legacy code. Roberts et al.

provide an example of introducing the visitor pattern into a

legacy application through the application of seven

refactorings (create new class, add method, extract code

as method, pull up method, add parameter to method, move

method across boundary, rename method) [64].

Tools such as the Smalltalk Refactoring Browser provide

a semi-automated method of introducing design patterns

into legacy systems through successive application of

simple refactorings. Several researchers have investigated

refactoring tools that can fully automate this process.

Cinneide and Nixon developed a prototype tool called

Design Pattern Tool (DPT) for adding design patterns to

existing Java source code through transformations that

preserve behavior. The tool performs the transformations by

decomposing design patterns into a sequence of mini-

patterns. Mini-patterns are imposed on the code through

mini-transformations. A mini-transformation consists of

pre-conditions, transformation steps, post-conditions, and

an argument that demonstrates behavior preservation. They

have succeeded in applying the following patterns to source

code: abstract factory, factory method, singleton, builder

and decorator [65]. Eden et al. developed a tool called a

pattern wizard that introduces design patterns into systems

written in the Eiffel programming language. The tool parses

the Eiffel code and produces an abstract syntax represen-

tation. The abstract representation is transformed to reflect

the pattern and is then translated back into Eiffel. Design

patterns are represented in the Smalltalk programming

language. Similarly to Cinneide and Nixon, Eden et al. have

recognized a recurring set of design motifs at a smaller scale

than design patterns, which they refer to as micro-patterns.

These micro-patterns are used in implementing their

transformation algorithms. They support the following

patterns: abstract factory, adapter, proxy, decorator, com-

posite, visitor, observer, and state [66].

Florijn, Meiers and van Winsen developed a prototype

Smalltalk design environment that has explicit support for

introducing new design patterns to an existing system by

code transformations. The environment is based upon the

idea of representing design elements (e.g. classes, methods,

patterns, associations) in a model, which is maintained

separately from the source code. Florijn et al. refer to these

design elements as ‘fragments.’ Tools in the design

environment perform manipulations on the fragment

model. The fragments and the relationships among them

are stored in a fragment database. The fragment database

also contains a catalog of available design patterns, which

are represented as fragment prototypes. A fragment browser

tool allows the developer to view the model and introduce

new patterns into the catalog. A visual tool called the OMT-

Tool, based on an extended version of the Smalltalk

Refactory Browser, supports introducing design patterns

into the source code. Once the developer has identified a set

Page 10: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656652

of classes as a target for a design pattern, the developer uses

the OMT-Tool to associate the classes with a design pattern.

The developer is responsible for modifying the source code

to conform to the design pattern, though the OMT-Tool does

support the simple refactorings of the Smalltalk Refactoring

Browser to simplify this process. Once the developer has

completed the refactoring, the OMT-Tool can be used to

verify that the source code conforms to the patterns. Pattern

conformance is checked by verifying that a set of constraints

is met. This functionality can also be used to find violations

in classes that have been previously identified as participat-

ing in design patterns. This environment was used to

reorganize a web browser that consisted of approximately

150 classes. [67].

4.2.4. Summary of refactoring tools

The refactoring tools described in this section can be

viewed as a treatment for the problem of degeneration. They

treat degeneration by simplifying the task of modifying the

architecture of a system so that it can be brought into

conformance with the intended architecture. Design pat-

terns, which represent solutions to common problems, are a

natural target for such refactorings. Judging from the papers

surveyed, pattern-level refactoring is a relatively small and

young area of research. However, there are an increasing

number of commercially and freely available tools that can

automate small refactorings. The proliferation of such tools

may indicate an increasing industry demand for refactoring

support. If so, we may expect increasing levels of research

in this area.

4.3. Researching degeneration: understanding evolution

The tools we have covered so far are useful for

practitioners dealing with degeneration. However, it is

also important for researchers to have tool support to study

architectural degeneration. The more we know about how

degeneration progresses, the better we can deal with it. To

study the progression of degeneration, researchers and

practitioners need evolution understanding tools that

provide information about how systems evolve over time.

Practitioners can, for example, use this information to

identify sections of the architecture that are more change-

and error-prone than others, which can then be reengineered

into more stable and less error-prone entities. Researchers

can, for example, use evolution information from families of

systems in order to identify patterns of change that can help

build a better general understanding of the relationship

between change and software degeneration.

4.3.1. Visualizing architectural changes of a system across

versions

Holt and Pak developed a tool called GASE (Graphical

Analyzer for Software Evolution) designed for visualizing

architectural changes of a system across different versions.

GASE is implemented as a four-stage system. The first stage

is an architecture recovery stage, where high-level infor-

mation about the system (modules, subsystems, and

dependencies) is extracted from the code. The second

stage identifies differences between the two different

versions under examination. The third stage translates

these differences into a representation of colored boxes

and arrows. The fourth stage provides an interactive

graphical user interface so that a user can view the

architecture as a box-and-arrow diagram and view differ-

ences between versions based on coloring [68]. The initial

implementation of GASE is targeted at the C programming

language and so GASE is not aware of object-oriented

entities such as classes and inheritance.

4.3.2. Combining metrics and visualization to understand

architectural changes

Tu and Godfrey developed a prototype environment

called BEAGLE that combines metrics and visualization to

provide understanding of architectural changes to a system.

Complexity metrics are used to measure the degree to which

functions have changed from one release to the next. Facts

are stored in a relational database that can be accessed

through an SQL-based query interface. The system structure

is displayed hierarchically (subsystems, files, functions).

Emphasis is on the architectural changes in the code from

one version to the next, and so the metrics and visualization

tools are oriented towards displaying changes, such as

identifying new, deleted, and changed entities (subsystems,

modules, functions). BEAGLE performs origin analysis so

that it can recognize a function from one version to another

by characteristics of the code, rather than by simply using

the name of the function or the file that it is stored in [69].

Like GASE, BEAGLE’s initial implementation targets

software developed in C, and so BEAGLE does not

explicitly support object-oriented concepts.

Antoniol et al. built a collection of tools based on AOL

(discussed in the section about consistency checkers) that

can examine and display the differences between successive

versions of a software system written in CCC. They

outline a five-step process for comparing two versions of a

system. In the first step, the two versions are translated from

CCC to AOL. In the second step, software metrics are

extracted from each version. In the third step, similarity

measures are computed between every class in the first

version and every class in the second version. In the fourth

step, these similarity measures are used to match up classes

between the two versions. In the fifth step, the results are

presented to the user. The final output of the system is a web

page that contains a Java applet that provides a box-and-

arrow view of the system, using the same coloring scheme

as Holt and Pak to highlight differences between versions.

The system can be viewed at three different levels

of abstraction: release level, class level, and member

level [70].

Page 11: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656 653

4.3.3. Summary of evolution understanding tools

Evolution understanding tools, as their name suggests,

support understanding how system architectures evolve

over time. Judging from the small number of papers that

were found, there does not appear to be much research

focusing on architectural evolution. However, a fair amount

of research focuses on measuring coupling based on

changes to multiple modules [2,87–89].

5. Prevention

In this paper, we have focused on technologies for

detecting and treating degeneration. Folk wisdom tells us

that ‘An ounce of prevention is worth a pound of cure,’ and

we might assume that if we apply the right tools and

techniques early enough, we can avoid degeneration

and reduce maintenance effort. However, before we propose

any solutions, we must first understand the nature of the

problem. In this section, we will discuss proposed causes of

degeneration, tools that may help prevent degeneration, and

arguments against trying to prevent degeneration.

5.1. Causes

Several researchers have discussed possible causes of

degeneration. Parnas lists two types of software aging:

software that is not updated over time (aging due to lack of

modifications over time), and software that is not properly

designed to accommodate change (aging due to modifi-

cation over time). In this paper, we are primarily concerned

with the latter. Parnas suggests a number of causes for this

problem [9], including:

1.

Lack of training in change prediction. Textbooks do not

discuss how to estimate the probability of change for

various different classes of changes.

2.

Rushed development. Developers do not take the time

required to design properly for change.

3.

Intuitive development is not modular. Developers do not

naturally write modular code.

4.

Lack of good example designs. There are not many good

designs available for designers to use as examples.

5.

Lack of design documentation. Design decisions are not

often documented in a form that is useful for future

maintainers.

6.

Poor quality of design documentation. If documentation

exists, it is poorly organized, incomplete, and imprecise.

7.

Lack of training in design reviews. Many developers do

not know how to prepare and hold a design review.

8.

Lack of training in design documentation. Developers do

not know how to produce a readable and precise

documentation of a design.

9.

Time pressure. Developers are under time pressure and

mistakenly believe that there is no time for proper

reviews.

Eick et al. offer the following examples of causes of code

decay, based on their study of a large telecommunications

system: [2]

10.

Inappropriate architecture. architecture that is not

designed to accommodate the necessary changes.

11.

Violations of the original design principles. Changes

that violate architectural rules, due to the introduction

of features not anticipated at design time.

12.

Imprecise requirements. Requirements are not comple-

tely defined and documented.

13.

Time pressure. Schedule issues cause developers to

write poor code, possibly in ignorance of the

architecture.

14.

Inadequate programming tools. Development tools that

are inadequate for the task at hand.

15.

Organizational environment. Issues in the organiz-

ational environment that may lead to low morale or

inadequate communication among developers.

16.

Programmer variability. Programmers who have

difficulty understanding or maintaining code developed

by more skilled co-workers.

17.

Inadequate change process. Issues such as the lack of a

version control system.

Van Gurp and Bosch have observed the following causes

for design erosion, based on their observations of industrial

case studies: [10]

18.

Traceability of design decisions. Design decisions are

difficult to infer from source code.

19.

Increasing maintenance cost. Developers make sub-

optimal modifications due to the increasing complexity

of the system. Either it is so complex that they do not

understand it fully, or it would require too much effort

to make a better change.

20.

Accumulation of design decisions. Design decisions

tend to accumulate over time and interact with each

other. This makes it harder to change the system, since

multiple design decisions must be reconsidered. As a

result, the system cannot adapt well to new require-

ments, and, repair requires significant effort.

21.

Iterative methods. Several development methods (e.g.

rapid prototyping, Extreme Programming [71]) forgo

the aspect of design that tries to predict future changes

and accommodate them in the architectural design.

This is quite a long list of contributors to degeneration.

Note that while there is some overlap among the three

different lists (e.g. Parnas and Eick et al. both mention time

pressure), there are also significant differences. Parnas’s list

is dominated by problems in design, particularly with design

documentation [9]. Eick et al.’s list is more general, dealing

with other issues such as requirements, organizational

factors, and causes that appear to be unavoidable (e.g.

violations of original design principles, programmer

Page 12: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656654

variability). Van Gurp and Bosch’s list places more

emphasis on the inevitability of degeneration, stating that

‘even the optimal strategy does not to lead to an optimal

design. It just delays inevitable problems like design erosion

and architectural drift’ [10].

5.2. Possible technologies for prevention

A full survey of technologies that address the proposed

causes of degeneration is beyond the scope of this paper.

Here, we present a selection of technologies that address

some of the causes listed above.

Software architecture analysis methods such as SAAM,

SAAMCS, ESAAMI, SAAMER and ATAM [72] address

causes 1 and 10 (poor change prediction, inappropriate

architecture). Documented design patterns and architecture

styles [16,17,73–78] address cause 4 (lack of example

designs). Design-level modeling languages such as UML

[14], and architecture description languages (e.g. Acme,

C2, Darwin, and Rapide) [79,80] address causes 5, 6, 8

(lack of quality design documentation). Inspection methods

such as object-oriented reading techniques (OORTs) [81]

address ‘lack of effective design reviews’ (cause 7).

Requirements frameworks such as REF [82], i* [83], and

Tropos [84] address ‘imprecise requirements cause 12

(cause 12). Embedding design decisions in source code,

either through consistent comments [85,86] or program-

ming language support for design-level constructs such as

design patterns [87–89] address ‘traceability of design

decisions’ (cause 18).

5.3. Is prevention even possible?

Some of the proposed causes of degeneration appear to be

unavoidable. In particular, ‘Violations of the original design

principles’ (cause 11) and ‘Iterative methods’ (cause 21)

seem to be fundamental to current development processes. It

is impossible to foresee all future changes, so violations of

the original design principles will inevitably occur. To deal

with changing requirements, iterative development tech-

niques were developed. Such techniques, which reduce the

amount of up-front design in favor of an incremental

approach, may contribute to degeneration by failing to

predict future changes. Some iterative development meth-

odologies (e.g. Extreme Programming) advocate an

approach that ignores possible future requirements and

focuses only on the requirements in the current iteration.

Advocates of these iterative methodologies claim that

predicting future changes is effectively impossible and that

attempting to do so is wasted effort. This philosophy is

encapsulated in the acronym YAGNI: ‘You aren’t gonna

need it’ [71]. They see ‘iterative development’ (cause 21) as

the only way to deal with cause ‘violations of the original

design principles’ (cause 11). This approach advocates

constant treatment for dealing with degeneration by

refactoring the code whenever the developer suspects that

degeneration may be occurring. Thus, degeneration is not

diagnosed by comparing the actual and the planned

architecture of the system, (indeed, there may be no planned

architecture to speak of!), but is detected by identifying ‘bad

smells’ in the source code [25].

6. Conclusion

In this paper we have surveyed technologies that can be

used in combating architectural degeneration. We have

discussed technologies for diagnosing degeneration, treat-

ing it, and researching it. Diagnosing degeneration cannot

be fully automated, but there exists good tool support for

assistance. Treatment technologies are less mature, but are

gaining in popularity. Only a handful of technologies exist

for studying degeneration. We have also discussed issues

involved in preventing degeneration, by identifying pro-

posed causes and technologies that address some of these

causes, as well as exploring some arguments against the

feasibility of prevention.

In the future, we would like to investigate how systems

tend to degenerate over time, by studying how and why

architectural violations enter the system. Once this infor-

mation is known, we could attempt to identify how

significant a problem each of the proposed causes represents

and determine which preventative technologies would be

most effective.

Acknowledgements

The authors acknowledge support from the NASA High

Dependability Computing Program under cooperative

agreement NCC-2-1298.

The authors would like to thank Jen Dix for proof-

reading this paper.

References

[1] M.W. Godfrey, E.H.S. Lee, Secrets from the Monster: extracting

Mozilla’s software architecture, Proc. Second Symp. Construct.

Softw. Eng. Tools (CoSET00) 2000.

[2] S.G. Eick, L. Graves, A.F. Karr, J.S. Marron, Does code decay?

Assessing the evidence from change management data, IEEE Trans.

Softw. Eng. 27 (1) (2001) 1–12.

[3] M. Lindvall, R. Tesoriero, P. Costa, Avoiding architectural degener-

ation: an evaluation process for software architecture, Eighth IEEE

Symp. Softw. Metrics 2002; 77–86.

[4] R. Tesoriero Tvedt, P. Costa, M. Lindvall, Does the code match the

design? A process for architecture evaluation, Proc. Int. Conf. Softw.

Maintenance 2002; 393–401.

[5] M.M. Lehman, L.A. Belady, Program Evolution: Processes of

Software Change, Harcourt Brace Jovanovich, London, 1985.

[6] M.M. Lehman, Laws of software evolution revisited, Eur. Workshop

Softw. Process Technol. 1996.

Page 13: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656 655

[7] F.P. Brooks, The Mythical Man-Month, Addison Wesley, Reading,

MA, 1995.

[8] D.E. Perry, A.L. Wolf, Foundations for the study of software

architecture, ACM Softw. Eng. Notes 17 (4) (1992) 40–52.

[9] D.L. Parnas, Software aging, Sixteenth Int. Conf. Softw. Eng. 1994;

279–287.

[10] J. van Gurp, J. Bosch, Design erosion: problems and causes, J. Syst.

Softw. 61 (2) (2002).

[11] R. Tesoriero Tvedt, P. Costa, M. Lindvall, Evaluating Software

Architectures,, 61 ed. Advances in Computers, Elsevier Science,

USA, 2004.

[12] L. Bass, P. Clements, R. Kazman, Software Architecture in Practice,

Addison-Wesley, 1997.

[13] R. Crispen, L. Stuckey, Structural model: architecture for software

designers, Proc. TRI-Ada ’94 1994.

[14] G. Booch, I. Jacobson, J. Rumbaugh, The Unified Modeling Language

User Guide, Addison-Wesley, Reading, MA, 1998.

[15] S. Bhansali, Software design by reusing architectures, Proc. Seventh

Knowl.-Based Softw. Eng. Conf. 1992.

[16] D. Garlan, M. Shaw, An Introduction to Software Architecture, World

Scientific Publishing Company, New Jersey, 1993.

[17] E. Gamma, R. Johnson, R. Helm, J. Vlissides, Design Patterns—

Elements of Reusable Object-oriented Systems, Addison-Wesley,

Reading, MA, 1994.

[18] R. Tesoriero Tvedt, P. Costa, M. Lindvall, Evaluating Software

Architectures, Advances in Computers, Elsevier Science, USA, 2003.

[19] N.C. Mendonca, J. Kramer, Requirements for an effective architecture

recovery framework, SIGSOFT 96 Workshops: Second Int. Softw.

Architect. Workshop Int. Workshop Multiple Perspect. Softw. Dev.

1996; 101–105.

[20] C.H. Lung, Software architecture recovery and restructuring through

clustering techniques, Third International Workshop on Software

Architecture, ACM Press, Orlando, FL, 1998. pp. 101–104.

[21] H.A. Muller, M.A. Orgun, S.R. Tilley, J.S. Uhl, A reverse engineering

approach to subsystem structure identification, J. Softw. Maintenance

5 (4) (1993) 181–204.

[22] H.A. Muller, K. Wong, S.R. Tilley, Understanding software systems

using reverse engineering technology, The 62nd Congress of

L’Association Canadienne Francaise pour l’Avancement des Sciences

Proceedings (ACFAS 1994) 1994.

[23] R. Kazman, S.J. Carriere, View extraction and view fusion in

architectural understanding, Fifth Int. Conf. Softw. Reuse 1998; 290–

299.

[24] S.R. Chidamber, C.F. Kemerer, A metrics suite for object-oriented

design, IEEE Trans. Softw. Eng. 20 (6) (1994) 476–493.

[25] M. Fowler, Refactoring: Improving the Design of Existing Code,

Addison-Wesley, Reading, MA, 2000.

[26] Systa, T., 2000. Static and Dynamic Reverse Engineering Techniques

for Java Software Systems. PhD University of Tampere, Finland,

2000.

[27] T. Systa, P. Yu, H.A. Muller, Analyzing Java software by combining

metrics and program visualization, Fourth Eur. Conf. Softw.

Maintenance Reeng. Zurich, Switzerland 2000; 199–208.

[28] PBS, http://swag.uwaterloo.ca/pbs.

[29] P.J. Finnigan, R.C. Holt, I. Kalas, S. Kerr, K. Kontogiannis,

H.A. Muller, J. Mylopoulos, S.G. Perelgut, M. Stanley, K. Wong,

The software bookshelf, IBM Syst. J. 36 (4) (1997) 564–593.

[30] M.N. Armstrong, C. Trudeau, Evaluating architectural extractors,

Proc. Fifth Working Conf. Reverse Eng. 1998.

[31] V. Tzerpos, R.C. Holt, The orphan adoption problem in architecture

maintenance, Proc. Fourth Working Conf. Reverse Eng. 1997; 76–82.

[32] Bishop, A.M. cxref. 2003.

[33] R.W. Schwanke, An intelligent tool for re-engineering software

modularity, Thirteenth Int. Conf. Softw. Eng. 1991.

[34] S. Mancoridis, B.S. Mitchell, Y. Chen, E.R. Gansner, Bunch: a

clustering tool for the recovery and maintenance of software system

structures, IEEE Int. Conf. Softw. Maintenance 1999.

[35] A. Cimitile, G. Visaggio, Software salvaging and the call dominance

tree, J. Syst. Softw. 28 (2) (1995) 117–127.

[36] A. Cimitile, A.R. Fasolino, P. Maresca, Reuse reengineering and

validation via concept assignment, IEEE Conf. Softw. Maintenance

1993; 216–225.

[37] O. Maqbool, H.A. Bahri, The weighted combined algorithm: a linkage

algorithm for software clustering, Proc. Eighth Eur. Conf. Softw.

Maintenance Reeng. 2004; 15–24.

[38] R. Koschke, T. Eisenbarth, A framework for experimental evaluation

of clustering techniques, 8th Int. Workshop Program Comprehension

2000; 201–210.

[39] J.F. Girard, R. Koschke, G. Schied, Comparison of abstract data type

and abstract state encapsulation detection techniques for architectural

understanding, Proc. Fourth Working Conf. Reverse Eng. 1997; 66–

75.

[40] N. Anquetil, T. Lethbridge, Extracting concepts from file names; a

new file clustering criterion, Proc. 20th Int. Conf. Softw. Maintenance

1998; 84–93.

[41] R. Fiutem, P. Tonella, G. Antoniol, E. Merlo, A cliche-based

environment to support architectural reverse engineering, Int. Conf.

Softw. Maintenance 1996.

[42] G. Antoniol, R. Fiutem, G. Lutteri, P. Tonella, S. Zanfei, E. Merlo,

Program understanding and maintenance with the CANTO environ-

ment, Int. Conf. Softw. Maintenance 1997.

[43] M.P. Chase, S.M. Christey, D.R. Harris, A.S. Yeh, Recovering

software architecture from multiple source code analyses, ACM-

SIGPLAN Workshop Program Anal. Softw. Tools Eng. 1998.

[44] C. Kramer, L. Prechelt, Design recovery by automated search for

structural design patterns in object oriented software, Third Working

Conf. Reverse Eng., Amsterdam, The Netherlands 1996; 208–215.

[45] G. Antoniol, R. Fiutem, L. Cristoforetti, Using metrics to identify

design patterns in object-oriented software, Fifth Int. Symp. Softw.

Metrics—METRICS’98 1998; 23–34.

[46] FUJABA, http://www.uni-paderborn.de/cs/fujaba.

[47] J. Niere, W. Schafer, J.P. Wadsack, L. Wendehals, J. Welsh, Towards

Pattern-based Design Recovery 2002.

[48] A. Weinand, E. Gamma, R. Marty, ETCC—an object oriented

application framework in CCC, Proc. Conf. Object-oriented

Programm. Syst., Languages Applic. 1998; 46–57.

[49] R.K. Keller, R. Schauer, S. Robitaille, P. Page, Pattern-based reverse-

engineering of design components, Proc. Int. Conf. Softw. Eng. 1999

1999; 226–235.

[50] J. Gustafsson, J. Paaki, L. Nenonen, A.I. Verkamo, Architecture-

centric evolution by software metrics and design patterns, Sixth Eur.

Conf. Softw. Maintenance Reeng. 2002; 108–115.

[51] P. Tonella, G. Antoniol, Object oriented design pattern inference, Int.

Conf. Softw. Maintenance, Oxford, UK 1999; 230–238.

[52] J. Rumbaugh, M. Blaha, W. Premerlani, F. Eddy, W. Lorenson,

Object-Oriented Modeling and Design, Prentice-Hall, Englewood

Cliffs, NJ, 1991.

[53] R. Fiutem, G. Antoniol, Idenitifying design-code inconsistencies in

object-oriented software: a case study, Int. Conf. Softw. Maintenance

1998; 94–102.

[54] G.C. Murphy, D. Notkin, K. Sullivan, Software reflexion models:

bridging the gap between source and high-level models, Third ACM

SIGSOFT Symp. Found. Softw. Eng., Washington, DC 1995.

[55] A. Bundy, A. Blewitt, I. Stark, Automatic verification of java design

patterns, Sixteenth Int. Conf. Autom. Softw. Eng. 2001; 324–327.

[56] M. Sefika, A. Sane, R.H. Campbell, Monitoring compliance of a

software system with its high-level design models, Eighteenth Int.

Conf. Softw. Eng. 1996; 387–396.

[57] Opdyke, W.F., 1992. Refactoring Object-Oriented Frameworks. PhD

University of Illinois, 1992.

[58] K. Beck, Test Driven Development: By Example, Addison Wesley

Professional, 2002.

Page 14: Combating architectural degeneration: a survey

L. Hochstein, M. Lindvall / Information and Software Technology 47 (2005) 643–656656

[59] R. Krikhaar, A. Postma, A. Sellink, M. Stroucken, C. Verhoef, A two-

phase process for software architecture improvement, IEEE Int. Conf.

Softw. Maintenance 1999; 371–380.

[60] IntelliJ IDEA, http://www.intellij.com/idea.

[61] Borland Together ControlCenter, http://www.borland.com/together.

[62] Eclipse, http://www.eclipse.org.

[63] JRefactory, http://jrefactory.sourceforge.net/csrefactory.html.

[64] D. Roberts, J. Brant, R. Johnson, Refactoring tool for Smalltalk,

Theory Practice Object Syst. 3 (4) (1997) 253–263.

[65] M.O. Cinneide, P. Nixon, A methodology for the automated

introduction of design patterns, Int. Conf. Softw. Maintenance 1999;.

[66] A.H. Eden, J. Gil, A. Yehudai, Precise specification and automatic

application of design patterns, Twelfth IEEE Int. Autom. Softw. Eng.

Conf., 2001 1997; 143–152.

[67] G. Florijn, M. Meijers, P. van Winsen, Tool support for object-

oriented patterns, ECOOP ’97 1997; 472–495.

[68] R.C. Holt, J.Y. Pak, GASE: visualizing software evolution-in-the-

large, Working Conf. Reverse Eng. 1996;.

[69] Q. Tu, M.W. Godfrey, An integrated approach for studying

architectural evolution, Tenth Int. Workshop Program Comprehen-

sion 2002; 127–136.

[70] G. Antoniol, G. Canfora, A. De Lucia, Maintaining traceability during

object-oriented software evolution: a case study, IEEE Int. Conf.

Softw. Maintenance, Oxford, UK 1999; 211–219.

[71] K. Beck, Extreme Programming Explained: Embrace Change,

Addison-Wesley, Reading, MA, 1999.

[72] L. Dobrica, E. Niemela, A survey on software architecture analysis

methods, IEEE Trans. Softw. Eng. 28 (7) (2002) 638–653.

[73] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, M. Stal,

Pattern-Oriented Software Architecture, Volume 1: A System of

Patterns, Wiley, New York, 1996.

[74] D. Schmidt, Pattern-Oriented Software Architecture, Volume 2:

Patterns for Concurrent and Networked Objects, Wiley, New York,

2000.

[75] Pattern Languages of Program Design, Addison-Wesley, 1995.

[76] Pattern Languages of Program Design 2, Addison-Wesley, 1996.

[77] Pattern Languages of Program Design 3, Addison-Wesley, 1997.

[78] Pattern Languages of Program Design 4, Addison-Wesley, 1999.

[79] P.C. Clements, A survey of architecture description languages, Eighth

Int. Workshop Softw. Specification Des. 1996; Design.

[80] N. Medvidovic, R.N. Taylor, A classification and comparison

framework for software architecture description languages, IEEE

Trans. Softw. Eng. 26 (1) (2000) 70–93.

[81] G. Travassos, F. Shull, M. Fredericks, V.R. Basili, Detecting defects

in object-oriented designs: using reading techniques to increase

software quality, Conf. Object Oriented Programm. Syst. Languages

Applic. (OOPSLA) 1999.

[82] P. Donzelli, A goal-driven and agent-based requirements engineering

framework, Requirements Eng. J. Mar (2003).

[83] E.S.K. Yu, Towards modelling and reasoning support for early-phase

requirements engineering, Proc. 3rd IEEE Int. Symp. Requirements

Eng. 1997.

[84] P. Bresciani, A. Perini, P. Giorgini, F. Giunchiglia, J. Mylopoulos,

Modeling early requirements in Tropos: a transformation based

approach, Proc. Second Int. Workshop Agent-Oriented Softw. Eng.

(AOSE) 2001.

[85] L. Prechelt, B. Unger-Lamprecht, M. Philippsen, W.F. Tichy, Two

controlled experiments assessing the usefulness of design pattern

documentation in program maintenance, IEEE Trans. Softw. Eng. 28

(6) (2002) 595–606.

[86] J. Sametinger, M. Riebisch, Evolution support by homogeneously

documenting patterns, aspects, and traces, Sixth Eur. Conf. Softw.

Maintenance Reeng. 2002; 134–140.

[87] A. Bryant, A. Catton, K. De Volder, G.C. Murphy, Explicit

programming, First Int. Conference on Aspect-Oriented Softw. Dev.

2002.

[88] J. Hannemann, G. Kiczales, Design pattern implementation in Java

and aspectJ, Seventeenth ACM Conf. Object-Oriented Programm.

Languages Applic. 2002.

[89] J. Bosch, Design patterns as language constructs, J. Object Oriented

Programm. 11 (2) (1998) 18–32.