combating architectural degeneration: a survey
TRANSCRIPT
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
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
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
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.
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.
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
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
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].
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
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].
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 notdiscuss how to estimate the probability of change for
various different classes of changes.
2.
Rushed development. Developers do not take the timerequired to design properly for change.
3.
Intuitive development is not modular. Developers do notnaturally write modular code.
4.
Lack of good example designs. There are not many gooddesigns available for designers to use as examples.
5.
Lack of design documentation. Design decisions are notoften documented in a form that is useful for future
maintainers.
6.
Poor quality of design documentation. If documentationexists, it is poorly organized, incomplete, and imprecise.
7.
Lack of training in design reviews. Many developers donot know how to prepare and hold a design review.
8.
Lack of training in design documentation. Developers donot know how to produce a readable and precise
documentation of a design.
9.
Time pressure. Developers are under time pressure andmistakenly 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 notdesigned to accommodate the necessary changes.
11.
Violations of the original design principles. Changesthat 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 towrite poor code, possibly in ignorance of the
architecture.
14.
Inadequate programming tools. Development tools thatare 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 havedifficulty understanding or maintaining code developed
by more skilled co-workers.
17.
Inadequate change process. Issues such as the lack of aversion 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 aredifficult 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 decisionstend 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
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.
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.
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.