No/Small citations needed
Finish it in 4 days
On the Criteria To Be Used in Decomposing Systems into
Reprinted from Communications of the ACM, Vol. 15, No. 12, December 1972 pp. 1053 – 1058
Copyright © 1972, Association for Computing Machinery Inc.
This is a digitized copy derived from an ACM copyrighted work. It is not guaranteed to be an
accurate copy of the author’s original work.
This paper discusses modularization as a mechanism for improving the flexibility and
comprehensibility of a system while allowing the shortening of its development time. The
effectiveness of a “modularization” is dependent upon the criteria used in dividing the
system into modules. A system design problem is presented and both a conventional and
unconventional decomposition are described. It is shown that the unconventional
decompositions have distinct advantages for the goals outlined. The criteria used in
arriving at the decompositions are discussed. The unconventional decomposition, if
implemented with the conventional assumption that a module consists of one or more
subroutines, will be less efficient in most cases. An alternative approach to implementation
which does not have this effect is sketched.
Key Words and Phrases: software, modules, modularity, software engineering, KWIC
index, software design
CR Categories: 4.0
A lucid statement of the philosophy of modular programming can be found in a 1970 textbook
on the design of system programs by Gouthier and Pont [1,10.23], which we quote below:
A well-defined segmentation of the project effort ensures system modularity. Each task
forms a separate, distinct program module. At implementation time each module and its
inputs and outputs are well-defined, there is no confusion in the intended interface with
other system modules. At checkout time the integrity of the module is tested
independently; there are few scheduling problems in synchronizing the completion of
several tasks before checkout can begin. Finally, the system is maintained in modular
fashion, system errors and deficiencies can be traced to specific system modules, thus
limiting the scope of detailed error searching.
Usually nothing is said about the criteria to be used in dividing the system into modules. This
paper will discuss that issue and, by means of examples, suggest some criteria which can be used
in decomposing a system into modules.
A Brief Status Report
The major advancement in the area of modular programming has been the development of
coding techniques and assemblers which (1) allow one module to be written with little
knowledge of the code in another module, and (2) allow modules to be reassembled and replaced
without reassembly of the whole system. This facility is extremely valuable for the production of
large pieces of code, but the systems most often used as examples of problem systems are highly
modularized programs and make use of the techniques mentioned above.
Expected Benefits of Modular Programming
The benefits expected of modular programming are: (1) managerial_development time should be
shortened because separate groups would work on each module with little need for
communication: (2) product flexibility_it should be possible to make drastic changes to one
module without a need to change others; (3) comprehensibility_it should be possible to study the
system one module at a time. The whole system can therefore be better designed because it is
What Is Modularization?
Below are several partial system descriptions called modularizations. In this context “module” is
considered to be a responsibility assignment rather than a subprogram. The modularizations
include the design decisions which must be made before the work on independent modules can
begin. Quite different decisions are included for each alternative, but in all cases the intention is
to describe all “system level” decisions (i.e. decisions which affect more than one module).
Example System 1: A KWIC Index Production System
The following description of a KWIC index will suffice for this paper. The KWIC index system
accepts an ordered set of lines, each line is an ordered set of words, and each word is an ordered
set of characters. Any line may be “circularly shifted” by repeatedly removing the first word and
appending it at the end of the line. The KWIC index system outputs a listing of all circular shifts
of all lines in alphabetical order.
This is a small system. Except under extreme circumstances (huge data base, no supporting
software), such a system could be produced by a good programmer within a week or two.
Consequently, none of the difficulties motivating modular programming are important for this
system. Because it is impractical to treat a large system thoroughly, we must go through the
exercise of treating this problem as if it were a large project. We give one modularization which
typifies current approaches, and another which has been used successfully in undergraduate class
We see the following modules:
Module 1: Input. This module reads the data lines from the input medium and stores them in
core for processing by the remaining modules. The characters are packed four to a word, and an
otherwise unused character is used to indicate the end of a word. An index is kept to show the
starting address of each line.
Module 2: Circular Shift. This module is called after the input module has completed its work.
It prepares an index which gives the address of the first character of each circular shift, and the
original index of the line in the array made up by module 1. It leaves its output in core with
words in pairs (original line number, starting address).
Module 3: Alphabetizing. This module takes as input the arrays produced by modules I and 2. It
produces an array in the same format as that produced by module 2. In this case, however, the
circular shifts are listed in another order (alphabetically).
Module 4: Output. Using the arrays produced by module 3 and module 1, this module produces
a nicely formatted output listing all of the circular shifts. In a sophisticated system the actual start
of each line will be marked, pointers to further information may be inserted, and the start of the
circular shift may actually not be the first word in the line, etc.
Module 5: Master Control. This module does little more than control the sequencing among the
other four modules. It may also handle error messages, space allocation, etc.
It should be clear that the above does not constitute a definitive document. Much more
information would have to be supplied before work could start. The defining documents would
include a number of pictures showing core formats, pointer conventions, calling conventions, etc.
All of the interfaces between the four modules must be specified before work could begin.
This is a modularization in the sense meant by all proponents of modular programming. The
system is divided into a number of modules with well-defined interfaces; each one is small
enough and simple enough to be thoroughly understood and well programmed. Experiments on a
small scale indicate that this is approximately the decomposition which would be proposed by
most programmers for the task specified.
We see the following modules:
Module 1: Line Storage. This module consists of a number of functions or subroutines which
provide the means by which the user of the module may call on it. The function call
CHAR(r,w,c) will have as value an integer representing the cth character in the rth line, wth
word. A call such as SETCHAR(rpv,c,d) will cause the cth character in the wth word of the rth
line to be the character represented by d (i.e. CHAR(r,w,c) = d). WORDS(r) returns as value the
number of words in line r. There are certain restrictions in the way that these routines may be
called; if these restrictions are violated the routines “trap” to an error-handling subroutine which
is to be provided by the users of the routine. Additional routines are available which reveal to the
caller the number of words in any line, the number of lines currently stored, and the number of
characters in any word. Functions DELINE and DELWRD are provided to delete portions of lines
which have already been stored. A precise specification of a similar module has been given in 
and  and we will not repeat it here.
Module 2: INPUT. This module reads the original lines from the input media and calls the line
storage module to have them stored internally.
Module 3: Circular Shifter. The principal functions provided by this module are analogs of
functions provided in module 1. The module creates the impression that we have created a line
holder containing not all of the lines but all of the circular shifts of the lines. Thus the function
call CSCHAR(I,w,c) provides the value representing the cth character in the wth word of the Ith
circular shift. It is specified that (1) if i < j then the shifts of line i precede the shifts of line j, and
(2) for each line the first shift is the original line, the second shift is obtained by making a one-
word rotation to the first shift, etc. A function CSSETUP is provided which must be called before
the other functions have their specified values. For a more precise specification of such a module
Module 4: Alphabetizer. This module consists principally of two functions. One, ALPH, must
be called before the other will have a defined value. The second, ITH, will serve as an index.
ITH(i) will give the index of the circular shift which comes ith in the alphabetical ordering.
Formal definitions of these functions are given .
Module 5: Output. This module will give the desired printing of set of lines or circular shifts.
Module 6: Master Control. Similar in function to the modularization above.
Comparison of the Two Modularizations
General. Both schemes will work. The first is quite conventional; the second has been used
successfully in a class project . Both will reduce the programming to the relatively
independent programming of a number of small, manageable, programs.
Note first that the two decompositions may share all data representations and access methods.
Our discussion is about two different ways of cutting up what may be the same object. A system
built according to decomposition I could conceivably be identical after assembly to one built
according to decomposition 2. The differences between the two alternatives are in the way that
they are divided into the work assignments, and the interfaces between modules. The algorithms
used in both cases might be identical. The systems are substantially different even if identical in
the runnable representation. This is possible because the runnable representation need only be
used for running; other representations are used for changing, documenting, understanding, etc.
The two systems will not be identical in those other representations.
Changeability. There are a number of design decisions which are questionable and likely to
change under many circumstances. This is a partial list.
1. Input format.
2. The decision to have all lines stored in core. For large jobs it may prove inconvenient or
impractical to keep all of the lines in core at any one time.
3. The decision to pack the characters four to a word. In cases where we are working with small
amounts of data it may prove undesirable to pack the characters; time will be saved by a
character per word layout. In other cases we may pack, but in different formats.
4. The decision to make an index for the circular shifts rather that actually store them as such.
Again, for a small index or a large core, writing them out may be the preferable approach.
Alternatively, we may choose to prepare nothing during CSSETUP All computation could be
done during the calls on the other functions such as CSCHAR.
5. The decision to alphabetize the list once, rather than either (a) search for each item when
needed, or (b) partially alphabetize as is done in Hoare’s FIND . In a number of circumstances
it would be advantageous to distribute the computation involved in alphabetization over the time
required to produce the index.
By looking at these changes we can see the differences between the two modularizations. The
first change is confined to one module in both decompositions. For the first decomposition the
second change would result in changes in every module! The same is true of the third change. In
the first decomposition the format of the line storage in core must be used by all of the programs.
In the second decomposition the story is entirely different. Knowledge of the exact way that the
lines are stored is entirely hidden from all but module 1. Any change in the manner of storage
can be confined to that module!
In some versions of this system there was an additional module in the decomposition. A symbol
table module (as specified in ) was used within the line storage module. This fact was
completely invisible to the rest of the system.
The fourth change is confined to the circular shift module in the second decomposition, but in
the first decomposition the alphabetizer and the output routines will also know of the change.
The fifth change will also prove difficult in the first decomposition. The output module will
expect the index to have been completed before it began. The alphabetizer module in the second
decomposition was designed so that a user could not detect when the alphabetization was
actually done. No other module need be changed.
Independent Development. In the first modularization the interfaces between the modules are
the fairly complex formats and table organizations described above. These represent design
decisions which cannot be taken lightly. The table structure and organization are essential to the
efficiency of the various modules and must be designed carefully. The development of those
formats will be a major part of the module development and that part must be a joint effort
among the several development groups. In the second modularization the interfaces are more
abstract; they consist primarily in the function names and the numbers and types of the
parameters. These are relatively simple decisions and the independent development of modules
should begin much earlier.
Comprehensibility. To understand the output module in the first modularization, it will be
necessary to understand something of the alphabetizer, the circular shifter, and the input module.
There will be aspects of the tables used by output which will only make sense because of the way
that the other modules work. There will be constraints on the structure of the tables due to the
algorithms used in the other modules. The system will only be comprehensible as a whole. It is
my subjective judgment that this is not true in the second modularization.
Many readers will now see what criteria were used in each decomposition. In the first
decomposition the criterion used was to make each major step in the processing a module. One
might say that to get the first decomposition one makes a flowchart. This is the most common
approach to decomposition or modularization. It is an outgrowth of all programmer training
which teaches us that we should begin with a rough flowchart and move from there to a detailed
implementation. The flowchart was a useful abstraction for systems with on the order of 5,000-
10,000 instructions, but as we move beyond that it does not appear to be sufficient; something
additional is needed.
The second decomposition was made using “information hiding” [41 as a criterion. The modules
no longer correspond to steps in the processing. The line storage module, for example, is used in
almost every action by the system. Alphabetization may or may not correspond to a phase in the
processing according to the method used. Similarly, circular shift might, in some circumstances,
not make any table at all but calculate each character as demanded. Every module in the second
decomposition is characterized by its knowledge of a design decision which it hides from all
others. Its interface or definition was chosen to reveal as little as possible about its inner
Improvement in Circular Shift Module
To illustrate the impact of such a criterion let us take a closer look at the design of the circular
shift module from the second decomposition. Hindsight now suggests that this definition reveals
more information than necessary. While we carefully hid the method of storing or calculating the
list of circular shifts, we specified an order to that list. Programs could be effectively written if
we specified only ( I ) that the lines indicated in circular shift’s current definition will all exist in
the table, (2) that no one of them would be included twice, and (3) that an additional function
existed which would allow us to identify the original line given the shift. By prescribing the
order for the shifts we have given more information than necessary and so unnecessarily
restricted the class of systems that we can build without changing the definitions. For example,
we have not allowed for a system in which the circular shifts were produced in alphabetical
order, ALPH is empty, and ITH simply returns its argument as a value. Our failure to do this in
constructing the systems with the second decomposition must clearly be classified as a design
In addition to the general criteria that each module hides some design decision from the rest of
the system, we can mention some specific examples of decompositions which seem advisable.
1. A data structure, its internal linkings, accessing procedures and modifying procedures are part
of a single module. They are not shared by many modules as is conventionally done. This notion
is perhaps just an elaboration of the assumptions behind the papers of Balzer  and Mealy .
Design with this in mind is clearly behind the design of BLISS .
2. The sequence of instructions necessary to call a given routine and the routine itself are part of
the same module. This rule was not relevant in the Fortran systems used for experimentation but
it becomes essential for systems constructed in an assembly language. There are no perfect
general calling sequences for real machines and consequently they tend to vary as we continue
our search for the ideal sequence. By assigning responsibility for generating the call to the person
responsible for the routine we make such improvements easier and also make it more feasible to
have several distinct sequences in the same software structure.
3. The formats of control blocks used in queues in operating systems and similar programs must
be hidden within a “control block module.” It is conventional to make such formats the interfaces
between various modules. Because design evolution forces frequent changes on control block
formats such a decision often proves extremely costly.
4. Character codes, alphabetic orderings and similar data should be hidden in a module for
greatest flexibility. 5. The sequence in which certain items will be processed should (as far as
practical) be hidden within a single module. Various changes ranging from equipment additions
to unavailability of certain resources in an operating system make sequencing extremely
Efficiency and Implementation
If we are not careful the second decomposition will prove to be much less efficient than the first.
If each of the functions is actually implemented as a procedure with an elaborate calling
sequence there will be a great deal of such calling due to the repeated switching between
modules. The first decomposition will not suffer from this problem because there is relatively
infrequent transfer of control between modules.
To save the procedure call overhead, yet gain the advantages that we have seen above, we must
implement these modules in an unusual way. In many cases the routines will be best inserted into
the code by an assembler; in other cases, highly specialized and efficient transfers would be
inserted. To successfully and efficiently make use of the second type of decomposition will
require a tool by means of which programs may be written as if the functions were subroutines,
but assembled by whatever implementation is appropriate. If such a technique is used, the
separation between modules may not be clear in the final code. For that reason additional
program modification features would also be useful. In other words, the several representations
of the program (which were mentioned earlier) must be maintained in the machine together with
a program performing mapping between them.
A Decomposition Common to a Compiler and Interpretor for the Same
In an earlier attempt to apply these decomposition rules to a design project we constructed a
translator for a Markov algorithm expressed in the notation described in . Although it was not
our intention to investigate the relation between compiling and interpretive translators of a
language, we discovered that our decomposition was valid for a pure compiler and several
varieties of interpreters for the language. Although there would be deep and substantial
differences in the final running representations of each type of compiler, we found that the
decisions implicit in the early decomposition held for all.
This would not have been true if we had divided responsibilities along the classical lines for
either a compiler or interpretor (e.g. syntax recognizer, code generator, run time routines for a
compiler). Instead the decomposition was based upon the hiding of various decisions as in the
example above. Thus register representation, search algorithm, rule interpretation etc. were
modules and these problems existed in both compiling and interpretive translators. Not only was
the decomposition valid in all cases, but many of the routines could be used with only slight
changes in any sort of translator.
This example provides additional support for the statement that the order in time in which
processing is expected to take place should not be used in making the decomposition into
modules. It further provides evidence that a careful job of decomposition can result in
considerable carryover of work from one project to another.
A more detailed discussion of this example was contained in .
We can find a program hierarchy in the sense illustrated by Dijkstra  in the system defined
according to decomposition 2. If a symbol table exists, it functions without any of the other
modules, hence it is on level 1. Line storage is on level I if no symbol table is used or it is on
level 2 otherwise. Input and Circular Shifter require line storage for their functioning. Output and
Alphabetizer will require Circular Shifter, but since Circular Shifter and line holder are in some
sense compatible, it would be easy to build a parameterized version of those routines which
could be used to alphabetize or print out either the original lines or the circular shifts. In the first
usage they would not require Circular Shifter; in the second they would. In other words, our
design has allowed us to have a single representation for programs which may run at either of
two levels in the hierarchy.
In discussions of system structure it is easy to confuse the benefits of a good decomposition with
those of a hierarchical structure. We have a hierarchical structure if a certain relation may be
defined between the modules or programs and that relation is a partial ordering. The relation we
are concerned with is “uses” or “depends upon.” It is better to use a relation between programs
since in many cases one module depends upon only part of another module (e.g. Circular Shifter
depends only on the output parts of the line holder and not on the correct working of SKI
WORD). It is conceivable that we could obtain the benefits that we have been discussing without
such a partial ordering, e.g. if all the modules were on the same level. The partial ordering gives
us two additional benefits. First, parts of the system are benefited (simplified) because they use
the services of lower levels. Second, we are able to cut off the upper levels and still have a usable
and useful product. For example, the symbol table can be used in other applications; the line
holder could be the basis of a question answering system. The existence of the hierarchical
structure assures us that we can “prune” off the upper levels of the tree and start a new tree on the
old trunk. If we had designed a system in which the “low level” modules made some use of the
“high level” modules, we would not have the hierarchy, we would find it much harder to remove
portions of the system, and “level” would not have much meaning in the system.
Since it is conceivable that we could have a system with the type of decomposition shown in
version I (important design decisions in the interfaces) but retaining a hierarchical structure, we
must conclude that hierarchical structure and “clean” decomposition are two desirable but
independent properties of a system structure.
We have tried to demonstrate by these examples that it is almost always incorrect to begin the
decomposition of a system into modules on the basis of a flowchart. We propose instead that one
begins with a list of difficult design decisions or design decisions which are likely to change.
Each module is then designed to hide such a decision from the others. Since, in most cases,
design decisions transcend time of execution, modules will not correspond to steps in the
processing. To achieve an efficient implementation we must abandon the assumption that a
module is one or more subroutines, and instead allow subroutines and programs to be assembled
collections of code from various modules.
Received August 1971; revised November 1971
I. Gauthier, Richard, and Pont, Stephen. Designing Systems Programs, (C), Prentice-Hall,
Englewood Cliffs, N.J., 1970.
2. Hoare, C. A. R. Proof of a program, FIND. Comm. ACM 14 1 (Jan. 1971), 39-45.
3. Parnas, D. L. A technique for software module specification with examples. Comm. ACM 15,
5 (May, 1972), 330-336.
4. Parnas, D. L. Information distribution aspects of design methodology. Tech. Rept., Depart.
Computer Science, Carnegie Mellon U., Pittsburgh, Pa., 1971. Also presented at the IFIP
Congress 1971, Ljubljana, Yugoslavia.
5. Dijkstra, E. W. The structure of “THE”-multiprogramming system. Comm. ACM 11, 5 (May
6. Galler, B., and Perlis, A. J. A View of Programming Languages, Addison-Wesley, Reading,
7. Parnas, D. L. A course on software engineering. Proc. SIGCSE Technical Symposium, Mar.
8. Parnas, D. L. On the criteria to be used in decomposing systems into modules. Tech. Rept.,
Depart.. Computer Science, Carnegie-Mellon U., Pittsburgh, Pa., 1971.
9. Balzer, R. M. Dataless programming. Proc. AFIPS 1967 FJCC, Vol. 31, AFIPS Press,
Montvale, N.J., pp. 535-544.
10. Mealy, G. H. Another look at data. Proc. AFIPS 1967 FJCC Vol. 31, AFIPS Press, Montvale,
N.J., pp. 525-534.
11. Wulf, W. A., Russell, D. B., and Habermann, A. N. BLISS A language for systems
programming Comm. ACM 14, 12 (Dec. 1971), 780-790.
An Introduction to Software Architecture
David Garlan and Mary Shaw
School of Computer Science
Carnegie Mellon University
Pittsburgh, PA 15213-3890
Also published as “An Introduction to Software Architecture,” Advances in Software Engineering
and Knowledge Engineering, Volume I, edited by V.Ambriola and G.Tortora, World Scientific
Publishing Company, New Jersey, 1993.
Also appears as CMU Software Engineering Institute Technical Report
©1994 by David Garlan and Mary Shaw
This work was funded in part by the Department of Defense Advanced Research Project Agency under grant
MDA972-92-J-1002, by National Science Foundation Grants CCR-9109469 and CCR-9112880, and by a grant
from Siemens Corporate Research. It was also funded in part by the Carnegie Mellon University School of
Computer Science and Software Engineering Institute (which is sponsored by the U.S. Department of Defense).
The views and conclusions contained in this document are those of the authors and should not be interpreted
as representing the official policies, either expressed or implied, of the U.S. Government, the Department of
Defense, the National Science Foundation, Siemens Corporation, or Carnegie Mellon University.
Keywords: Software architecture, software design, software engineering
As the size of software systems increases, the algorithms and data structures of
the computation no longer constitute the major design problems. When
systems are constructed from many components, the organization of the
overall system—the software architecture—presents a new set of design
problems. This level of design has been addressed in a number of ways
including informal diagrams and descriptive terms, module interconnection
languages, templates and frameworks for systems that serve the needs of
specific domains, and formal models of component integration mechanisms.
In this paper we provide an introduction to the emerging field of software
architecture. We begin by considering a number of common architectural
styles upon which many systems are currently based and show how different
styles can be combined in a single design. Then we present six case studies to
illustrate how architectural representations can improve our understanding of
complex software systems. Finally, we survey some of the outstanding
problems in the field, and consider a few of the promising research directions.
1. Introduction ……………………………………………………………………………………….. 2
2. From Programming Languages to Software Architecture ………………… 3
2.1. High-level Programming Languages …………………………………………………………. 3
2.2. Abstract Data Types………………………………………………………………………………. 4
2.3. Software Architecture ……………………………………………………………………………. 4
3. Common Architectural Styles……………………………………………………………. 5
3.1. Pipes and Filters …………………………………………………………………………………… 6
3.2. Data Abstraction and Object-Oriented Organization ……………………………………. 8
3.3. Event-based, Implicit Invocation ……………………………………………………………… 9
3.4. Layered Systems …………………………………………………………………………………… 11
3.5. Repositories …………………………………………………………………………………………. 12
3.6. Table Driven Interpreters ……………………………………………………………………….. 13
3.7. Other Familiar Architectures………………………………………………………………….. 14
3.8. Heterogeneous Architectures……………………………………………………………………. 15
4. Case Studies ………………………………………………………………………………………… 16
4.1. Case Study 1: Key Word in Context …………………………………………………………… 16
4.2. Case Study 2: Instrumentation Software …………………………………………………….. 22
4.3. Case 3: A Fresh View of Compilers …………………………………………………………… 26
4.4. Case 4: A Layered Design with Different Styles for the Layers ……………………… 28
4.5. Case 5: An Interpreter Using Different Idioms for the Components …………………… 30
4.6. Case 6: A Blackboard Globally Recast as Interpreter ……………………………………. 33
5. Past, Present, and Future ……………………………………………………………………. 36
Acknowledgements ………………………………………………………………………………………. 37
Bibliography ………………………………………………………………………………………………….. 37
Garlan & Shaw: An Introduction to Software Architecture 1
List of Figures
1 Pipes and Filters …………………………………………………………………………………………. 7
2 Abstract Data Types and Objects…………………………………………………………………. 8
3 Layered Systems ……………………………………………………………………………………….. 11
4 The Blackboard …………………………………………………………………………………………. 13
5 Interpreter…………………………………………………………………………………………………. 14
6 KWIC – Shared Data Solution ………………………………………………………………….. 18
7 KWIC – Abstract Data Type Solution ……………………………………………………….. 19
8 KWIC – Implicit Invocation Solution ……………………………………………………… 20
9 KWIC – Pipe and Filter Solution ……………………………………………………………… 20
10 KWIC – Comparison of Solutions ……………………………………………………………. 21
11 Oscilloscopes – An Object-oriented Model……………………………………………….. 23
12 Oscilloscopes – A Layered Model ……………………………………………………………… 24
13 Oscilloscopes – A Pipe and Filter Model…………………………………………………… 24
14 Oscilloscopes – A Modified Pipe and Filter Model…………………………………… 25
15 Traditional Compiler Model ……………………………………………………………………. 26
16 Traditional Compiler Model with Shared Symbol Table ……………………….. 26
17 Modern Canonical Compiler …………………………………………………………………… 27
18 Canonical Compiler, Revisited………………………………………………………………… 27
19 PROVOX – Hierarchical Top Level…………………………………………………………… 28
20 PROVOX – Object-oriented Elaboration …………………………………………………… 29
21 Basic Rule-Based System ………………………………………………………………………….. 31
22 Sophistocated Rule-Based System……………………………………………………………. 32
23 Simplified Rule-Based System…………………………………………………………………. 33
24 Hearsay-II ………………………………………………………………………………………………….. 34
25 Blackboard View of Hearsay-II …………………………………………………………………. 35
26 Interpreter View of Hearsay-II …………………………………………………………………. 36
Garlan & Shaw: An Introduction to Software Architecture 2
As the size and complexity of software systems increases, the design problem
goes beyond the algorithms and data structures of the computation: designing
and specifying the overall system structure emerges as a new kind of problem.
Structural issues include gross organization and global control structure;
protocols for communication, synchronization, and data access; assignment of
functionality to design elements; physical distribution; composition of design
elements; scaling and performance; and selection among design alternatives.
This is the software architecture level of design. There is a considerable
body of work on this topic, including module interconnection languages,
templates and frameworks for systems that serve the needs of specific domains,
and formal models of component integration mechanisms. In addition, an
implicit body of work exists in the form of descriptive terms used informally to
describe systems. And while there is not currently a well-defined terminology
or notation to characterize architectural structures, good software engineers
make common use of architectural principles when designing complex
software. Many of the principles represent rules of thumb or idiomatic
patterns that have emerged informally over time. Others are more carefully
documented as industry and scientific standards.
It is increasingly clear that effective software engineering requires facility in
architectural software design. First, it is important to be able to recognize
common paradigms so that high-level relationships among systems can be
understood and so that new systems can be built as variations on old systems.
Second, getting the right architecture is often crucial to the success of a software
system design; the wrong one can lead to disastrous results. Third, detailed
understanding of software architectures allows the engineer to make
principled choices among design alternatives. Fourth, an architectural system
representation is often essential to the analysis and description of the high-
level properties of a complex system.
In this paper we provide an introduction to the field of software
architecture. The purpose is to illustrate the current state of the discipline and
examine the ways in which architectural design can impact software design.
The material presented here is selected from a semester course, Architectures
for Software Systems, taught at CMU by the authors . Naturally, a short
paper such as this can only briefly highlight the main features of the terrain.
This selection emphasizes informal descriptions omitting much of the course’s
material on specification, evaluation, and selection among design alternatives.
We hope, nonetheless, that this will serve to illuminate the nature and
significance of this emerging field.
In the following section we outline a number of common architectural
styles upon which many systems are currently based, and show how
Garlan & Shaw: An Introduction to Software Architecture 3
heterogeneous styles can be combined in a single design. Next we use six case
studies to illustrate how architectural representations of a software system can
improve our understanding of complex systems. Finally, we survey some of
the outstanding problems in the field, and consider a few of the promising
The text that makes up the bulk of this article has been drawn from
numerous other publications by the authors. The taxonomy of architectural
styles and the case studies have incorporated parts of several published papers
[1, 2, 3, 4]. To a lesser extent material has been drawn from other articles by the
authors [5, 6, 7].
2. From Programming Languages to Software Architecture
One characterization of progress in programming languages and tools has been
regular increases in abstraction level—or the conceptual size of software
designers building blocks. To place the field of Software Architecture into
perspective let us begin by looking at the historical development of abstraction
techniques in computer science.
2.1. High-level Programming Languages
When digital computers emerged in the 1950s, software was written in
machine language; programmers placed instructions and data individually and
explicitly in the computer’s memory. Insertion of a new instruction in a
program might require hand-checking of the entire program to update
references to data and instructions that moved as a result of the insertion.
Eventually it was recognized that the memory layout and update of references
could be automated, and also that symbolic names could be used for operation
codes, and memory addresses. Symbolic assemblers were the result. They were
soon followed by macro processors, which allowed a single symbol to stand for
a commonly-used sequence of instructions. The substitution of simple
symbols for machine operation codes, machine addresses yet to be defined, and
sequences of instructions was perhaps the earliest form of abstraction in
In the latter part of the 1950s, it became clear that certain patterns of
execution were commonly useful—indeed, they were so well understood that
it was possible to create them automatically from a notation more like
mathematics than machine language. The first of these patterns were for
evaluation of arithmetic expressions, for procedure invocation, and for loops
and conditional statements. These insights were captured in a series of early
high-level languages, of which Fortran was the main survivor.
Higher-level languages allowed more sophisticated programs to be
developed, and patterns in the use of data emerged. Whereas in Fortran data
types served primarily as cues for selecting the proper machine instructions,
Garlan & Shaw: An Introduction to Software Architecture 4
data types in Algol and it successors serve to state the programmer’s intentions
about how data should be used. The compilers for these languages could build
on experience with Fortran and tackle more sophisticated compilation
problems. Among other things, they checked adherence to these intentions,
thereby providing incentives for the programmers to use the type mechanism.
Progress in language design continued with the introduction of modules to
provide protection for related procedures and data structures, with the
separation of a module’s specification from its implementation, and with the
introduction of abstract data types.
2.2. Abstract Data Types
In the late 1960s, good programmers shared an intuition about software
development: If you get the data structures right, the effort will make
development of the rest of the program much easier. The abstract data type
work of the 1970s can be viewed as a development effort that converted this
intuition into a real theory. The conversion from an intuition to a theory
• the software structure (which included a representation packaged with
its primitive operators),
• specifications (mathematically expressed as abstract models or algebraic
• language issues (modules, scope, user-defined types),
• integrity of the result (invariants of data structures and protection from
• rules for combining types (declarations),
• information hiding (protection of properties not explicitly included in
The effect of this work was to raise the design level of certain elements of
software systems, namely abstract data types, above the level of programming
language statements or individual algorithms. This form of abstraction led to
an understanding of a good organization for an entire module that serves one
particular purpose. This involved combining representations, algorithms,
specifications, and functional interfaces in uniform ways. Certain support was
required from the programming language, of course, but the abstract data type
paradigm allowed some parts of systems to be developed from a vocabulary of
data types rather than from a vocabulary of programming-language constructs.
2.3. Software Architecture
Just as good programmers recognized useful data structures in the late 1960s,
good software system designers now recognize useful system organizations.
Garlan & Shaw: An Introduction to Software Architecture 5
One of these is based on the theory of abstract data types. But this is not the
only way to organize a software system.
Many other organizations have developed informally over time, and are
now part of the vocabulary of software system designers. For example, typical
descriptions of software architectures include synopses such as (italics ours):
•“Camelot is based on the client-server model and uses remote procedure
calls both locally and remotely to provide communication among
applications and servers.”
•“Abstraction layering and system decomposition provide the appearance
of system uniformity to clients, yet allow Helix to accommodate a
diversity of autonomous devices. The architecture encourages a client-
server model for the structuring of applications.”
•“We have chosen a distributed, object-oriented approach to managing
•“The easiest way to make the canonical sequential compiler into a
concurrent compiler is to pipeline the execution of the compiler phases
over a number of processors. . . . A more effective way [is to] split the
source code into many segments, which are concurrently processed
through the various phases of compilation [by multiple compiler
processes] before a final, merging pass recombines the object code into a
Other software architectures are carefully documented and often widely
disseminated. Examples include the International Standard Organization’s
Open Systems Interconnection Reference Model (a layered network
architecture) , the NIST/ECMA Reference Model (a generic software
engineering environment architecture based on layered communication
substrates) [13, 14], and the X Window System (a distributed windowed user
interface architecture based on event triggering and callbacks) .
We are still far from having a well-accepted taxonomy of such architectural
paradigms, let alone a fully-developed theory of software architecture. But we
can now clearly identify a number of architectural patterns, or styles, that
currently form the basic repertoire of a software architect.
3. Common Architectural Styles
We now examine some of these representative, broadly-used architectural
styles. Our purpose is to illustrate the rich space of architectural choices, and
indicate what are some of the tradeoffs in choosing one style over another.
To make sense of the differences between styles, it helps to have a common
framework from which to view them. The framework we will adopt is to treat
an architecture of a specific system as a collection of computational
Garlan & Shaw: An Introduction to Software Architecture 6
components—or simply components-—together with a description of the
interactions between these components—the connectors. Graphically speaking,
this leads to a view of an abstract architectural description as a graph in which
the nodes represent the components and the arcs represent the connectors. As
we will see, connectors can represent interactions as varied as procedure call,
event broadcast, database queries, and pipes.
An architectural style, then, defines a family of such systems in terms of a
pattern of structural organization. More specifically, an architectural style
determines the vocabulary of components and connectors that can be used in
instances of that style, together with a set of constraints on how they can be
combined. These can include topological constraints on architectural
descriptions (e.g., no cycles). Other constraints—say, having to do with
execution semantics—might also be part of the style definition.
Given this framework, we can understand what a style is by answering the
following questions: What is the structural pattern—the components,
connectors, and constraints? What is the underlying computational model?
What are the essential invariants of the style? What are some common
examples of its use? What are the advantages and disadvantages of using that
style? What are some of the common specializations?
3.1. Pipes and Filters
In a pipe and filter style each component has a set of inputs and a set of
outputs. A component reads streams of data on its inputs and produces
streams of data on its outputs, delivering a complete instance of the result in a
standard order. This is usually accomplished by applying a local
transformation to the input streams and computing incrementally so output
begins before input is consumed. Hence components are termed “filters”. The
connectors of this style serve as conduits for the streams, transmitting outputs
of one filter to inputs of another. Hence the connectors are termed “pipes”.
Among the important invariants of the style, filters must be independent
entities: in particular, they should not share state with other filters. Another
important invariant is that filters do not know the identity of their upstream
and downstream filters. Their specifications might restrict what appears on the
input pipes or make guarantees about what appears on the output pipes, but
they may not identify the components at the ends of those pipes. Furthermore,
the correctness of the output of a pipe and filter network should not depend on
the order in which the filters perform their incremental processing—although
fair scheduling can be assumed. (See  for an in-depth discussion of this style
and its formal properties.) Figure 1 illustrates this style.
Common specializations of this style include pipelines, which restrict the
topologies to linear sequences of filters; bounded pipes, which restrict the
amount of data that can reside on a pipe; and typed pipes, which require that
the data passed between two filters have a well-defined type.
Garlan & Shaw: An Introduction to Software Architecture 7
Data flowASCII stream
Figure 1: Pipes and Filters
A degenerate case of a pipeline architecture occurs when each filter
processes all of its input data as a single entity.1 In this case the architecture
becomes a “batch sequential” system. In these systems pipes no longer serve
the function of providing a stream of data, and therefore are largely vestigial.
Hence such systems are best treated as instances of a separate architectural style.
The best known examples of pipe and filter architectures are programs
written in the Unix shell . Unix supports this style by providing a notation
for connecting components (represented as Unix processes) and by providing
run time mechanisms for implementing pipes. As another well-known
example, traditionally compilers have been viewed as a pipeline systems
(though the phases are often not incremental). The stages in the pipeline
include lexical analysis, parsing, semantic analysis, code generation. (We
return to this example in the case studies.) Other examples of pipes and filters
occur in signal processing domains , functional programming , and
distributed systems .
Pipe and filter systems have a number of nice properties. First, they allow
the designer to understand the overall input/output behavior of a system as a
simple composition of the behaviors of the individual filters. Second, they
support reuse: any two filters can be hooked together, provided they agree on
the data that is being transmitted between them. Third, systems can be easily
maintained and enhanced: new filters can be added to existing systems and old
filters can be replaced by improved ones. Fourth, they permit certain kinds of
specialized analysis, such as throughput and deadlock analysis. Finally, they
naturally support concurrent execution. Each filter can be implemented as a
separate task and potentially executed in parallel with other filters.
But these systems also have their disadvantages.2 First, pipe and filter
systems often lead to a batch organization of processing. Although filters can
1In general, we find that the boundaries of styles can overlap. This should not deter us from
identifying the main features of a style with its central examples of use.
2This is true in spite of the fact that pipes and filters, like every style, has a set of devout
religious followers—people who believe that all problems worth solving can best be solved using
that particular style.
Garlan & Shaw: An Introduction to Software Architecture 8
process data incrementally, since filters are inherently independent, the
designer is forced to think of each filter as providing a complete
transformation of input data to output data. In particular, because of their
transformational character, pipe and filter systems are typically not good at
handling interactive applications. This problem is most severe when
incremental display updates are required, because the output pattern for
incremental updates is radically different from the pattern for filter output.
Second, they may be hampered by having to maintain correspondences
between two separate, but related streams. Third, depending on the
implementation, they may force a lowest com-
mon denominator on data transmission, resulting in added work for each
filter to parse and unparse its data. This, in turn, can lead both to loss of
performance and to increased complexity in writing the filters themselves.
3.2. Data Abstraction and Object-Oriented Organization
In this style data representations and their associated primitive operations are
encapsulated in an abstract data type or object. The components of this style are
the objects—or, if you will, instances of the abstract data types. Objects are
examples of a sort of component we call a manager because it is responsible for
preserving the integrity of a resource (here the representation). Objects interact
through function and procedure invocations. Two important aspects of this
style are (a) that an object is responsible for preserving the integrity of its
representation (usually by maintaining some invariant over it), and (b) that
the representation is hidden from other objects. Figure 2 illustrates this style.3
obj is a manager
op is an invocation
Figure 2: Abstract Data Types and Objects
3We haven’t mentioned inheritance in this description. While inheritance is an important
organizing principle for defining the types of objects in a system, it does not have a direct
architectural function. In particular, in our view, an inheritance relationship is not a connector,
since it does not define the interaction between components in a system. Also, in an architectural
setting inheritance of properities is not restricted to object types—but may include connectors and
even architectural styles.
Garlan & Shaw: An Introduction to Software Architecture 9
The use of abstract data types, and increasingly the use of object-oriented
systems, is, of course, widespread. There are many variations. For example,
some systems allow “objects” to be concurrent tasks; others allow objects to
have multiple interfaces [20, 21].
Object-oriented systems have many nice properties, most of which are well
known. Because an object hides its representation from its clients, it is possible
to change the implementation without affecting those clients. Additionally,
the bundling of a set of accessing routines with the data they manipulate
allows designers to decompose problems into collections of interacting agents.
But object-oriented systems also have some disadvantages. The most
significant is that in order for one object to interact with another (via
procedure call) it must know the identity of that other object. This is in
contrast, for example, to pipe and filter systems, where filters do need not
know what other filters are in the system in order to interact with them. The
significance of this is that whenever the identity of an object changes it is
necessary to modify all other objects that explicitly invoke it. In a module-
oriented language this manifests itself as the need to change the “import” list
of every module that uses the changed module. Further there can be side-
effect problems: if A uses object B and C also uses B, then C’s effects on B look
like unexpected side effects to A, and vice versa.
3.3. Event-based, Implicit Invocation
Traditionally, in a system in which the component interfaces provide a
collection of procedures and functions, components interact with each other by
explicitly invoking those routines. However, recently there has been
considerable interest in an alternative integration technique, variously referred
to as implicit invocation, reactive integration, and selective broadcast. This
style has historical roots in systems based on actors , constraint satisfaction,
daemons, and packet-switched networks.
The idea behind implicit invocation is that instead of invoking a
procedure directly, a component can announce (or broadcast) one or more
events. Other components in the system can register an interest in …
Evaluating Alternative Designs
Imagine you are in charge of a small 3-person development group who will be
developing a KWIC index generation tool for an online course. Specifically, your
product should have the following features:
• It’s input will be a group of HTML files representing the online notes
from one lecture.
• The titles of each page in the group will be indexed.
• The tool will be run once a week with a new lecture’s worth of HTML
pages. The tool should add the new index entries to the existing index.
• The output of the indexing tool will be HTML that is ready to post on
• The execution platform will be a Windows 10 PC.
Although this list of features only gives an outline of the requirements for the
product, it should give you enough of a feel for the intended use that you can
make educated guesses for any questions you run into as you complete the
assignment. Feel free to use your best judgement in such a situation. If you have
other questions about features, feel free to ask.
The main task of your assignment is to evaluate the 4 KWIC Index architectures
discussed in the Reading 03 reading assignment(Main Program/Subroutine with
Shared Data, Abstract Data Types, Implicit Invocation, and Pipes and Filters)
, and then select the one you believe is best for the scenario outlined above.
Write up your results in a short paper (>= 4 pages) that you will turn in. Be sure
to include the following elements in your solution:
1. Project Summary: provide a brief description of the requirements for
the system you are creating. You can start with the 5 bullets listed
above, and extend that with any additional assumptions, features, or
restrictions you think up yourself as you proceed through the
2. Evaluation Criteria: Devise a list of key design decisions (either a
choice to support some kind of change, or a choice to commit to
something unchangeable) that are relevant to the system at hand. Be
specific; something like “support a change in function” is too general–
“support a change from indexing page titles to indexing all words on
the page” is better. Your goal is to provide a set of design decisions
that is more comprehensive than the list Garlan and Shaw used in
Figure 10 on p. 21, and that will provide an effective way to compare
the architectures for this specific scenario.
3. Evaluate 4 Architectures: Evaluate each of the 4 architectures
against your set of criteria, briefly discussing the strengths or
weaknesses it has with respect to each design decision you have
4. Select the Best: Choose which of the 4 is best suited for use in this
hypothetical situation. Justify your choice by drawing on the
evaluation you have performed.
5. Conclusions: You may find that your final choice among these 4
candidate architectures still has some shortcomings. In wrapping up
your paper, you can identify any weak points in the architecture you
have selected that need to be addressed for the project to be a success.
You may also make suggestions about alternative architectures that
were not considered, or about anything you might change or do
differently in your selected architecture for solving the problem.
The bulk of your paper will probably be spent on describing the evaluation
criteria and presenting the evaluation of the 4 alternatives.
The following rubric will be used to assess your work:
Project Summary 10
Excellent: Provides a clear explanation of realistic
features, including significant additional features above
the minimum 5, and providing additional concrete details
about the 5 requirements outlined in the assignment.
Realistic assumptions about operating conditions or
expectations of use are clearly communicated.
Good: Provides a clear explanation of realistic
features, including some additions to features above the
minimum 5, and provides concrete elaboration of some of
the 5 requirements outlined in the assignment. Realistic
assumptions about operating conditions or expectations of
use are communicated.
Satisfactory: Provides a clear explanation of features
that may include minor additions to features above the
minimum 5. Some of the 5 required features may be
elaborated more concretely. Some simple assumptions or
restrictions regarding use of the system are presented.
Poor: Simply restates the minimum 5 features, without
communicating any significant additional insight into
requirements for the system.
No attempt: Section is missing. 0/10
Evaluation criteria 30
Excellent: Provides a clear, specific list of evaluation
criteria that is significantly more comprehensive that
the example outlined in the textbook. Criteria are
directly related to the requirements presented in the
summary, and how each criterion can be judged is
Good: Provides a clear list of evaluation criteria with
a significant attempt to be comprehensive. Most criteria
are directly related to the requirements presented in the
summary and specific enough to have direct relevance to
the system at hand. How most criteria can be judged is
Satisfactory: Provides a list of criteria that goes
beyond the basics covered in the case study from the
textbook. Some criteria may be too general, too difficult
to apply, or poorly connected to the requirements
presented in the summary.
Poor: Simply restates the basic criteria described in
the text without any significant additional
No attempt: Section is missing. 0/10
Excellent: Explicitly addresses how each of the four
architectures measures up against each criterion you have
developed in a systematic way. Explicit strengths and/or
weaknesses for each architecture are noted for each
evaluation criterion. A table or other mechanism is used
to summarize the results of the evaluation in a concise
way that can be quickly scanned.
Good: Explicitly addresses how each of the four
architectures measures up against each criterion you have
developed in a systematic way, including a clear
description of the strengths and weaknesses of each of
the four architectures.
Satisfactory: Explicitly addresses how each of the four
architectures measures up against each criterion you have
Poor: Attempts to evaluate the four architectures, but
without any clear connection to the presented evaluation
critieria or any clear summary of strengths and
weaknesses for each architecture.
No attempt: Section is missing. 0/10
Excellent: An architecture is chosen as best, and a
well-reasoned systematic justification that relies on all
criteria in your evaluation is presented to make a strong
case for your selection.
Good: An architecture is chosen as best, and a
justification is provided that draws on significant
elements of your evaluation.
Satisfactory: An architecture is chosen as best. A basic
justification is provided, but it is not directly
connected to the elements of your evaluation.
Poor: An architecture is chosen as best, but little or
no convincing justification is provided.
No attempt: Section is missing. 0/10
Excellent: In addition to summarizing the selection of
the strongest architecture, the conclusions discuss the
implications of all important weaknesses of that choice
that were identified in the evaluation. The conclusions
point out what requirements changes would invalidate the
architecture choice that was selected and why, as well as
what alternative architecture would then be a better
match. The conclusions provide an appropriate discussion
of ways the risks of changing requirements could be
addressed through possible modifications to the selected
Good: A basic conclusion that restates the architectural
choice is provided. Some weaknesses in the selected
architecture are discussed. Potential changes in
requirements that may result in a change to which
architecture is the best fit are also discussed. Possible
modifications to the selected architecture to improve the
way it meets the problem may be included.
Satisfactory: A basic conclusion that restates the
architectural choice is provided. Some weak points in the
selected architecture are discussed.
Poor: A basic conclusion that restates the architectural
choice is provided.
No attempt: Section is missing. 0/10
Excellent: All writing is clear and readable, without
grammar errors, punctuation errors, or other writing
problems. Figures or summary tables are used
appropriately where they clarify presentation. Clear
headings and a cohesive document organization are used.
The whole document looks like a professionally prepared
Good: All writing is clear and readable, with only
occasional minor writing errors. An appropriate attempt
is made to use appropriate figures or summary tables
where they help clarify presentation. Clear headings and
a cohesive document organization are used.
Satisfactory: The document is readable, but some
significant errors appear in the writing and/or
organization. Some parts of the exposition may not
communicate clearly. Summary representations of key
portions of the work may be missing. Clear headings are
Poor: The document is readable, but includes significant
errors in both writing and organization that make
portions of the document hard to follow.
No attempt: Section is missing. 0/10
- Evaluating Alternative Designs
Dept. of Information and Computer Science
University of California, Irvine
Irvine, California 92697
neno @I ics.uci.edu
Explicit focus on architecture has shown tremendous
potential to improve the current state-of-the-art in soft-
ware development. Relatively quickly, software architec-
ture research has produced credible results. However,
some of this initial success has also resulted in unrealistic
expectations and failure to recognize the limits of this line
of research, which can result in backlash when the unre-
alistic expectations are not met. One solution is to attempt
to clearly delineate the boundaries of applicability and
effectiveness of software architectures. This paper repre-
sents a step in that direction: it dispels some common mis-
conceptions about architectures and discusses problem
areas for which architecture is well suited and those for
which it is not.’
Software architecture has become an area of intense research
in the software engineering community. A number of
architecture modeling notations and support tools, as well as
new architectural styles, have emerged. Explicit focus on
architecture has shown tremendous potential to improve the
current state-of-the-art in software development and alleviate
many of its problems. Architecture addresses an essential
difficulty in software engineering-complexity-via
abstraction and its promise of supporting reuse. At the same
time, one fact should not be overlooked: software
architecture is not a “silver bullet” , and simply
introducing it into an existing development lifecycle will not
fully exploit this potential for improvement.
In a few short years, software architecture research has
’ Effort sponsored by the Defense Advanced Research Projects
Agency, and Rome Laboratory, Air Force Materiel Command, US-
AF, under agreement number F30602-97-2-0021. The U.S. Gov-
ernment is authorized to reproduce and distribute reprints for
Governmental purposes notwithstanding any copyright annotation
thereon. The views and conclusions contained herein are those of
the authors and should not be interpreted as necessarily represent-
ing the official policies or endorsements, either expressed or im-
plied, of the Defense Advanced Research Projects Agency, Rome
Laboratory or the U.S. Government.
Permission to make digital or hard copies ofall or part ofthis work for
prrsonal or classroom use ib granted without fee provided that copies
arc not made or dlstnhuted for prolit or commercial advantage and that
copies hear this notice and the full citation on the first page. To copy
otherwise. to republish. to post on servers or to redistribute to lists,
requires prior specific permission and/or a fee.
ISAW Orlando Florida USA
Copyright ACM 1998 l-581 13-OSI-3/98/11…$5.00
Separating Fact from Fiction in Software Architecture
Richard N. Taylor
Dept. of Information and Computer Science
University of California, Irvine
Irvine, California 92697
produced credible, if not impressive, results. However, some
of this initial success has also resulted in overblown claims
and “hype,” unrealistic expectations, and occasional failure to
recognize the shortcomings and limits of this line of research.
While such an attitude may be useful in gathering the initial
momentum needed to put a young discipline on the map, it
has the potential downside of resulting in backlash when the
unrealistic expectations are not met.
We believe that, as the “first generation” of architecture
research projects has become mature enough to have its
actual impact evaluated, some backlash is indeed beginning
to emerge. The reasons are numerous. Some projects have
promised more than they have been able to deliver.
Architecture is still largely an academic notion, and little
resulting technology has been transitioned to industry. The
focus of the research itself is often misunderstood.
Architecture is sometimes incorrectly equated with
architecture description languages (ADLs) and with design.
As such misconceptions take hold, a danger emerges that
some real benefits and positive results of architecture
research will be dismissed.
It is up to software architecture researchers to alleviate this
problem. We must recognize that we have failed to exploit a
lot of the potential in those areas for which architecture is
best suited, while promising too much, too quickly in the
areas for which it is not. We must make clear that there are
certain problem areas that architecture research will not be
able to address and thus make explicit the boundaries of this
field. This paper attempts to do so. It is partly a result of our
experience with a specific project, C2 , and partly an
extrapolation of lessons learned from an extensive study of
the existing architecture work [6, 81. While this paper may
well not be the first place where some of the ideas will have
appeared, they seem to have been forgotten and we believe
that it is crucial to raise them again in the consciousness of
The remainder of the paper is organized as follows. Section 3
addresses some common misconceptions about architectures.
Sections 4 and 5, respectively, discuss the types of tasks for
which architecture is well suited and those which are largely
outside its scope. Discussion and conclusions round out the
3. COMMON MISCONCEPTIONS
Architecture is often equated with architecture description
languages (ADLs), which are only an enabler to achieve its
benefits, and not the end product. Architecture is also equated
with design; though related, their foci are quite different.
Finally, existing research has focused to a large degree on
architecture-based analysis. This section discusses why these
are incomplete, if not incorrect, notions of architecture.
3.1 Architectures and ADLs
Software architecture is a high-level view of a system that
focuses on structure, communication protocols, assignment
of software components and connectors to hardware
components, and so forth. Every software system has an
architecture, although it may not be explicitly modeled.
An ADL, on the other hand, can be viewed as a notation that
helps architects model an aspect of the architecture.
Therefore, multiple ADLs may be used to model a single
architecture. ADLs may be formal, semi-formal, or informal.
Their purpose is not the same as that of programming
languages: they are meant to provide a basis for early
analysis of a system and for its design and implementation,
but also for communication and understanding among
stakeholders in a project. ADLs may be general-purpose or
special-purpose modeling languages.
3.2 Architecture and Design
Current literature leaves the issue of the relationship between
architecture and design ambiguous, allowing for several
. architecture and design are the same;
l architecture is at a level of abstraction above design, so it
is simply another step (artifact) in a software development
l architecture is something new and is somehow different
from design (but just how remains unspecified).
The second interpretation is closest to the truth. To some
extent, architecture serves the same purpose as design.
However, its explicit focus on system structure and
interconnections distinguishes it from traditional software
design, such as object-oriented (00) design, which focuses
more on modeling lower level abstractions, such as
algorithms and data types. As a (high level) architecture is
refined, its connectors may lose prominence by becoming
distributed across the (lower level) architecture’s elements,
eventually resulting in the transformation of the architecture
into a design.
3.3 Architecture and Analysis
A large fraction of architecture research to date has focused
on the architecture as a vehicle for early system analysis.
Early understanding of the properties of an application is
indeed invaluable. On the other hand, there are several
unresolved issues associated with architecture-level analysis.
Addressing these may require a shift in the current mentality.
In order to enable formal analysis, ADLs must supply
appropriate formal constructs. Performing meaningful
analyses may require introduction of constructs into an ADL
that are perhaps premature for the current abstraction level.
This is also likely to hamper the different stakeholders’
understanding of the architecture. A more balanced approach
for evaluating an architecture, which couples informal
“inspections” of the architecture with more powerful formal
analyses may be preferable: it would result in a less formal
architectural model that would focus attention on the
“quality,” elegance, and suitability of the architecture for the
Another critical issue is that, once a property of an
architecture is established, one must also ensure that that
property will be preserved in the design and implementation.
Existing architecture research has mostly failed to address
this problem. Better and more practical refinement
techniques that would ensure consistency among the
architecture, design, and implementation are needed.
4. WHAT ARCHITECTURE IS GOOD AT
In this section we discuss the tasks that arise in the process of
software development for which architectures are well suited.
These are the strengths of architectures and should be the
primary focus of researchers and practitioners. In general,
architecture provides a solid basis for large-scale
development of distributed applications, using off-the-shelf
components and connectors, implemented in multiple
programming languages. It also has a number of additional
benefits. Several are discussed below.
Architectures are a good communication facilitator among
the different stakeholders in a project. They are at a
sufficiently high level of abstraction to enable understanding
by the non-technical stakeholders, such as managers or
customers. Architecture arises from an analysis of the
requirements and of application domain characteristics and is
the earliest model of a solution to the customer needs .
For this reason, it is important that architectural descriptions
be simple, understandable, and possibly graphical, with well
understood, but not necessarily formally defined, semantics.
Architectural models can also present multiple views of the
same architecture , e.g., a high level graphical view, a
lower level view with formal specifications of components
and connectors, a conceptual architecture, one or more
implementation architectures, the corresponding
development process, the data or control flow view, and so
on. Different stakeholders (e.g., architects, developers,
managers, customers) may require different views of the
architecture. The customer may be satisfied with a high-level,
“boxes and arrows” description, the developers may want
detailed component and connector models, while the
managers may require a view of the development process.
One of the greatest benefits of architectures is their
separation of computation from interaction in a system.
Software connectors are elevated to a first-class status, and
their benefits have been demonstrated by a number of
existing approaches [ 1, 13, 151. Explicit connectors remove
from components the responsibility of knowing how they are
interconnected and minimize component interdependencies.
Changes to a system’s functionality are thus limited to the
components. All decisions regarding communication,
mediation, and coordination in a system are isolated inside
connectors [ 121.
Architectures are an ideal platform for supporting course-
grain software evolution. This includes adding, removing,
and replacing components and connectors, while ensurin
f structural and behavioral properties of an application.
Architecture-based evolution can occur both at system
specification time  and at its execution time [lo]. Although
individual components and connectors can also evolve using
approaches such as subtyping or inheritance [5, 71,
architecture is generally not a good foundation for fine-grain
evolution that requires replacement of individual source code
Architecture affords software engineers a lot of flexibility
because of its separation from implementation. This allows
an architect to experiment with various configurations
(architectural plug-and-play) and enact “what if’ scenarios
rather inexpensively. Architectural analysis tools can then
indicate potential problems early enough in the lifecycle so as
to minimize the costs of correcting them. In order to fully
exploit this separation, a reliable mapping from the
architecture to the implementation is needed. This is related
to architectural refinement, discussed Section 3.3, and
remains an open area of research.
Finally, architecture is the appropriate level of abstraction at
which invariant properties of a problem domain or rules of a
compositional style (i.e., an architectural style) can be
exploited and should be elaborated. Doing so for domain-
specific architectures (DSSAs) results in a reference
architecture that is reused across applications in the domain.
Doing so for a style results in a set of heuristics that, if
followed, will guarantee a resulting system certain desirable
properties. DSSAs and styles also enable easier
communication among stakeholders. For example, just
saying that a system is in the “client-server” or “pipe and
filter” style implies a certain structure and a set of properties
without having to discuss its details. DSSAs and styles have
the potential to enable code generation for canonical
components and connectors, as well as the “glue” code to
compose them in a system. Substantial experience in a
particular domain and/or style can be used as a basis for
generalizing the given approach to other problem areas [ 141.
5. WHAT ARCHITECTURE IS NUT GOOD AT
The previous section has outlined a set of benefits developers
can derive from an explicit architectural focus. However, all
of a project’s needs may not be addressible by architecture.
Architecture alone certainly cannot compensate for problems
in requirements acquisition, project scheduling and budgets,
organizational issues, such as poorly trained employees,
inefficient process, and so forth. There are also some
technical areas for which architecture is not well suited.
As already discussed, architecture cannot be effectively
employed to support @e-grain evolution. Architecture
assumes that applications are constructed from building
blocks that are above the level of a source statement. If the
evolution of a given system predominantly occurs at the
source code statement level, then architecture is not the
appropriate abstraction for supporting that evolution.
In general, architecture cannot guarantee the properties of the
implemented system. Properties like reliability or
’ Batory and O’Malley quite appropriately refer to this as the
“Lego paradigm” in software development .
performance cannot be fully assessed at the architectural
level, although the characteristics of the architecture and/or
the style can provide an indication of how the system will
behave. A system’s implementation can be modified
independently of its architecture; many modifications (e.g.,
evolution at the source code level) will not have any effect on
the underlying architecture, thus the architecture will not aid
developers in preventing errors. If we disallow the direct
modification of an implementation, we essentially reduce
architecture-based software development to a variant of
transformational programming [ 111, thus inheriting all of its
problems and limitations, with the added problem of scale.
Also, unless specific techniques are developed to support
architecture-based evolution, and particularly runtime
evolution, a system may have to be entirely regenerated every
time its architecture is modified. A related issue is source
code optimization. Architecture is not the correct abstraction
level for this task; the right place to do so is code itself.
Simply employing an explicit architecture does not guarantee
a development process that will ensure the quality of the
resulting system. “Architecting” is only a step in the software
engineering lifecycle. Certainly, architecture can be a guide
for the process in that it contains a conceptual, high level
view of the system. However, enacting an effective and
efficient process to transform that architecture into a running
system then becomes the responsibility of the management
A problem that is actually exacerbated by architectures is
traceability across the different aspects (views) of a system
and different levels of abstraction. As discussed above, a
software architecture often consists of multiple views and
may be modeled at multiple levels of abstraction (Figure 1).
We call a particular view of the architecture at a given level of
abstraction (i.e., a single point in the two-dimensional space
of Figure 1) an “architectural cross-section.” It is critical for
changes in one cross-section to be correctly reflected in
others. A particular architectural cross-section can be
considered “dominant,” so that all changes to the architecture
graphical – – – – – – n
I Level of
requirements high level detailed source
architecture architecture code
Figure 1: Two-dimensional space of architectural views and levels of
abstraction. The vertical axis is a set of discrete values with a nominal
ordering. The horizontal axis is a continuum with an ordinal ordering of
values, where system requirements are considered to be the highest
level of abstraction and source code the lowest. One possible dominant
cross-section (graphical view of the high level architecture) is shown.
are made to it and then reflected in others. More frequently,
changes will be made to the most appropriate or convenient
cross-section. Traceability support will hence need to exist
across all pertinent cross-sections.
However, the relationships among architectural views are not
always well understood. For example, while it is typically
straight forward to provide support for tracing changes
between textual and graphical views, it may be less clear how
the data flow view should affect the development process
view. In other cases, changes in one view (e.g., process)
should never affect another (e.g., control flow). An even
bigger hurdle is providing traceability support across both
architectural views and levels of abstraction simultaneously.
Finally, although much research has been directed at
methodologies for making the transition from requirements
to design (e.g., 00), this process is still an art form. Further
research is especially needed to understand the effects of
changing requirements on architectures and vice versa.
Finally, we revisit an issue discussed earlier: formalism.
Formalism has been extensively adopted and employed in
architecture research to date, resulting in a number of
benefits. However, too often this has been at the expense of
understanding, communication, and cognitive support for
developers. As a bridge between requirements and design,
architecture is the artifact that will need to be understandable
to a very diverse set of stakeholders. By removing ambiguity,
formalism can aid understandability. On the other hand, some
decisions will purposely be left unspecified at the
architectural level; formalism typically does not allow for
such incompleteness. Additionally, it is still unclear how
much formalism is adequate at the level of architecture and
what types of formal methods are best suited for the needs of
Software architectures show great potential for reducing
development costs while improving the quality of the
resulting software. However, this potential cannot be fulfilled
simply by explicitly focusing on architectures, just like a new
programming language cannot by itself solve the problems of
software engineering. A programming language is only a tool
that allows (but does not force) developers to put sound
software engineering principles and techniques into practice.
Similarly, one can think of software architectures as tools that
also must be supported with specific techniques to achieve
desired properties. As is typically the case with tools,
software architectures are much better suited to solving some
types of problems than others. Understanding the types of
problems to which architectures can be applied most
effectively will help practitioners maximize the benefits of an
architecture-centered approach to software engineering.
[I] R. Allen and D. Garlan. A Formal Basis for Architectural
Connection. ACM Transactions on Sofhvare Engineering
and Methodology, July 1997.
 D. Batory and S. O’Malley. The Design and
Implementation of Hierarchical Software Systems with
Reusable Components. ACM Transactions on Software
Engineering and Methodology, October 1992.
 F. P. Brooks, Jr. Essence and Accidents of Software
Engineering. IEEE Computer, April 1987.
 P. B. Kruchten. The 4+1 View Model of Architecture.
IEEE Software, November 1995.
 N. Medvidovic, P. Oreizy, J. E. Robbins, and R. N.
Taylor. Using Object-Oriented Typing to Support
Architectural Design in the C2 Style. In Proceedings of
the Fourth Symposium on the Foundations of Software
Engineering, San Francisco, CA, October 1996.
 N. Medvidovic and D. S. Rosenblum. Domains of
Concern in Software Architectures and Architecture
Description Languages. In Proceedings of the USENZX
Conference on Domain-Specific Languages, Santa
Barbara, CA, October 1997.
 N. Medvidovic, D. S. Rosenblum, and R. N. Taylor. A
Type Theory for Software Architectures. Technical
Report, UCI-ICS-98-14, Department of Information and
Computer Science, University of California, Irvine, April
 N. Medvidovic and R. N. Taylor. A Framework for
Classifying and Comparing Architecture Description
Languages. In Proceedings of the Sixth European
Software Engineering Conference together with the Fifth
ACM SIGSOFT Symposium on the Foundations of
Software Engineering, Zurich, Switzerland, September
 N. Medvidovic, R. N. Taylor, and D. S. Rosenblum. An
Architecture-Based Approach to Software Evolution. In
Proceedings of the International Workshop on the
Principles of Sofiware Evolution, Kyoto, Japan, April
[lo] F? Oreizy, N. Medvidovic, and R. N. Taylor. Architecture-
Based Runtime Software Evolution. In Proceedings of
the 20th International Conference on Software
Engineeting (ICSE’98), Kyoto, Japan, April 1998.
[ 111 H. Partsch and R. Steinbruggen. Program Transformation
Systems. ACM Computing Surveys, September 1983.
[ 121 D. E. Perry. Software Architecture and its Relevance to
Software Engineering. Coord’97, September 1997.
[ 131 M. Shaw, R. DeLine, D. V. Klein, T. L. Ross, D. M.
Young, and G. Zelesnik. Abstractions for Software
Architecture and Tools to Support Them. ZEEE
Transactions on Software Engineering, April 1995.
[ 141 R. N. Taylor. Generalization from domain experience:
The superior paradigm for software architecture
research? In Proceedings of the Second Internutional
Software Architecture Workshop (ISAW-2), San
Francisco, CA, October 1996.
[ 151 R. N. Taylor, N. Medvidovic, K. M. Anderson, E. J.
Whitehead, Jr., J. E. Robbins, K. A. Nies, I’, Oreizy, and
D. L. Dubrow. A Component- and Message-Based
Architectural Style for GUI Software. IEEE Transactions
on Software Engineering, June 1996.
[ 161 W. Tracz. DSSA (Domain-Specific Software
Architecture) Pedagogical Example. ACM SIGSOFT
Software Engineering Notes, July 1995.