from the editor Editor in Chief: Steve McConnell
■
Construx Software
■
[email protected]
Real Quality For Real Engineers Steve McConnell
F
or decades, experts have struggled to define quality. Edwards Deming said that the only definition of quality that mattered was the consumer’s.1 Joseph Juran said that quality was fitness for use.2 Philip Crosby provided the strictest definition of quality as “conformance to requirements.”3 Conformance to requirements Although they differ on the details, quality experts agree that the customer’s view of requirements is critically important. For that reason, I’ve found Crosby’s definition of “conformance to requirements” to be the most useful definition in examining software quality. Taking into account many software projects’ tendency to elicit some but not all of the customer’s complete requirements, “requirements” cannot be interpreted solely as the written requirements. Requirements must also include implicit requirements—those that the customer assumes regardless of whether the development team happens to write them down. Thus, the working definition of quality that I use is “conformance to requirements, both stated and implied.” The “ities” of software quality In addition to specific functional requirements, software quality is also affected by common nonfunctional characteristics that are often referred to as the “ities.” The ities that affect software’s internal quality (quality Copyright © 2002 Steven C. McConnell. All Rights Reserved.
visible to the software’s developers) include maintainability, flexibility, portability, reusability, readability, scalability, testability, and understandability. The ities that affect the software’s external quality (visible to the customer) include usability, reliability, adaptability, and integrity, as well as correctness, accuracy, efficiency, and robustness.4 Some of these characteristics overlap, but all have different shades of meaning that are desired more in some cases and less in others. The attempt to maximize certain characteristics invariably conflicts with the attempt to maximize others. Figure 1 presents a summary of the ways in which external quality characteristics affect each other. These characteristics will be prioritized differently on different projects, which means the software quality target is always changing. Finding an optimal solution from a set of competing, changing objectives is challenging, but that’s part of what makes software development a true engineering discipline. From product quality to project quality When software people refer to quality, we usually refer to the quality of the software product we are producing. From a management perspective, however, customers also have requirements for projects. I think it’s reasonable to draw an analogy from products to projects, conceiving project quality as conformance to requirements, both stated and implied. Customers’ functional requirements for projects draw from a small number of possible attributes, namely schedule, resources, cost, and quality of the product March/April 2002
IEEE SOFTWARE
5
Correctness
Construction: Andy Hunt and Dave Thomas, Pragmatic Programmers, {Andy, Dave}@pragmaticprogrammer.com
Design: Martin Fowler, ThoughtWorks,
[email protected] Loyal Opposition: Robert Glass, Computing Trends,
[email protected]
Efficiency
⇓
Reliabilty
⇑
Senior Lead Editor Dale C. Strok
[email protected]
Pauline Hosillos Art Director Toni Van Buskirk Cover Illustration Dirk Hagner
Advertising Assistant Debbie Sims
■
■
■
■
CONTRIBUTING EDITORS
Greg Goth, Denise Hurst, Gil Shif, Keri Schreiner, and Margaret Weatherford
■ Editorial: All submissions are subject to editing for clarity, style, and space. Unless otherwise stated, bylined articles and departments, as well as product and service descriptions, reflect the author’s or firm’s opinion. Inclusion in IEEE Software does not necessarily constitute endorsement by the IEEE or the IEEE Computer Society. To Submit: Send 2 electronic versions (1 word-processed and 1 postscript or PDF) of articles to Magazine Assistant, IEEE Software, 10662 Los Vaqueros Circle, PO Box 3014, Los Alamitos, CA 90720-1314;
[email protected]. Articles must be original and not exceed 5,400 words including figures and tables, which count for 200 words each.
6
IEEE SOFTWARE
March/April 2002
⇑
⇑
⇑
⇑ ⇓
⇑
Robustness
⇓
⇑
⇓
⇑
⇓
⇓
produced. In some cases, a customer might prioritize cost higher—in others, schedule or product quality. Additionally, project quality includes nonfunctional requirements such as
Publisher Angela Burgess
Membership/Circulation Marketing Manager Georgann Carter
⇓
⇓
⇓
⇑
⇑
⇓
⇓
⇓
⇑
⇓
⇑
⇑
⇓
⇑
⇓
⇑
⇓
⇑
⇓ Hurts
Figure 1. Interactions between product quality external characteristics.
Executive Director David Hennage
Assistant Publisher Dick Price
⇑
⇓
⇑ Helps
Technical Illustrator Alex Torres Production Artist Carmen Flores-Garvey
⇑ ⇓
Accuracy STAFF
Magazine Assistants Dawn Craig
[email protected]
⇑
Adaptability
Quality Time: Jeffrey Voas, Cigital,
[email protected]
Staff Editors Shani Murray, Scott L. Andresen, and Kathy Clark-Fisher
⇑
Integrity
Manager: Don Reifer, Reifer Consultants,
[email protected]
Associate Editors Jenny Ferrero and Dennis Taylor
⇑ ⇑
Usability
Country Report: Deependra Moitra, Lucent Technologies
[email protected]
Group Managing Editor Crystal Chweh
⇑
Ro bu stn es s
Bookshelf: Warren Keuffel,
[email protected]
Ad ap ta bi lit y Ac cu ra cy
D E PA R T M E N T E D I T O R S
Ef fic ie nc y Re lia bi lty In te gr ity
How focusing on the factor below affects the factor to the right
Co rre ctn es s Us ab ili ty
FROM THE EDITOR
■
Efficiency: Minimal use of schedule, budget, and staff to deliver a particular software product. Flexibility: The extent to which the project can be modified to deliver software other than that for which the project was originally intended or to respond to changes in project goals. Improvability: The degree to which project experiences can be fed back into the project to improve project performance. Predictability: The degree to which a project’s cost, schedule, and product quality outcomes can be forecast in advance. Repeatability: The degree to which the project after the current project can be conducted using practices similar to those used on the current project. Robustness: The degree to which the project will continue to function in the presence of stressful environmental conditions.
■
■
Sustainability: The duration for which a project can continue using its current practices. Visibility: The ability of a customer to accurately determine project status and progress.
These project characteristics interplay with each other just as the software quality attributes do. Figure 2 shows the interactions. In addition to the interactions shown in Figure 2, some of these project quality characteristics tend to support or undermine the various product characteristics summarized in Figure 1. Different projects have different priorities among efficiency, flexibility, improvability, and the other characteristics shown in Figure 2. An established business might place high values on efficiency, predictability, improvability, and repeatability. A start-up company might place a higher value on robustness and visibility; it might not value sustainability and repeatability at all. This suggests that there isn’t one best definition of project quality for all projects; the best definition depends on the project’s consumers and those consumers’ specific project requirements.
FROM THE EDITOR
Efficiency
Ef fic ie nc y Fl ex ib ili ty Im pr ov ab ili ty Pr ed ict ab ili ty Re pe at ab ili ty Ro bu stn es s Su sta in ab ili ty Vi sib ili ty
How focusing on the factor below affects the factor to the right
⇑
⇓
Flexibility
⇑
Improvability
⇑
Predictability
⇑
⇓
Repeatability
⇓
⇓
Robustness
⇓
⇑
⇓ ⇓ ⇑
⇑
⇑
⇑
⇑
⇑
⇑ Helps
⇑
⇓ ⇑
⇓ ⇑ ⇑
⇑
⇑
⇑
⇑
⇓
Sustainability Visibility
EDITOR IN CHIEF: Steve McConnell 10662 Los Vaqueros Circle Los Alamitos, CA 90720-1314
[email protected]
⇑
⇑
⇑
⇑
⇑ ⇑
⇑ ⇑ ⇑
⇑
⇓ Hurts
Figure 2. Interactions between project quality characteristics.
Real engineering One difference between a craftsman and an engineer is that a craftsman defines quality on his own terms, whereas an engineer defines quality through his customers’ eyes. The craftsman settles into a way of working that suits him personally, while the engineer adapts his approach on each project to best satisfy his customer’s requirements. Software engineering purists argue that software should always be produced to the highest level of quality, by which they mean the highest levels of product quality. End-user requirements certainly should be considered, but the organization that builds and sells the software is another consumer whose requirements must be taken into account. The product characteristics that constitute quality to the end user do not necessarily satisfy the software-developing organization’s project quality requirements. As Deming pointed out in Out of the Crisis, different consumers can have different definitions of quality for the same product, and this applies as much to project quality as it does to product quality. The project
A S S O C I AT E E D I T O R S I N C H I E F
Design: Maarten Boasson, Quaerendo Invenietis
[email protected] Construction: Terry Bollinger, Mitre Corp.
[email protected] Requirements: Christof Ebert, Alcatel Telecom
[email protected] Management: Ann Miller, University of Missouri, Rolla
[email protected] Quality: Jeffrey Voas, Cigital
[email protected] Experience Reports: Wolfgang Strigel, Software Productivity Center;
[email protected] EDITORIAL BOARD
⇑ ⇑
EDITOR IN CHIEF EMERITUS: Alan M. Davis, Omni-Vista
team, manager, and sponsoring organization can all be considered consumers of a project. A manager might consider a project to have high quality if it provides good visibility, robustness, and repeatability. The project team might value efficiency, improvability, and sustainability. The sponsoring organization might value predictability and flexibility. A manager who factors product quality into the project plans but ignores project goals takes an abridged view of software quality. One hallmark of engineering work is the constant balancing of trade-offs. With the extensive trade-off decisions required to balance both software product attributes and software project goals, software personnel have abundant opportunities to hone their engineering skills in this area.
Don Bagert, Texas Tech University Richard Fairley, Oregon Graduate Institute Martin Fowler, ThoughtWorks Robert Glass, Computing Trends Andy Hunt, Pragmatic Programmers Warren Keuffel, independent consultant Brian Lawrence, Coyote Valley Software Karen Mackey, Cisco Systems Deependra Moitra, Lucent Technologies, India Don Reifer, Reifer Consultants Suzanne Robertson, Atlantic Systems Guild Dave Thomas, Pragmatic Programmers INDUSTRY ADVISORY BOARD
Robert Cochran, Catalyst Software (chair) Annie Kuntzmann-Combelles, Q-Labs Enrique Draier, PSINet Eric Horvitz, Microsoft Research David Hsiao, Cisco Systems Takaya Ishida, Mitsubishi Electric Corp. Dehua Ju, ASTI Shanghai Donna Kasperson, Science Applications International Pavle Knaflic, Hermes SoftLab Günter Koch, Austrian Research Centers Wojtek Kozaczynski, Rational Software Corp. Tomoo Matsubara, Matsubara Consulting Masao Matsumoto, Univ. of Tsukuba Dorothy McKinney, Lockheed Martin Space Systems Nancy Mead, Software Engineering Institute Stephen Mellor, Project Technology Susan Mickel, AgileTV Dave Moore, Vulcan Northwest Melissa Murphy, Sandia National Laboratories Kiyoh Nakamura, Fujitsu Grant Rule, Software Measurement Services Girish Seshagiri, Advanced Information Services Chandra Shekaran, Microsoft Martyn Thomas, Praxis Rob Thomsett, The Thomsett Company John Vu, The Boeing Company Simon Wright, Integrated Chipware Tsuneo Yamaura, Hitachi Software Engineering M A G A Z I N E O P E R AT I O N S C O M M I T T E E
References 1. W. Edwards Deming, Out of the Crisis, MIT Press, Cambridge, Mass., 2000. 2. J.M. Juran, Juran’s Quality Handbook, McGraw-Hill, New York, 1998. 3. P.B. Crosby, Quality Is Free: The Art of Making Quality Certain, Mentor Books, Denver Colo., 1992. 4. S. McConnell, Code Complete, Microsoft Press, Redmond, Wash., 1993.
George Cybenko (chair), James H. Aylor, Thomas J. Bergin, Frank Ferrante, Forouzan Golshani, Rajesh Gupta, Steve McConnell, Ken Sakamura, M. Satyanarayanan, Nigel Shadbolt, Munindar P. Singh, Francis Sullivan, James J. Thomas P U B L I C AT I O N S B O A R D
Rangachar Kasturi (chair), Mark Christensen, George Cybenko, Gabriella Sannitti di Baja, Lee Giles, Thomas Keefe, Dick Kemmerer, Anand Tripathi
March/April 2002
IEEE SOFTWARE
7
manager Editor: Donald J. Reifer
■
Reifer Consultants
■
[email protected]
Ten Deadly Risks in Internet and Intranet Software Development Donald Reifer
W
hen I think of Internet development, I picture a handful of developers working frantically against the clock amid the litter of takeout food containers to churn out code for the Web. As you might expect, these developers tend to be recent graduates who use the latest technology to turn out applications that live for weeks but not years. Not that this is bad. Some of the best work I have seen in years has come about this way. The reason is simple. With their “We can do anything” attitude, these teams are willing to work on technology’s proverbial bleeding edge. They know what they produce is going to change, so they aren’t afraid to try something new. That’s because they know they can fix problems as they occur without suffering the severe penalties they might have faced in earlier development environments. This article contrasts the current Internet and intranet development climate with earlier releases (see Table 1) and identifies the 10 greatest risks facing today’s Young Internet/intranet Lions. Where’s the action on today’s Internet? With the world economy’s recent slowdown, you might easily think that the move to the Net has also slackened. Don’t believe it. As Table 2 shows, current trends in Internet and intranet project work indicate oth12
IEEE SOFTWARE
March/April 2002
erwise. As the economy fizzled last year, large firms stopped expanding, instead consolidating and moving to the Web to cut costs. Yes, lots of startups bit the dust, but in many cases Big Business accelerated its move to the Web. Firms such as GE have reportedly saved millions by pursuing such strategies (James P. Pelz, “GE Takes to the Net to Lower Company Costs,” Los Angeles Times, 9 Oct. 2000, pp. C1–5). As an example of the impact, a large financial client of mine currently has over 2,000 programmers involved in Web development. Five years ago, its 3,000-member software workforce worked solely on client-server applications. Traditional versus Internet and intranet development risks Based on this background information, you might well ask, “As firms large and small move applications across the enterprise to the Web, what risks do they face? How do these compare with risks that they’ve previously encountered?” Table 3 answers these questions. In the first two columns, I’ve listed the risks in terms of their cost and schedule impacts for both traditional as well as Internet/intranet developments. You’ll see why I consider them deadly. The noticeable differences in risks stem from the project characteristics (large versus small teams, technology-driven versus requirements-driven, and so forth) and who’s moving to the Net (large firms instead of small startups, for example). While many of the risks are familiar to traditional projects, the impact of unrealistic expectations 0740-7459/02/$17.00 © 2002 IEEE
MANAGER
and technology volatility should strike you as new and important. Don’t get confused by Table 3. The mitigation strategies listed are aimed primarily at Internet/intranet developments. How do I mitigate these risks? Just identifying the risks is easy. Knowing how to reduce their deadly consequences is much harder. The third column of Table 3 lists my recommendations for dealing with primarily the Internet/intranet risks. Most of these mitigation strategies aren’t new, but some are innovative, I believe. That’s because, until recently, issues identified in Internet and intranet projects hadn’t surfaced or were deemed less important. For example, you need to specifically align Web development with your business goals. Else, why build them? As another example, concepts such as nightly builds, weekly releases to the field, and having the customer function as a member of the programming team are novel. If you’re interested in risk management, you’ll find many publications on the topic, many based on experience rather than theory. But, be warned. In most cases, you will have to extend this experience into the Internet/intranet world as you try to address the peculiarities of Web projects. That’s not so bad when you think about it. It’s easier to extend existing knowledge than to plow new ground.
T
he list of 10 deadly Internet and intranet risks won’t surprise many. But based on them, software engineers need to strike a balance between the technologies that many in the Internet world depend
RAPID
A game-changing, nonproprietary rapid and agile development project model for building business solutions of any size in short timeframes. It shortens the clock-speed (and time to market) for delivery of core business benefits. It is the only approach that can guarantee delivery on an exact day under tight, Internet-time deadlines.
WHAT IS DSDM?
DSDM (Dynamic Systems Development Method) is a framework that encompasses all aspects needed for successful delivery: people, process and technology - with emphasis on people; most projects fail as a result of people-based problems. DSDM is primarily nonprescriptive so it is synergistic with many AGILE Created using software development agile techniques and now methods such as XP, in version 4.0, it continues Scrum, Adaptive, Crystal, to be evolved using DSDM, DSDM etc. The framework the only Agile Method created provides underlying using an Agile Method. principles, processes, project NON-PROPRIETARY lifecycle, artifacts, key roles and The non-proprietary DSDM method responsibilities, and guidance on evolved collaboratively over the last management techniques and 6 years by an International nonprofit development techniques. It's a consortium. It's tool and technology tool to effectively: independent. No tools to purchase * Understand, or be hamstrung by. * Plan, * Communicate, BUSINESS CENTERED Built in * Control, and partnership with big business for * Deliver all projects. business. DSDM will scale from five to hundreds of developers. As the most business benefit focused LEARN MORE visit the DSDM method in the world, many of the Consortium web site at world's largest companies and http://www.dsdm.org or contact numerous government bodies have DSDM directly at established DSDM as their standard. 800-610-6776. DSDM is ISO 9000 certified.
Table 1 Traditional versus Internet/intranet development Characteristic
Traditional approaches
Internet/intranet development
Primary goal Typical project size Typical timeline Development approach employed
Build quality software products at minimum cost Medium to large (20 or more team members) 10 to 18 months Classical requirements-based paradigms; done phased or incrementally; document-driven with focus on getting the design right before coding Traditional methods; object-oriented approaches, procedural languages (C, C++); limited, multimedia, CASE tools; use cases; lots of modeling; and so on Software Capability Maturity Model, SPICE, ISO-based Code-based systems; mostly new; some reuse; lots of legacy involved; many external interfaces; lots of interactions; often complex applications Professional software engineers typically with 5+ years of experience in at least two pertinent application domains
Bring quality products to market as quickly as possible Small (3 to 7 team members) Several weeks to 6 months Rapid application or spiral development (Rational Unified Model, MBase, and so on)
Primary technologies used
Processes employed Products developed
People involved
Agile methods; component-based approaches; multimedia; new language environments (Java, XML, HTML, and so forth); visualization; and so on Generally ad hoc, pairwise programming; refactoring Object-based systems; many reusable building blocks; few external interfaces; little interaction; simple applications Less experienced programmers; users as developers; graphic designers; new hires right out of school
March/April 2002
IEEE SOFTWARE
13
MANAGER
upon, on the one hand, and the tried and true processes that promote risk management and help us achieve business goals, on the other. The “just do it” attitude of the past few years has done as much harm as good to the software engineering profession. While a few good
practices have emerged for Internet development, many time-tested software engineering practices were jettisoned based on the faulty belief that teams don’t have time to put these practices to work. By looking at the list of deadly risks, we can recognize our mistake. My message is that
“Good software engineering pays off on Internet applications, too.” Try them, they’ll make your job easier.
Don Reifer is president of Reifer Consultants and visiting associate of the Center for Software Engineering at the University of Southern California. Contact him at
[email protected].
Table 2 Who’s Developing Internet/intranet Applications? Time Period
Who’s playing?
What’s their primary interest?
1980s Early 1990s Late 1990s Now
Researchers Internet service providers Internet startups Large firms as well as yet more startups
Technical issues (protocols, for example) Meeting the demand for performance (many subscribers) Content is king as everyone moves to the Net Save money, collaborate, and build an e-business infrastructure
Table 3 Traditional and Internet and intranet project risks and mitigation strategies Traditional risks
Ten deadly Internet and intranet project risks
Internet/intranet Risk mitigation strategies
Personnel shortfalls
Personnel shortfalls
Unrealistic budgets and schedules
Misalignment with business goals
Volatile requirements
Unrealistic customer and schedule expectations
Shortfalls in externally furnished components
Volatile technology, changing so rapidly that it is hard to keep up (SML, .NET, Persistence, J2EE, and so forth) Unstable software releases (although the software works, it performs poorly and crashes frequently)
Bring on a skilled core team. Have the team mentor new people. Make training and teamwork part of the culture. Hire top-notch personnel while the job market remains soft. Align developments with business goals and highlight importance of development. Make the customer part of the team. Set schedule goals around frequent deliveries of varying functionality. Introduce new technology slowly, according to a plan. Use technology because it supports business goals, not because it is the latest and greatest thing to do. Stabilize requirements and designs as much as practical. Plan to refactor releases from the start. Don’t deliver applications when quality is poor and system crashes (say “no”). Manage functionality using releases. Deliver working prototypes before you target new functionality. Introduce new methods and tools slowly, as justified by business cases, not merely because they are new and appealing. Make sure methods and tools are of production quality. Set clear expectations and measures of success. Make effort a learning exercise (to make staff feel they are learning, growing, and gaining valuable experience). Staff the team carefully with compatible workforce. Build team and provide it with leadership. Manage conflicts to ease friction. Acquire dedicated workspace for the team. Appropriate collaboration tools. Lots of space available for meetings and pizza.
Gold-plating
Released software has poor quality New methods and unstable tools (primarily causing schedule delays)
Constant changes in software functionality (shooting at a moving target) Even newer methods and more unstable tools (causes heartburn, headaches, and schedule delays)
High turnover (especially of skilled personnel)
High turnover (especially of those personnel skilled in the new technology)
Friction between developers and customers
Friction within the team (lack of leadership, overwork, unrealistic expectations, and so forth)
Unproductive office space
Unproductive office space
14
IEEE SOFTWARE
March/April 2002
requirements Editor: Suzanne Robertson
■
The Atlantic Systems Guild
■
[email protected]
Managing Requirements for Business Value John Favaro Requirements engineers are often asked the question, “What will the investment in requirements give to our business?” John Favaro uses his experience in financial engineering to help us answer this question by providing some tools for quantifying the value of requirements. —Suzanne Robertson
M
anaging requirements for business value? What’s there to manage? Aren’t they just...there?” Such perplexity was perfectly understandable in the good old days, when all you had to do was get the requirements from your customer, design your system, and produce one of those awful conformance matrices that demonstrated that you had implemented each and every one of the requirements in your system. Against this dreary backdrop of conformance matrices and the like, it is indeed hard to imagine a role for requirements in the development and execution of business strategy. But, as the song says, the times, they are a changin’. We are now looking outside the traditional boundaries and developing ways of getting maximum business value from our investment in requirements. This column discusses three ways of realizing this value: managed requirements process, requirements agility, and contractual innovation. Toward requirements reuse The first important change in recent years has been the emergence of a true, actively managed requirements process, which replaces the passive approach of the past 0740-7459/02/$17.00 © 2002 IEEE
where requirements simply arrived, if you were lucky, on a one-way street from the customer. An analyst who masters the requirements process can become an active participant in the strategic conception of a system or product. He or she can elicit and formulate requirements in such a way that the path from requirement to implemented system feature is illuminated in all of its consequences, both technical and economic. Of course, we already know that a robust requirements process is a key factor in resolving problems early in the life cycle, with all the familiar economic benefits. But an actively managed requirements process gives you much more than that. Requirements analysts and customers alike can discover a new flexibility. Although there are always a few nonnegotiable requirements (such as no loss of human life), the vast majority are suitable for examination, reformulation, negotiation, or adaptation. As unexpected consequences are uncovered (for example, the projected high cost of implementation), the analyst can cooperate with the customer to search for other, equally satisfactory requirements formulations. The economic tools of cost–benefit analysis for this process have been well understood for years. A full cost–benefit analysis of a requirement (or group of requirements) needs an investment in time and resources. Furthermore, assessing the cost–benefit of requireMarch/April 2002
IEEE SOFTWARE
15
REQUIREMENTS DEPT TITLE
ments is more difficult than design or implementation, because requirements are the furthest upstream in the development process. Consequently there are more unknown factors; it can take a full development cycle before the complete economic impact of a requirement is known. Perhaps you can now understand why requirements researchers are studying techniques for creating reusable requirements.1 The first time you conceive a system based on a requirement, estimating the costs and benefits might be difficult. But, after carrying through the system to implementation, you will have a much better idea of the costs and benefits triggered by that requirement. A wellformulated, measurable, reusable requirement—including a full cost– benefit analysis as part of its description—is every bit as valuable as a reusable software module.
system. Requirements that once were vague become crystal clear as uncertainty is resolved; a requirement once thought to be rigid could be negotiated and reformulated to permit several alternative features that could satisfy it. Such changing conditions provide opportunities for the strategist to increase the value of his process. However, the traditional tools of cost–benefit analysis that apply so well to the noniterative requirements process have proven less adequate to help the requirements analyst examine the economic value of his or her newfound strategic flexibility—and this brings me to the third important change in recent years. I’d like to draw now on my interaction with Kent Beck over the past few years to discuss some cuttingedge ideas about the relationship between strategy and finance and their effect on requirements management.
Agile requirements The second important change has been the emergence of strategies for confronting the bête noire of the requirements process: vague and changing requirements. These strategies are best reflected in a new generation of software life cycles known as agile processes (see www.agilealliance.org). Some prominent examples of agile processes include Alistair Cockburn’s Crystal Methods, Bob Charette’s Lean Development, and Jim Highsmith’s Adaptive Software Development. The common denominator of these agile processes is the iterative development paradigm, which breaks up the tradition of upfront requirements elicitation and analysis. No longer do you fire and forget requirements and then move on to the next phase. Requirements may be introduced, modified, or removed in successive iterations. As Kent Beck (chief evangelist of Extreme Programming, the most visible of the agile processes) exhorts us, requirements management should “embrace change.”2 Business conditions change, giving rise to new requirements; requirements thought to be critical turn out not to be as the customer sees the first versions of the
Contractual innovation Evaluating the financial impact of strategic decisions has been the subject of great debate since the dawn of economics as a discipline. In a list of the Top 10 Unsolved Problems in Corporate Finance first compiled by the legendary financial authors Richard Brealey and Stewart Myers in 1981 and unchanged in last year’s sixth edition of their classic textbook Principles of Corporate Finance, the financial impact was ranked Number 1.3 In recent years, people have placed hope in a new branch of financial theory known as contingent claims analysis— or more popularly, real options— made possible by the breakthroughs in the 1970s in option pricing theory. In this approach, the opportunities created by strategic flexibility are evaluated with the financial tools of option pricing theory. Let’s take an example from XP. Suppose the customer requires your application to provide access to an enterprise-wide knowledge management system that he or she is contemplating introducing in a few months. A simple cost–benefit analysis on the system features that would satisfy this requirement is positive,
16
IEEE SOFTWARE
March/April 2002
say, $10 implementation cost versus $15 in benefits. But, an enormously uncertain environment undermines the cost–benefit analysis. The customer admits that the uncertainty (“volatility”) of his estimate is as much as 100 percent. If the knowledge management system is never introduced, then it will have been a waste of time to provide the access capability. However, if it is introduced, the access capability could become far more valuable than originally envisioned. The customer says that the uncertainty will be resolved in a year. The XP process permits the strategy of waiting until a future iteration to take up the requirement. Is there any way to calculate the economics of this alternative strategy? The tools of option pricing theory can in fact calculate the value of waiting to be slightly less than $8—more than the $5 of benefit accrued by immediate development.4 The option to delay implementing a requirement is an example of the way that contingent claims analysis is making a profound impact on the requirements process in the form of contractual innovation, a result of the new discipline of financial engineering born with the advent of option pricing theory. Beck likes to say that unlike fixed-scope traditional contracts, XP contracts have optional scope: every iteration provides a formal decision point in which the customer can change direction, abandoning requirements, introducing new requirements, or selecting between alternative requirements. For example, you sign such contracts not for a fixed set of functionality, but for a team’s best effort for a fixed period at a fixed price. The precise scope to be implemented will be negotiated periodically over the life of the contract, much as professional services contracts are run today. Changes in the team’s actual velocity and the relative estimates attached to each feature are factored into these scope negotiations, as are changes in the perceived relative value of these features. Option pricing theory yields some surprising insights to the economic
REQUIREMENTS DEPT TITLE
value of such contracts. For instance, there is an exotic type of option known as a best-of or rainbow option. The owner possesses two options, of which only one can be exercised. The rainbow option has the most value when the alternatives are negatively correlated—that is, when the same conditions that increase one alternative’s value will decrease the other’s. This implies that a contract’s value is enhanced by contradictory requirements. For example, two requirements each specifying the application to run on a different platform is a rainbow option. If the choice can be delayed to some later time, it adds value for the customer, letting him hedge the ultimate platform choice in the contract. Similarly, a requirement whose utility is uncertain (estimated cost and value are close) gains value by inclusion in an optional scope clause, because the option to delay implementation has demonstrable economic value. Where contractual flexibility exists to select among alternative requirements, add requirements, or even to abandon requirements, economic value is added.
Adding value What does all this mean for you as a requirements analyst? As the requirements process evolves to embrace increased strategic flexibility, and the new financial tools of contingent claims analysis mature, requirements become an important currency in system characteristic negotiation. By learning to create reusable requirements with a companion cost–benefit analysis, you bring valuable material to the table from the very beginning. By studying the new generation of agile development processes, you become fluent in the strategic possibilities to add value to the requirements process over the entire product life cycle. By learning something about the new tools of financial analysis I’ve introduced, you can better understand how strategic flexibility in the requirements process adds value. For that is what the requirements process should be about. If you remember nothing else from this column, remember this—stamp it on your forehead if necessary: the purpose of the requirements process should not be to “cover all eventualities,” or to “limit the dam-
age,” or to “minimize risk,” or even to “satisfy the customer.” The purpose of the requirements process is to add business value. It is a subtle shift in perspective for the requirements analyst, but it makes all the difference because it puts you in the position of managing requirements to make the most of your strategic opportunities.
References 1. S. Robertson and J. Robertson, “Chapter 12: Reusing Requirements,” Mastering the Requirements Process, Addison-Wesley, Reading, Mass., 1999, pp. 218–234. 2. K. Beck, Extreme Programming Explained: Embrace Change, Addison-Wesley, Reading, Mass., 1999. 3. R. Brealey and S.C. Myers, Principles of Corporate Finance, McGraw-Hill, New York, 2000. 4. M. Amram and N. Kulatilaka, Real Options: Managing Strategic Investment in an Uncertain World, Harvard Business School Press, Cambridge, Mass., 1999.
John Favaro is an independent consultant based in Pisa, Italy. He is European co-chair of the IEEE Technical Subcommittee on Software Reuse and a founding member of the steering committee of the Society for Software Education. He has degrees in computer science from Berkeley and Yale. Contact him at Via Gamerra 21, 56123 Pisa, Italy;
[email protected].
Get CSDP Certified Announcing IEEE Computer Society's new
Certified Software Development Professional Program Doing Software Right •
Demonstrate your level of ability in relation to your peers
•
Measure your professional knowledge and competence
The CSDP Program differentiates between you and others in a field that has every kind of credential, but only one that was developed by, for, and with software engineering professionals.
"The exam is valuable to me for two reasons: One, it validates my knowledge in various areas of expertise within the software field, without regard to specific knowledge of tools or commercial products... Two, my participation, along with others, in the exam and in continuing education sends a message that software development is a professional pursuit requiring advanced education and/or experience, and all the other requirements the IEEE Computer Society has established. I also believe in living by the Software Engineering code of ethics endorsed by the Computer Society. All of this will help to improve the overall quality of the products and services we provide to our customers..." — Karen Thurston, Base Two Solutions
Register Today Visit the CSDP web site at http://computer.org/certification or contact
[email protected]
design Editor: Martin Fowler
■
T h o u g h t Wo r k s
■
[email protected]
Public versus Published Interfaces Martin Fowler
O
ne of the growing trends in software design is separating interface from implementation. The principle is about separating modules into public and private parts so that you can change the private part without coordinating with other modules. However, there is a further distinction—the one between public and published interfaces. This distinction is important because it affects how you work with the interface. Public versus published Let’s assume I’m writing an application in a modern modular language—to make things more concrete, let’s assume this language is Java. My application thus consists of several classes (and interfaces), each of which has a public interface. This public interface of a class defines a group of methods that any other class in the system can invoke. While I’m enhancing a public method, I realize that one of its parameters is redundant— I don’t need to use it in the method (maybe I can get that value through some other route or maybe I just don’t need it anymore). At this point, I can eliminate that value from the method signature, clarifying the method and potentially saving work for its callers. Because this method is public, any class in the system can call it. Should I remove the parameter? In this case, I would argue yes, because there are benefits and it isn’t difficult. Al18
IEEE SOFTWARE
March/April 2002
though the method might be used anywhere, I can easily find the users with a search tool. If I have one of the new breeds of refactoring tools (see www.refactoring.com for details) available for Java, I can do it with a simple menu click—the tool will then automatically update all the callers. So, changing a public method isn’t a big deal. However, things rapidly change if I put that software out on the Web as a component, and other people, whom I don’t know, start building applications on top of it. If I now delete the parameter, everybody else’s code will break when I upgrade. Now I must do something a little more elaborate. I can produce the new method with one less parameter but keep the old method—probably recoding the old method to call the new one. I mark the old method as deprecated, assuming people will move the code over and that I can change it in the next release or two. The two cases are quite different, yet there’s nothing in the Java language to tell the difference—a gap that’s also present in a few other languages. Yet there’s something to be said for the public–published distinction being more important than the more common public–private distinction. The key difference is being able to find and change the code that uses an interface. For a published interface, this isn’t possible, so you need a more elaborate interface update process. Interface users are either callers or are classes that subclass or implement an interface. 0740-7459/02/$17.00 © 2002 IEEE
DESIGN
Advice on publishing Recognizing the difference between public and published leads to an important set of consequences. Don’t treat interfaces as published unless they are If you need to change an interface and can find and change all users, then don’t bother going through all the forwarding and deprecation gambits. Just make the change and update the users. Don’t publish interfaces inside a team I once suggested to somebody that we change a public method, and he objected because of the problems caused by its being published. The real problem was that although there were only three people on the team, each developer treated his interfaces as published to the other two. This is because the team used a strong form of code ownership in which each module was assigned to a single programmer and only that programmer could change the module’s code. I’m sympathetic to code ownership—it encourages people to monitor their code’s quality—but a strong code ownership model such as this one causes problems by forcing you to treat interperson interfaces as published. I encourage a weaker ownership model in which one person is responsible for the module but other people can make changes when necessary. This lets other developers do things such as alter calls to changed methods. (You can also use collective code ownership—where anyone can change anything—to avoid internal publishing.) This kind of ownership usually requires a configuration management system that supports concurrent writers (such as CVS) rather than one that uses pessimistic locking. There is a limit to how big a team you can run without some form of internal publishing, but I would err on the side of too little publishing. In other words, assume you don’t need to publish interfaces, and then adjust if you find this causes problems.
Publish as little as you can as late as you can Because publishing locks you into the slower cycle of changes, limit how much you publish. This is where a language’s inability to distinguish between public and published becomes an issue. The best you can do is declare some modules to be the interface and then counsel your software users not to use the other modules, even if they can see them. Keep these interfaces as thin as you can. Publish as late as possible in the development cycle to give yourself time to refine the interfaces. One strategy is to work closely with one or two users of your components—users who are friendly enough to cope with sharp interface changes—before you publish to the masses. Try to make your changes additions In addition to distinguishing between published and public interfaces, we can also identify two types of interface changes. Generally, changes can alter any aspect of an interface. However, there are some changes that only cause additions to an interface, such as adding a method. Additions won’t break any of the interface’s clients—existing clients have no problem using the old methods. Consequently, when you make a change, it’s worth considering whether you can recast it into an addition. For example, if you need to remove a parame-
There’s something to be said for the public–published distinction being more important than the more common public–private distinction.
ter from a method, instead of changing the method, try adding a new method without the parameter. That way, you get an addition rather than a general alteration, and your clients remain compatible. Additions can still cause problems if outside groups have their own implementation of your interface. If that happens, even adding a method breaks the alternative implementation. Thus, some component technologies, such as COM, use immutable interfaces. With an immutable interface, once it’s published, you guarantee not to change it. If you want to change the interface, you must create a second interface, and components can then support this interface at their leisure. It’s not the ideal scheme, but it certainly has its merits.
I
would like to see the public– published distinction appear more in languages and platforms. It’s also interesting that environments don’t tend to provide the facilities to evolve interfaces. Some can deprecate a method that’s due to be removed: Eiffel does this as part of the language, and Java does it (but as part of the built-in documentation). I haven’t seen anyone add a marker to a method that warns implementers of something that’s going to be added or would let you add something to an interface in a provisional way. That’s part of a more general issue in software platforms. So far, platforms haven’t sufficiently understood that software is supposed to be soft and thus needs facilities that allow change. In recent years, we’ve taken more steps in this direction with component-packaging systems, but these are just the early steps.
Martin Fowler is the chief scientist for ThoughtWorks, an Internet systems delivery and consulting company. Contact him at
[email protected]. March/April 2002
IEEE SOFTWARE
19
software construction Editors: Andy Hunt and Dave Thomas ■ The Pragmatic Programmers a n d y @ p r a g m a t i c p r o g r a m m e r. c o m ■ d a v e @ p r a g m a t i c p r o g r a m m e r. c o m
Software Archaeology Andy Hunt and Dave Thomas
T
his isn’t programming, this is archaeology!” the programmer complained, wading through the ancient rubble of some particularly crufty pieces of code. (One of our favorite jargon words: www. tuxedo.org/~esr/jargon/html/entry/crufty. html.) It’s a pretty good analogy, actually. In real archaeology, you’re investigating some
situation, trying to understand what you’re looking at and how it all fits together. To do this, you must be careful to preserve the artifacts you find and respect and understand the cultural forces that produced them. But we don’t have to wait a thousand years to try to comprehend unfathomable artifacts. Code becomes legacy code just about as soon as it’s written, and suddenly we have exactly the same issues as the archaeologists: What are we looking at? How does it fit in with the rest of the world? And what were they thinking? It seems we’re always in the position of reading someone else’s code: either as part of a code review, or trying to customize a piece of open source software, or fixing a bug in code that we’ve inherited. 20
IEEE SOFTWARE
March/April 2002
This analogy is such a compelling and potentially useful one that Dave, Andy, Brian Marick, and Ward Cunningham held a workshop on Software Archaeology at OOPSLA 2001 (the annual ACM Conference on Object-Oriented Programming, Systems, Languages, and Applications). The participants discussed common problems of trying to understand someone else’s code and shared helpful techniques and tips (see the “Tools and Techniques” sidebar). Roll up your sleeves What can you do when someone dumps 250k lines of source code on your desk and simply says, “Fix this”? Take your first cue from real archaeologists and inventory the site: make sure you actually have all the source code needed to build the system. Next, you must make sure the site is secure. On a real dig, you might need to shore up the site with plywood and braces to ensure it doesn’t cave in on you. We have some equivalent safety measures: make sure the version control system is stable and accurate (CVS is a popular choice; see www.cvshome. org). Verify that the procedures used to build the software are complete, reliable, and repeatable (see January/February issue’s column for more on this topic). Be aware of build dependency issues: in many cases, unless you build from scratch, you’re never really sure of the results. If you’re faced with build time measured in hours, with multiple platforms, then the investment in accurate dependency management might be a necessity, not a luxury. Draw a map as you begin exploring the 0740-7459/02/$17.00 © 2002 IEEE
SOFTWARE CONSTRUCTION
code. (Remember playing “Colossal Cave?” You are in a maze of twisty little passages, all alike….) Keep detailed notes as you discover priceless artifacts and suspicious trapdoors. UML diagrams might be handy (on paper—don’t get distracted by a fancy CASE tool unless you’re already proficient), but so too are simple notes. If there are more than one of you on the project, consider using a Wiki or similar tool to share your notes (you can find the original Wiki at www.c2.com/cgi/wiki?WikiWikiWeb and a popular implementation at www.usemod.com/cgi-bin/wiki.pl. As you look for specific keywords, routine names, and such, use the search capabilities in your integrated development environment (IDE), the Tags feature in some editors, or tools, such as Grep, from the command line. For larger projects, you’ll need larger tools: you can use indexing engines such as Glimpse or SWISH++ (simple Web indexing system for humans) to index a large source code base for fast searching. The mummy’s curse Many ancient tombs were rumored to be cursed. In the software world, the incantation for many of these curses starts with “we’ll fix it later.” Later never comes for the original developers, and we’re left with the curse. (Of course, we never put things off, do we?) Another form of curse is found in misleading or incorrect names and comments that help us misunderstand the code we’re reading. It’s dangerous to assume that the code or comments are completely truthful. Just because a routine is named readSystem is no guarantee that it isn’t writing a megabyte of data to the disk. Programmers rarely use this sort of cognitive dissonance on purpose; it’s usually a result of historical accident. But that can also be a valuable clue: How did the code get this way, and why? Digging beneath these layers of gunk, cruft, and patch upon patch, you might still be able to see the original system’s shape and gain
insight into the changes that were required over the years. Of course, unless you can prove otherwise, there’s no guarantee that the routine you’re examining is even being called. How much of the source contains code put in for a future that never arrived? Static analysis of the code can prove whether a routine is being used in most languages. Some IDEs can help with this task, or you can write ad hoc tools in your favorite scripting language. As always, you should prove assumptions you make about the code. In this case, adding specific unit tests helps prove—and continue to prove—what a routine is doing (see www.junit.org for Java and www.xprogramming.org for other languages). By now, you’ve probably started to understand some of the terminology that the original developers used. Wouldn’t it be great to stumble across a Rosetta stone for your project that would help you translate its vocabulary? If there isn’t one, you can start a glossary yourself as part of your note-taking. One of the first things you might uncover is that there are discrepancies in the meaning of terms from different sources. Which version does the code use? Duck blinds and aerial views In some cases, you want to observe the dynamics of the running system without ravaging the source code. One excellent idea from the workshop was to use aspects to systematically introduce tracing statements into the code base without
Instead of hiding in a “duck blind” and getting the view on the ground, you might want to consider an aerial view of the site.
editing the code directly (AspectJ for Java is available at www.aspectj.org). For instance, suppose you want to generate a trace log of every database call in the system. Using something like AspectJ, you could specify what constitutes a database call (such as every method named “db*’” in a particular directory) and specify the code to insert. Be careful, though. Introducing any extra code this way might produce a “Heisenbug,” a bug introduced by the act of debugging. One solution to deal with this issue is to build in the instrumentation in the first place, when the original developers are first building and testing the software. Of course, this brings its own set of risks. One of the participants described an ancient but still-used mainframe program that only works if the tracing statements are left in. Whether diagnostic tracing and instrumentation are added originally or introduced later via aspects, you might want to pay attention to what you are adding, and where. For instance, say you want to add code that records the start of a transaction. If you find yourself doing that in 17 places, this might indicate a structural problem with the code—and a potential answer to the problem you’re trying to solve. Instead of hiding in a “duck blind” and getting the view on the ground, you might want to consider an aerial view of the site. Synoptic, plotting, and visualization tools provide quick, high-level summaries that might visually indicate an anomaly in the code’s static structure, in the dynamic trace of its execution, or in the data it handles. For instance, Ward Cunningham’s Signature Survey method (http://c2.com/doc/SignatureSurvey) reduces each source file to a single line of the punctuation. It’s a surprisingly powerful way of seeing a file’s structure. You can also use visualization tools to plot data from the volumes of tracing information languishing in text files. As with real archaeology, it pays to be meticulous. Maintain a deliberate March/April 2002
IEEE SOFTWARE
21
SOFTWARE CONSTRUCTION
Tools and Techniques
■
The workshop identified these analysis tools and techniques: ■ Scripting languages for
■ ■ ■ ■ ■ ■ ■ ■ ■
–ad hoc programs to build static reports (included by and so on) –filtering diagnostic output Ongoing documentation in basic HTML pages or Wikis Synoptic signature analysis, statistical analysis, and visualization tools Reverse-engineering tools such as Together’s ControlCenter Operating-system-level tracing via truss and strace Web search engines and tools to search for keywords in source files IDE file browsing to flatten out deep directory hierarchies of the source Test harnesses such as Junit and CPPUnit API documentation generation using Javadoc, doxygen, and so on Debuggers
■
■
Participants also identified these invasive tools: ■ ■ ■ ■
Hand-inserted trace statements Built-in diagnostic instrumentation (enabled in production code as well) Instrumentation to log history of data values at interface calls Use of AspectJ to introduce otherwise invasive changes safely
pace; and keep careful records. Even for a short-term, quick patch that doesn’t require you to understand the whole code base, keep careful records of what you’ve learned, what you’ve tried, what worked, and what didn’t. What were they thinking? Archaeologists generally don’t make wisecracks about how stupid a particular culture was. In our industry, we generally don’t show such restraint. But it’s important when reading code to realize that apparently bone-headed decisions that appear to be straight out of a “Dilbert” cartoon seemed perfectly reasonable to the developers at the time. Understanding “what they were thinking” is critical to understanding how and why they wrote the code the way they did. If you discover they misunderstood something, you’ll likely find that mistake in more than one place. But rather than simply “flipping the bozo bit” on the original authors, try to evaluate their strengths as well as weaknesses. You might find lost treasure—buried domain expertise that’s been forgotten. Also, consider to which “school of 22
IEEE SOFTWARE
March/April 2002
programming” the authors belonged. Regardless of implementation language, West-Coast Smalltalkers will write in a different style from European Modula programmers, for instance. In a way, this approach gets us back to the idea of treating code as literature. What was the house style? By understanding what the developers were thinking, what influenced them, what techniques they were fond of, and which ones they were unaware of, you will be much better positioned to fully understand the code they produced and take it on as your own. Leaving a legacy Given that today’s polished code will inevitably become the subject of some future developer’s archaeological dig, what can we do to help them? How can we help them comprehend “what we were thinking” and work with our code? ■
Ensure the site is secure when you leave. Every file related to the project should be under version control, releases should be appropriately identified, and the build
■
■
should be automatic and reliable. Leave a Rosetta stone. The project glossary was useful for you as you learned the domain jargon; it will be doubly useful for those who come after you. Make a simple, high-level treasure map. Honor the “DRY” principle: Don’t duplicate information that’s in the code in comments or in a design document. Comments in the code explain “why,” the code itself shows “how,” and the map shows where the landscape’s main features are located, how they relate to each other, and where to find more detailed information. Build in instrumentation, tracing, and visualization hooks where applicable. This could be as simple as tracing “got here” messages or as intricate as an embedded HTTP server that displays the application’s current status (our book, The Pragmatic Programmer [Addison-Wesley, 2000] discusses building testable code). Use consistent naming conventions to facilitate automatic static code analysis and search tools. It helps us humans, too. No evil spells. You know what the incantations sound like. Don’t let the mummy’s curse come back to haunt programmers later. The longer a curse festers and grows, the worse it is when it strikes— and curses often backfire against their creators first.
Acknowledgments Many thanks to the other workshop organizers, Brian Marick and Ward Cunningham, and to the attendees: Ken Anderson, Vladimir Degen, Chet Hendrickson, Michael Hewner, Kevin Johnson, Norm Kerth, Dominik Kuropka, Dragos Manolescu, John McIntosh, Walter Risi, Andy Schneider, Glenn Vanderburg, and Charles Weir. Andy Hunt and Dave Thomas are partners in The
Pragmatic Programmers, LLC. They feel that software consultants who can’t program shouldn’t be consulting, so they keep current by developing complex software systems for their clients. They also offer training in modern development techniques to programmers and their management. They are coauthors of The Pragmatic Programmer and Programming Ruby, both from Addison-Wesley. Contact them through www. pragmaticprogrammer.com.
focus
guest editors’ introduction
The Software Engineering of Internet Software Elisabeth Hendrickson, Quality Tree Software Martin Fowler, ThoughtWorks
hen we first started working with the Web, it was a publishing medium. The Web was essentially a poorly edited and rather erratic encyclopedia. Although much of the Web is still that, the Web is increasingly doing things that we’ve typically seen in enterprise applications. Rather than simply presenting static information, Web sites invite user interaction. The Web plays host to a new generation of systems following green screens and client-server GUIs.
W
The real departure for Web-based enterprise applications is the possibility of wideranging accessibility. A system that might be deployed in-house at a manufacturing company can now be deployed to all the dealers of that manufacturer’s products. Indeed, it can be deployed to all the customers. In the past, such a wide deployment was a major hurdle. With Web-based technologies, providing external access to internal systems is as easy as publicizing a URL. Without doubt, the Web has significantly affected the users of computer systems, but what effect has it had on enterprise application designers? That’s the question we wanted to explore with this special issue. What things are the same, what things are new, and what things have changed in some subtle but interesting ways? The activities that people need to do to build Web software do not differ from those we do with any software. We still analyze requirements, design, write code, test, and deploy. Yet the pressures of building Web 0740-7459/02/$17.00 © 2002 IEEE
applications add some new variations of familiar activities. None of these changes are necessarily massive, but they suffice to suggest some new directions. Quality, scalability, usability, maintainability Jeff Offutt’s article “Quality Attributes of Web Software Applications” is a good starting point for this discussion; he explores quality attributes for Web-based systems. Although the Web hasn’t necessarily introduced any new quality issues, Web applications often combine more quality challenges than previous generations of applications. Furthermore, the Web’s capabilities introduce new twists on traditional ideas of quality. The Web also brings the question of scalability to the fore. Whereas a system might have had a few thousand users a decade ago, today Web system designers must consider the possibility that hundreds of thousands of users might log in at once. Colleen March/April 2002
IEEE SOFTWARE
23
Roe and Sergio Gonik dig into the issue of scalability in “Server-Side Design Principles for Scalable Internet Systems.” Their article culls principles from their experiences with software that is both behaviorally complex and that needs that elusive scalability. Additionally, many Web users are end customers. Web applications have a greater need for usability than many enterprise applications previously demanded. Larry Constantine and Lucy Lockwood tackle this problem with techniques for designing user interfaces through user models, task models, and content models in their article, “Usage-Centered Engineering for Web Applications.” When we talk about user interfaces, we must remember that as well as designing them from the outside to be usable, we must also design them from the inside to be maintainable. Web applications need to change as business changes, and even the most usable site can become a problem if the interface’s internal design is difficult to change and fix. In their article, “Objects and the Web,” Alan Knight and Naci Dai peer inside a design to reveal that one of the oldest patterns for user interfaces, the Model-View-Controller, is a key part of maintainable Web systems. Indeed, MVC plays a bigger-than-ever part in the Web. Agile, international, testing, lessons learned Web applications must be up quickly to compete. In “Going Faster: Testing the Web Application,” Edward Hieatt and Robert Mee explore this dilemma and suggest that
About the Authors Elisabeth Hendrickson is an independent consultant who specializes in software quality assurance and management. She has 14 years of experience working with leading software companies and is the founder of Quality Tree Software. An award-winning author, she has published more than 20 articles and is a frequently invited speaker at major software quality and software management conferences. Contact her at
[email protected].
Martin Fowler is the chief scientist for ThoughtWorks: an enterprise application development and integration company. He has spent many years applying object oriented technology to enterprise applications. He edits the Design column for IEEE Software and is the author of several books on software design. He is currently working on a book on enterprise application architecture, which you can find at www.martinfowler.com.
24
IEEE SOFTWARE
March/April 2002
high quality is not just compatible with high speed—quality enables speed. By writing the tests before the code, the authors discovered that they not only produced a more solid end product but also could do it more quickly. It’s hard to write an introduction this far without raising the spirit of another recent fad: agile methods such as Extreme Programming. Throughout many of these articles, you’ll see that theme. Web applications seem particularly suited to agile processes’ emphasis on quality and speed. One feature of agile methods is their de-emphasis on modeling. In “Lessons in Agility from Internet-Based Development,” Scott Ambler describes a case study of how a Web development team used modeling within an agile process. He concludes that there is less emphasis on documentation and tools but more emphasis on communication and quality. Modeling is there, it’s recognizable, but a little different. The Web’s wide reach plays particular havoc with those who like to think that world revolves around the English language. Native English speakers often run into Web pages they can’t read and get a taste of what it’s like for many in the world who don’t have a dominant grasp of English. This leads to a new challenge: enabling Web applications for international use. In “Software Localization for Internet Software: Issues and Methods,” Rosann Webb Collins focuses on the needs of internationalization, providing one step toward a checklist for Web applications that need a global reach. Eric Altendorf, Moses Hohman, and Roman Zabicki provide a nice summary of lessons learned from Web development in “Using J2EE on a Large, Web-Based Project.” This is a case study of a moderately large Web application using the popular enterprise Java technologies. The authors discuss the challenges they faced designing dynamic HTML user interfaces, tackling internationalization, and integrating third-party systems that were developed before the Web was around.
O
ur challenge as a profession, both in Web development and in wider software development, is to better understand and communicate these lessons. We hope this issue helps to do just that.
focus
engineering Internet software
Quality Attributes of Web Software Applications Jeff Offutt, George Mason University
he World Wide Web was originally designed to present information to Web surfers using simple sites that consisted primarily of hyperlinked text documents. Modern Web applications run large-scale software applications for e-commerce, information distribution, entertainment, collaborative working, surveys, and numerous other activities. They run on distributed hardware platforms and heterogeneous computer
T Web applications have very high requirements for numerous quality attributes. This article discusses some of the technological challenges of building today’s complex Web software applications, their unique quality requirements, and how to achieve them. 0740-7459/02/$17.00 © 2002 IEEE
systems. The software that powers Web applications is distributed, is implemented in multiple languages and styles, incorporates much reuse and third-party components, is built with cutting edge technologies, and must interface with users, other Web sites, and databases. Although the word “heterogeneous” is often used for Web software, it applies in so many ways that the synonymous term “diverse” is more general and familiar, and probably more appropriate. The software components are often distributed geographically both during development and deployment (diverse distribution), and communicate in numerous distinct and sometimes novel ways (diverse communication). Web applications consist of diverse components including traditional and nontraditional software, interpreted scripting languages, plain HTML files, mixtures of HTML and
programs, databases, graphical images, and complex user interfaces. As such, engineering an effective Web site requires large teams of people with very diverse skills and backgrounds. These teams include programmers, graphics designers, usability engineers, information layout specialists, data communications and network experts, and database administrators. This diversity has led to the notion of Web site engineering.1 The tremendous reach of Web applications into all areas of communication and commerce makes this one of the largest and most important parts of the software industry. Yet a recent National Research Council study2 found that the current base of science and technology is inadequate for building systems to control critical software infrastructure. The President’s commission on critical infrastructure protection reached March/April 2002
IEEE SOFTWARE
25
Instead of “sooner but worse,” it is often advantageous to deliver Web applications “later and better.”
26
IEEE SOFTWARE
this same conclusion in the President’s Information Technology Advisory Committee report.3 This inadequacy is particularly severe in the novel, high-risk area of Web application software. Although Web software development uses cutting-edge, diverse technologies, little is known about how to ensure quality attributes such as Web application reliability. Unique aspects of Web application software Several factors inherent to Web development contribute to the quality problem. Developers build Web-based software systems by integrating numerous diverse components from disparate sources, including custombuilt special-purpose applications, customized off-the-shelf software components, and third-party products. In such an environment, systems designers choose from potentially numerous components, and they need information about the various components’ suitability to make informed decisions about the software’s required quality attributes. Much of the new complexity found with Web-based applications also results from how the different software components are integrated. Not only is the source unavailable for most of the components, the executables might be hosted on computers at remote, even competing organizations. To ensure high quality for Web systems composed of very loosely coupled components, we need novel techniques to achieve and evaluate these components’ connections. Finally, Web-based software offers the significant advantage of allowing data to be transferred among completely different types of software components that reside and execute on different computers. However, using multiple programming languages and building complex business applications complicates the flow of data through the various Web software pieces. When combined with the requirements to keep data persistent through user sessions, persistent across sessions, and shared among sessions, the list of abilities unique to Web software begins to get very long. Thus, software developers and managers working on Web software have encountered many new challenges. Although it is obvious that we struggle to keep up with the technology, less obvious is our difficulty in understanding just how Web software de-
March/April 2002
velopment is different, and how to adapt existing processes and procedures to this new type of software. Economic changes We evaluate software by measuring the quality of attributes such as reliability, usability, and maintainability, yet academics often fail to acknowledge that the basic economics behind software production has a strong impact on the development process. Although the field of software engineering has spent years developing processes and technologies to improve software quality attributes, most software companies have had little financial motivation to improve their software’s quality. Software contractors receive payment regardless of the delivered software’s quality and, in fact, are often given additional resources to correct problems of their own making. So-called “shrink wrap” vendors are driven almost entirely by time to market; it is often more lucrative to deliver poor-quality products sooner than high-quality products later. They can deliver bug fixes as new releases that are sold to generate more revenue for the company. For most application types, commercial developers have traditionally had little motivation to produce high-quality software. Web-based software, however, raises new economic issues. When I recently surveyed a number of Web software development managers and practitioners, I found that companies that operate through the Web depend on customers using and, most importantly, returning to their sites. Thus, unlike many software contractors, Web application developers only see a return on their investment if their Web sites satisfy customer needs. And unlike many software vendors, if a new company puts up a competitive site of higher quality, customers will almost immediately shift their business to the new site once they discover it. Thus, instead of “sooner but worse,” it is often advantageous to be “later and better.” Despite discussions of “sticky Web sites” and development of mechanisms to encourage users to return,4 thus far the only mechanism that brings repeat users to Web sites has been high quality. This will likely remain true for the foreseeable future. In software development, a process driver is a factor that strongly influences the process used to develop the software. Thus, if soft-
ware must have very high reliability, the development process must be adapted to ensure that the software works well. When I surveyed the important quality process drivers for traditional software, developers always gave a single answer that stands far above the rest: time to market. But when I recently made the same survey of Web software development managers and practitioners, they claim that time to market, although still important, is no longer the dominant process driver. They see the three most important quality criteria for Web application success (and thus, the underlying software) as ■ ■ ■
Reliability Usability Security
Additional important criteria include ■ ■ ■ ■
Availability Scalability Maintainability Time to market
Of course, this is hardly a complete list of important or even relevant quality attributes, but it provides a solid basis for discussion. Certainly speed of execution is also important, but network factors influence this more than software does, and other important quality attributes such as customer service, product quality, price, and delivery stem from human and organizational rather than software factors. That said, the quality attributes I just listed track closely with those cited in other books and articles,1, 5–8 suggesting wide agreement that successful Web software development depends on satisfying these quality attributes. Reliability Extensive research literature and a collection of commercial tools have been devoted to testing, ensuring, assuring, and measuring software reliability. Safety-critical software applications such as telecommunications, aerospace, and medical devices demand highly reliable software, but although many researchers are reluctant to admit it, most software currently produced does not need to be highly reliable. I have been teaching software testing in various forms for 15 years yet have always felt like I was selling something that nobody wants.
Many businesses’ commercial success depends on Web software, however—if the software does not work reliably, the business will not succeed. The user base for Web software is very large and expects Web applications to work as reliably as if they were going to the grocery store or calling to order from a catalog. Moreover, if a Web application does not work well, the users do not have to drive further to reach another store; they can simply point their browser to a different URL. Web sites that depend on unreliable software will lose customers, and the businesses could lose much money. Companies that want to do business over the Web must spend resources to ensure high reliability. Indeed, they cannot afford not to.
Companies that want to do business over the Web must spend resources to ensure high reliability.
Usability Web application users have grown to expect easy Web transactions—as simple as buying a product at a store. Although much wisdom exists on how to develop usable software and Web sites (Jakob Nielsen’s text9 being a classic example), many Web sites still do not meet most customers’ usability expectations. This, coupled with the fact that customers exhibit little site loyalty, means unusable Web sites will not be used—customers will switch to more usable Web sites as soon as they come online. Security We have all heard about Web sites being cracked and private customer information distributed or held for ransom. This is only one example of the many potential security flaws in Web software applications. When the Web functioned primarily to distribute online brochures, security breaches had relatively small consequences. Today, however, the breach of a company’s Web site can cause significant revenue losses, large repair costs, legal consequences, and loss of credibility with customers. Web software applications must therefore handle customer data and other electronic information as securely as possible. Software security is one of the fastest growing research areas in computer science, but Web software developers currently face a huge shortfall in both available knowledge and skilled personnel. Availability In our grandparents’ time, if a shopkeeper March/April 2002
IEEE SOFTWARE
27
Designing and building Web software applications that scale well represents one of today’s most interesting and important software development challenges.
in a small town wanted to take a lunch break, he would simply put a sign on the front door that said “back at 1:00.” Although today’s customers expect to be able to shop during lunchtime, we do not expect stores to be open after midnight or on holidays. But that only works for “brick and mortar” stores. When customers can visit our stores online, 2:00 a.m. in North America is the middle of the afternoon in Asia, and national or religious holidays fall on different days in different countries. On the Web, customers not only expect availability 24 hours a day, seven days a week, they expect the Web site to be operational every day of the year—24/7/365. Even a 10-minute downtime can be damaging; I recently purchased $50 worth of books online but switched companies because the first Web site gave a “missing file” error message. Ten minutes later, that Web site was operational again but had lost my sale. Although this was only one sale, many customers would never come back. Availability means more than just being up and running 24/7/365; the Web software must also be accessible to diverse browsers. In the seemingly never-ending browser wars of the past few years, some software vendors actively sought to make sure their software would not work under competitors’ browsers. By using features only available for one browser or on one platform, Web software developers become foot soldiers in the browser wars, sometimes unwittingly. As an example, one major Web site at my organization uses Shockwave flash, which is only compatible with Microsoft IE and Netscape under Windows. Thus, the many Unix and Netscape users in my building cannot view their own Web site. To be available in this sense, Web sites must adapt their presentations to work with all browsers, which requires significantly more knowledge and effort on developers’ part. Scalability A recent television advertisement showed a small group of young men and women nervously launching their Web site. The celebration started when the site got its first hit, but their faces quickly turned gray when the number of hits went into the thousands, then millions. As with a small corner store, as few as three or four people can create a commercial Web site but, unfortunately (or fortunately
28
IEEE SOFTWARE
March/April 2002
for the profit margin), virtually an unlimited number of customers can visit the Web site. We must therefore engineer Web software applications to be able to grow quickly in terms of both how many users they can service and how many services they can offer. The need for scalability has driven many technology innovations of the past few years. The industry has developed new software languages, design strategies, and communication and data transfer protocols in large part to allow Web sites to grow as needed. Scalability also directly influences other attributes. Any programming teacher knows that any design will work for small classroom exercises, but large software applications require discipline and creativity. Likewise, as Web sites grow, small software weaknesses that had no initial noticeable effects can lead to failures (reliability problems), usability problems, and security breaches. Designing and building Web software applications that scale well represents one of today’s most interesting and important software development challenges. Maintainability One novel aspect of Web-based software systems is the frequency of new releases, or the update rate. Traditional software involves marketing, sales, and shipping or even personal installation at customers’ sites. Because this process is expensive, software manufacturers usually collect maintenance modifications over time and distribute them to customers simultaneously. For a software product released today, developers will start collecting a list of necessary changes. For a simple change (say, changing a button’s label), the modification might be made immediately. But the delay in releases means that customers won’t get more complex (and likely important) modifications for months, perhaps years. Web-based software, however, gives customers immediate access to maintenance updates—both small changes (such as changing the label on a button) and critical upgrades can be installed immediately. Instead of maintenance cycles of months or years, Web sites can have maintenance cycles of days or even hours. Although other software applications have high maintenance requirements, and some research has focused on “on the fly” maintenance10 for specialized applications, frequent maintenance has never before been necessary for such a quantity of commercial software.
Internet
Another ramification of the increased update rate has to do with compatibility. Users do not always upgrade their software; hence, software vendors must ensure compatibility between new and old versions. Companies can control the distribution of Web software to eliminate that need, although Web applications must still be able to run correctly on several Web browsers and multiple versions of each browser. Another possible consequence of the rapid update rate is that developers might not feel the same need to fix faults before release—they can always be fixed later. I have seen no data to indicate this is happening. Time to market This has always been a key business driver and remains important for Web software, but it now shares the spotlight with other quality attributes. Most of the software industry continues to give priority to first to market. Given the other factors discussed here, however, the requirement for patience can and must impact the process and management of Web software projects. Analysis Software researchers, practitioners, and educators have discussed these criteria for years. No type of application, however, has had to satisfy all of these quality attributes at the same time, and Web software components are coupling more loosely than any previous software applications. In fact, these criteria have until recently been important to only a small fraction of the software industry. They are now essential to the bottom line of a large and fast growing part of the industry, but we do not yet have the knowledge to satisfy or measure these criteria for the new technologies used in Web software applications. Technology changes The commercial use of the Internet and Web has grown explosively in the past five years. In that time, the Internet has evolved from primarily being a communications medium (email, files, newsgroups, and chat rooms) to a vehicle for distributing information to a full-fledged market channel for e-commerce. Web sites that once simply displayed information for visitors have become interactive, highly functional systems that
GIF images
HTTP server
Browser HTML forms
CGI programs
Helpers Audio Video
Database
Client side
Files
Server side
let many types of businesses interact with many types of users. These changes have had an enormous impact on software engineering. As the use of the Internet and Web has grown, the amount, type, and quality of software necessary for powering Web sites has also grown. Just a few years ago, Web sites were primarily composed of static HTML files, so-called “soft brochures,” usually created by a single Webmaster who used HTML, JavaScript, and simple CGI scripts to present information and obtain data from visitors with forms. Figure 1 illustrates the early Web, a typical client-server configuration in which the client is a Web browser that people use to visit Web sites that reside on different computers, the servers, and a software package called a Web server sends the HTML files to the client. HTML files contain JavaScripts, which are small pieces of code that are interpreted on the client side. HTML forms generate data that are sent back to the server to be processed by CGI programs. This very simple model of operation can support relatively small Web sites. It uses small-scale software, offers little security, usually cannot support much traffic, and offers limited functionality. This was called a two-tier system because two separate computers were involved. The Web’s function and structure have changed drastically, particularly in the past 24 months, yet most software engineering researchers, educators, and practitioners have not yet grasped how fully this change affects engineering principles and processes. Web sites are now fully functional software systems that provide business-to-customer e-commerce, business-to-business e-commerce, and many
Figure 1. Firstgeneration Web sites followed a client-server model that suffices to support simple page viewing and limited traffic but does not scale well.
March/April 2002
IEEE SOFTWARE
29
Network
Client
Web server
Figure 2. Modern Web sites generally follow an N-tier model that, by separating presentation from business logic, supports much greater application complexity, higher traffic, and stronger site security.
30
IEEE SOFTWARE
Middleware
Middleware
Application servers
services to many users. Instead of referring to visitors to Web sites, we now refer to users, implying much interaction. Instead of Webmasters, large Web sites must employ Web managers leading diverse teams of IT professionals that include programmers, database administrators, network administrators, usability engineers, graphics designers, security experts, marketers, and others. This team uses diverse technologies including several varieties of Java (Java, servlets, Enterprise JavaBeans, applets, and JavaServer Pages), HTML, JavaScript, XML, UML, and many others. The growing use of third-party software components and middleware represents one of the biggest changes. The technology has changed because the old two-tier model did not support the high quality requirements of Web software applications. It failed on security, being prone to crackers who only need to go through one layer of security on a computer that is, by definition, open to the world to provide access to all data files. It failed on scalability and maintainability because as Web sites grow, a twotier model cannot effectively separate presentation from business logic, and the applications thus become cumbersome and hard to modify. It failed on reliability: whereas previous Web software generations relied on CGI programs, usually written in Perl, many developers have found that large complex Perl programs can be hard to program correctly, understand, or modify. Finally, it failed on availability because hosting a site on one Web server imposes a bottleneck: any server problems will hinder user access to the Web site. Figure 2 illustrates current Web site software. Instead of a simple client-server model, the configuration has expanded first to a three-tier model and now more generally to an N-tier model. Clients still use a browser to visit Web sites, which are hosted and deliv-
March/April 2002
Database server
ered by Web servers. But to increase quality attributes such as security, reliability, availability, and scalability, as well as functionality, most of the software has been moved to a separate computer—the application server. Indeed, on large Web sites, a collection of application servers typically operates in parallel, and the application servers interact with one or more database servers that may run a commercial database. The client-server interaction, as before, uses the Internet, but middleware—software that handles communication, data translation, and process distribution—often connects the Web and application servers, and the application and database servers. New Web software languages such as Java are easier to modify and program correctly and permit more extensive reuse. These features enhance maintainability, reliability, and scalability. The N-tier model also permits additional security layers between potential crackers and the data and application business logic. The ability to separate presentation (typically on the Web server tier) from the business logic (on the application server tier) makes Web software easier to maintain and expand in terms of customers serviced and services offered. Distributed computing, particularly for the application servers, allows the Web application to tolerate failures and handle more customers, and allows developers to simplify the software design. Newer design models11,12 have extended these goals even further. JavaServer Pages (JSPs) and Enterprise JavaBeans (EJBs) let developers separate presentation from logic, which helps make software more maintainable. To further subdivide the work, developers can create a software dispatcher that accepts requests on the Web server tier, then forwards the request to an appropriate hardware or software component on the application tier. Such design strategies lead to more
reliable software and more scalable Web sites. Of course the technology keeps changing, with the latest major addition to the technology being Microsoft’s .NET. As of this writing, it is too early to say what type of effect .NET will have, although it does not seem to provide additional abilities beyond what is already available. Clearly, modern Web sites’ increased functionality creates a need for increasingly complex software, system integration and design strategies, and development processes. This leads to two exciting conclusions: ■
■
One of the largest and fastest-growing software industry segments finds itself in dire need of the high-end software engineering practices and processes that researchers and educators have been developing and teaching for years. The new models for Web-based software production and deployment require that we adapt or replace many of the research solutions available now.
These conclusions imply that we need significant research progress, education, and training in diverse software engineering areas. The software that drives the Web has become a critical part of the world’s infrastructure. Although Web software’s immaturity poses significant risk to both industry and government, it also represents an opportunity for software engineering researchers and educators. Planning for the future One more important issue remains: the lack of engineers skilled in Web software development. A recent study from the US National Research Council found that the current base of science and technology is inadequate for building systems to control critical software infrastructure.2 Much of Web software is built from existing systems and involves complicated analysis to effectively compose and integrate these components into systems. Yet the combination of a shortage of skilled IT engineers and the large number of IT jobs means companies often resort to hiring engineers who have less skills and education than desired. As a small example, US universities graduate approximately 25,000 bachelor degrees in computer science every year, but industry recently esti-
mated that it needs more than 200,000 IT professionals.2 Even with the current economic downturn, the university output is not enough. If universities could double the production of computer scientists, we still could not put a dent in the need. (Most economists and business leaders currently believe last year’s many layoffs and bankruptcies in the e-commerce sector resulted from temporary problems, and expect significant growth in the near future. I optimistically accept this prognosis; if it is wrong, then this article will be irrelevant anyway.) We can only meet this need by ■ ■
■
Retraining experienced engineers to work with the new technology Applying knowledge and technology to increase efficiency, thereby reducing the number of engineers needed Finding ways to let people with less education and skills contribute
Although Web software’s immaturity poses significant risk to both industry and government, it also represents an opportunity for software engineering researchers and educators.
We are already seeing some progress in all three of these directions: ■
■
■
Training classes and university courses in Web software engineering technologies are increasing and experiencing very high enrollment. The Web software engineering courses at George Mason University are oversubscribed every semester, with some students being nondegree professionals seeking to improve their marketability. New textbooks, tools, languages, and standards are emerging to make Web software engineering knowledge more accessible and easier to learn, use, and deploy. For example, several XML innovations in the past year have made it a more useful and accessible language, while new development tools and refinements in the standards for JSPs and EJBs allow more software to be created with less effort. Automated technologies have recently allowed nonprogrammers to contribute more to Web software development. When HTML was first introduced, an HTML writer needed to fully know the language and be proficient with a text editor to create Web pages. Recent tools provide point-and-click ability to create Web pages that can even be enhanced with dynamic HTML and JavaScripts March/April 2002
IEEE SOFTWARE
31
while requiring very little knowledge of HTML and programming. Achieving the high quality requirements of Web software represents a difficult challenge. Although other segments of the software industry have already mastered some of these, such as the need for reliability in telecommunications and network routing, aerospace, and medical devices, they have typically done so by hiring the very best developers on the market, using lots of resources (time, developers, and testing), or relying on old, stable software and technologies. Unfortunately, these solutions will not work for Web applications. There are simply not enough of the best developers to implement all the Web software needed today, and few but the largest companies can afford to invest extra resources in the form of time, developers, and testing. Finally, it should be obvious that old, stable software technologies will not suffice as a base for the Web—it relies on the latest cutting-edge software technology. Although the use of new technology involves some risk, it allows us to achieve otherwise unattainable levels of scalability and maintainability. Indeed, technological innovations in just the past three years have greatly advanced the field, both in breadth and depth. And research progresses: New conferences appear almost monthly, and traditional conferences feature more tracks and papers on Web software issues. Every new PhD student, it seems, wants to write a thesis on some aspect of Web software engineering, and more textbooks and classes teach Web software application material. When George Mason University offered a graduate course in Web software engineering in Fall 2000, the class immediately became popular, with a large waiting list.
About the Author Jeff Offutt is an associate professor of information and software engineering at George Mason
University. His current research interests include software testing, analysis and testing of Web applications, object-oriented program analysis, module and integration testing, formal methods, and software maintenance. He received a PhD in computer science from the Georgia Institute of Technology. He served as program chair for ICECCS 2001 and is on the editorial boards for IEEE Transactions on Software Engineering, Journal of Software Testing, Verification and Reliability, and Journal of Software and Systems Modeling. He is a member of the ACM and IEEE Computer Society. Contact him at the Dept. of Information and Software Eng., Software Eng. Research Lab, George Mason Univ., Fairfax, VA 22030-4444;
[email protected]; www.ise.gmu.edu/faculty/ofut.
32
IEEE SOFTWARE
March/April 2002
W
eb software engineering presents challenging and unique research problems. We currently lack the knowledge to create Web software of sufficient complexity or quality and that can be updated quickly and reliably. Although Web software engineering has significant differences from traditional software engineering, we can adapt much of what we already know to understanding and resolving these differences. Not only are we making extraordinary progress, we are also bringing much research of the past 20 years to fruition. Indeed, this is an exciting time to be a software engineer.
Acknowledgments This work is supported in part by the US National Science Foundation under grant CCR-98-04111.
References 1. T.A. Powell, Web Site Engineering: Beyond Web Page Design, Prentice Hall, Upper Saddle River, N.J., 2000. 2. F.B. Schneider, Trust in Cyberspace, National Academy Press, Washington, D.C., 1999. 3. President’s Information Technology Advisory Committee, Information Technology Research: Investing in our Future, tech. report, Nat’l Coordination Office for Computing, Information, and Communications, Washington, D.C., 1999; www.ccic.gov/ac/report. 4. D.A. Menascé, Scaling for E-Business: Technologies, Models, Performance, and Capacity Planning, Prentice Hall, Upper Saddle River, N.J., 2000. 5. E. Dustin, J. Rashka, and D. McDiarmid, Quality Web Systems: Performance, Security, and Usability, AddisonWesley, Reading, Mass., 2001. 6. L.L. Constantine and L.A.D. Lockwood, Software for Use: A Practical Guide to the Models and Methods of Usage Centered Design, ACM Press, New York, 2000. 7. S. Murugesan and Y. Deshpande, “Web Engineering: A New Discipline for Development of Web-Based Systems,” Web Engineering 2000, Lecture Notes in Computer Science 2016, Springer-Verlag, Berlin, 2001, pp. 3–13. 8. N. Kassem and the Enterprise Team, Designing Enterprise Applications with the Java 2 Platform, Enterprise Edition, Sun Microsystems, Palo Alto, Calif., 2000. 9. J. Nielsen, Designing Web Usability, New Riders Publishing, Indianapolis, Ind., 2000. 10. M.E. Segal and O. Frieder, “On-the-Fly Program Modification: Systems for Dynamic Updating,” IEEE Software, vol. 10, no. 2, Mar. 1993, pp. 53–65. 11. Wrox Multi Team, Professional Java Server Programming, J2EE edition, Wrox Press, Chicago, 2000. 12. A. Scharl, Evolutionary Web Development, SpringerVerlag, Berlin, 2000.
For more information on this or any other computing topic, please visit our Digital Library at http://computer.org/publications/dlib.
focus
engineering Internet software
Server-Side Design Principles for Scalable Internet Systems Colleen Roe and Sergio Gonik, GemStone Systems
Today’s Internet systems face real challenges in trying to serve many concurrent users. n today’s Internet systems, nothing is constant but change. If you build a successful site they will come—and boy, will they come. Consequently, designers today are faced with building systems to serve an unknown number of concurrent users with an unknown hit rate and transactionsper-second requirement. The key to designing and maintaining such systems is to build in scalability features from the start, to create Internet systems whose capacity we can incrementally increase to satisfy ramping demand.
I
This article explores the design principles guiding the creation of scalable systems. Each principle is discussed and followed by examples of its application. 34
IEEE SOFTWARE
Any application can essentially be characterized by its consumption of four primary system resources: CPU, memory, file system bandwidth, and network bandwidth. Scalability is achieved by simultaneously optimizing the consumption of these resources and designing an architecture that can grow modularly by adding more resources. This article looks at the underlying principles needed to achieve such designs and discusses some specific strategies that exploit these principles. The principles of scalable architecture Relatively few design principles are required to design scalable systems. The list is limited to ■ ■
divide and conquer (D&C) asynchrony
March/April 2002
■ ■ ■
encapsulation concurrency parsimony
We have used these principles in system design over several years with good success. As is evident in the discussion that follows, there is some degree of overlap in the principles. Despite this, each presents a concept that is important in its own right when designing scalable systems. There are also tensions between these principles; one might sometimes be applied at the cost of another. The crux of good system design is to strike the right balance. Divide and conquer D&C means that the system should be partitioned into relatively small subsystems, each carrying out some well-focused function. This permits deployments that can 0740-7459/02/$17.00 © 2002 IEEE
leverage multiple hardware platforms or simply separate processes or threads, thereby dispersing the load in the system and enabling various forms of load balancing and tuning. D&C varies slightly from the concept of modularization in that it addresses the partitioning of both code and data and could approach the problem from either side. One example is replicated systems. Many applications can, in fact, be broken into replicated instances of a system. Consider an insurance application that is deployed as five separate but identical systems, each serving some specific geography. If load increases, more instances are deployed, and all running instances now service a smaller geography. Another example is functional–physical partitioning. A system that processes orders is broken into two components: an ordertaking component and an order-satisfaction component. The order-taking component acquires order information and places it in a queue that is fed into the order-satisfaction component. If system load increases, the components might run on two or more separate machines. Asynchrony Asynchrony means that work can be carried out in the system on a resource-available basis. Synchronization constrains a system under load because application components cannot process work in random order, even if resources exist to do so. Asynchrony decouples functions and lets the system schedule resources more freely and thus potentially more completely. This lets us implement strategies that effectively deal with stress conditions such as peak load. Asynchrony comes at a price. Asynchronous communications are generally more difficult to design, debug, and manage. “Don’t block” is probably the most important advice a scalable-system designer can receive. Blocking = bottlenecks. Designing asynchronous communications between systems or even between objects is always preferable. Moreover, use background processing where feasible. Always question the need to do work online in real time. A little lateral thinking can sometimes result in a solution that moves some work into background processing. For example, order satisfaction can include a background process that
emails an appropriate notification to the user on completion. Encapsulation Encapsulation results in system components that are loosely coupled; ideally, there is little or no dependence among components. This principle often (but not always) correlates strongly with asynchrony. Highly asynchronous systems tend to have well-encapsulated components and vice versa. Loose coupling means that components can pursue work without waiting on work from others. Layers and partitions are an example of the application of encapsulation. Layers and partitions within layers are the original principles that drive software architecture. The concepts might be old, but they are still legitimate. Layers provide well-insulated boundaries between system parts, and they permit reimplementation of layers without perturbation to surrounding layers. Work can be carried out independently within layers.
The more concurrency an application exploits, the better the possibilities to expand by adding more hardware.
Concurrency Concurrency means that there are many moving parts in a system. Activities are split across hardware, processes, and threads and can exploit the physical concurrency of modern symmetric multiprocessors. Concurrency aids scalability by ensuring that the maximum possible work is active at all times and addresses system load by spawning new resources on demand (within predefined limits). One example is to exploit multithreading. Question the need to carry out work serially. Look for opportunities to spawn threads to carry out tasks asynchronously and concurrently. You can also accommodate expansion by adding more physical platforms. Concurrency also maps directly to the ability to scale by rolling in new hardware. The more concurrency an application exploits, the better the possibilities to expand by adding more hardware. Parsimony Parsimony means that a designer must be economical in what he or she designs. Each line of code and each piece of state information has a cost, and, collectively, the costs can increase exponentially. A developer must ensure that the implementation is as efficient and lightweight as possible. Paying attention to thousands of microdetails in March/April 2002
IEEE SOFTWARE
35
Application stack Presentation Transcoding Personalize content management
Application Handle exceptions Invoke services Maintain conversational state Mediation between presentation and service
Services Domain service Single behavior services External services Object distribution Transaction control Use case control
Domain Business behavior Semantic validation
Persistence Basic CRUD services OR mapping Transaction support Profiling
Figure 1. Layers and their responsibilities in a service-based architecture.
a design and implementation can eventually pay off at the macrolevel with improved system throughput. Parsimony also means that designers must carefully use scarce or expensive resources. Such resources might be cached or pooled and multiplexed whenever possible. This principle basically pervades all the others. No matter what design principle a developer applies, a parsimonious implementation is appropriate. Some examples include ■
■
■
■
Algorithms. Ensure that algorithms are optimal to the task at hand. Everyone knows that O(n) algorithms are preferable to, say, O(n2), but sometimes this is overlooked. Several small inefficiencies can add up and kill performance. Object models. Pare object models to the bone. Most large object models with many object-to-object relationships are expensive to instantiate, traverse, process, or distribute. We sometimes have to compromise a model’s purity for a simpler and more tractable model to aid system performance. I/O. Performing I/O, whether disk or network, is typically the most expensive operation in a system. Pare down I/O activities to the bare minimum. Consider buffering schemes that collect data and do a single I/O operation as opposed to many. Transactions. Transactions use costly resources. Applications should work outside of transactions whenever feasible and go into and out of each transaction in the shortest time possible.
Strategies for achieving scalability Strategies are high-level applications of one or more design principles. They are not design patterns but rather entities that encompass a class of patterns. Given a strat36
IEEE SOFTWARE
March/April 2002
egy, we can articulate multiple patterns that embody its semantics. Careful system partitioning A scalable system’s most notable characteristic is its ability to balance load. As the system scales up to meet demand, it should do so by optimally distributing resource utilization. For Java-based Internet systems, load balancing maps to a well-distributed virtual machine (VM) workload from Web client hits. For other systems, the workload is distributed among operating system processes. Partitioning breaks system software into domain components with well-bounded functionality and a clear interface. In object terms, a domain component is “a set of classes that collaborate among themselves to support a cohesive set of contracts that you can consider black boxes.”1 Each component defines part of the architectural conceptual model and a group of functional blocks and connectors.2 Ultimately, the goal is to partition the solution space into appropriate domain components that map onto the system topology in a scalable manner. Principles to apply in this strategy include D&C, asynchrony, encapsulation, and concurrency. Service-based layered architecture During design, a service-oriented perspective facilitates the definition of appropriate components and data-sharing strategies. A service encapsulates a subset of the application domain into a domain component and provides clients contractual access to it. By making services available to a client’s application layer, service-based architectures (see Figure 1) offer an opportunity to share a single component across many different systems. Having a services front end lets a component offer different access rights and visibility to different clients. In a sense, services do for components what interfaces do for objects. Domain
components are shared by different remote systems, each having its own contractual view of the component. This paradigm is helpful in Web-enabled enterprise application integration (EAI) solutions.3 A service-based architecture not only aids scalability by statically assisting in proper component design; it also offers dynamic benefits. For example, Enterprise JavaBeans’ stateless session beans help implement a service layer that supports dynamic scalability by enabling multiplexed access from different clients—through bean implementation sharing (see http://java.sun.com/products/ejb/ docs.html). We would gain further benefits if we pooled stateless session bean interfaces in the Web server tier. Each interface would be associated with a preactivated bean implementation living in a middle-tier VM. We could then use these interfaces to load balance across the VMs. Principles to apply in this strategy include D&C and encapsulation. Just-enough data distribution The primary principle for object distribution is parsimony: distribute out as little data as possible. For an object to be distributed outward, it must be serialized and passed through memory or over a network. This involves three system resources: ■
■
■
CPU utilization and memory in the server to serialize the object and possibly packetize it for travel across the network Network bandwidth or interprocess communication activity to actually transmit to the receiver CPU utilization and memory in the receiver to (possibly) unpacketize, deserialize, and reconstruct the object graph
Hence, an object’s movement from server to receiver comes at a fairly high cost. There are two benefits of just-enough data distribution: it diminishes the bandwidth needed to flow data through the system, and it lessens the amount of data a process contains at any particular point in time. Let’s step back and look at the big picture. Suppose a large e-commerce application services 10,000 concurrent users. Each user must receive data. Now suppose the amount of information sent to a client increases by 10 Kbytes per hit. The total
amount of additional information the system would thus have to send to service all its clients would increase by 100 Mbytes. Because large-scale systems serve many users, relatively small increases in the amount of data sent to an individual client are magnified thousands of times. Seemingly small increases can have a significant impact on total system throughput. There are quite a few guidelines for keeping just the right amount of data in the right place at the right time. The next several paragraphs describe some techniques. Use lazy initialization strategies to fetch only the frequently used fields when an object is first instantiated and initialized. Lazy initialization schemes can save significant database querying, but a designer must understand what fields are most commonly used in a class to devise a good scheme. Be forewarned that too much lazy initialization is a bad thing. A design must balance the costs of bringing more data over at object initialization time against the need to go back to the database again and again to fetch more fields. Use state holders to pass requested data from the back end to the presentation layer. State holders represent a flattening of the object graph into one object containing all the pertinent data for a particular business case. A service-based layered architecture supports this concept well because services tend to be associated with subsets of data from domain model graphs. In C and C++, data is commonly passed around by reference because having a shared memory view is easy. VMs have private memory, and Java does not offer pointer functionality directly, so application data tends to be passed by value. Current Java technologies offer a shared memory view across VMs, and some offer transactional access to shared data and even persistence in native object format. This kind of technology can help optimize performance. If direct reference is not possible, use a key system for passing data identity. Keys can, for example, define catalog data associated with a Web page. Instead of sending the whole catalog to the user’s browser, an initial view is sent and follow-up views are populated as necessary based on returned request keys. Sharing read-only data can significantly improve scalability by cutting down on database queries and subsequent I/O. In a Java 2
A servicebased architecture not only aids scalability by statically assisting in proper component design: it also offers dynamic benefits.
March/April 2002
IEEE SOFTWARE
37
The world is moving in the direction of alerting and notification, which are push-based paradigms. Consequently, even transactional requests are increasingly handled asynchronously.
Enterprise Edition application, many users might execute in the same VM. Each user can avoid acquiring its own copy of data by sharing read-only copies across all users. Do not underestimate the impact of object distribution. In our experience, it is often the primary determinant of system viability in distributed systems. Principles that apply in this strategy include parsimony and D&C. Pooling and multiplexing Pooling is an effective way to share and reuse resources, which can be expensive in terms of memory usage (for example, large object graphs) or overhead (such as object instantiation, remote object activation, and relational database [RDB] connections). Initialization for remotely accessed resources is especially expensive because various protocols at different layers come into play. Furthermore, these resources have hard limits (such as the availability of ports) and scalability constraints on the remote server software itself. Pooling in general and multiplexing connections in particular are solutions that optimize resource sharing and reuse. Some examples of resources that might be pooled include Java database connectivity (JDBC) connections, RDB connections, buffers, EJBs, and ports. Multiplexing lets many actors share one resource. The paradigm is especially valuable when accessing back-end systems using JDBC connections or the like. Here are some approaches to pooling: ■
■
Manage pool resources dynamically, creating resources on an as-needed basis and letting resources be reaped after a set time. One option is to use a background process to wake up at preset epochs and remove unused resources. The pool can then dynamically meet changing system load requirements throughout the day. Always test for resource validity before use when managing resources whose life cycle you do not fully control.
The main principle that applies in pooling is parsimony. Queuing work for background processes Queuing lets a foreground resource serv38
IEEE SOFTWARE
March/April 2002
ing an interactive user delegate work to a background process, which makes the foreground resource more responsive to the user. Queuing can also permit work prioritization. Data warehouse searches, object cache synchronization with a back-end RDB, and message-broker-based EAI integration are all examples of candidates for asynchronous decoupled interaction. If a designer uses a priority scheme, he or she can map it to various queues to increase throughput by using concurrent dispatching. Careful management of the number of queues and their relative priority can enhance scalability. Queue content dispatched to domain components in other processes, perhaps using a messaging subsystem, affords an opportunity for load balancing. Designers can accomplish load balancing by ■
■
■
Replicating functionality into various processes and apportioning the number of queued requests sent to each process, possibly with a weighting factor for each request based on its memory, I/O, and computational resource usage Partitioning functionality into various processes and apportioning according to type the queued requests sent to each process, again possibly with a weighting factor Combining these schemes to apportion requests according to type and number
The key here is to always try to tease apart a design issue so that it can be handled asynchronously. Resist the trap of assuming everything must be synchronous. Consider that even customer access from a Web browser does not necessarily require synchronous response (response at the application level, not the HTTP level). For example, an online customer need not necessarily wait for credit card approval. A designer could set up a merchandise payment use case so that an email message is sent with the purchase confirmation sometime after the purchase. The world is moving in the direction of alerting and notification, which are push-based paradigms. Consequently, even transactional requests are increasingly handled asynchronously. Often it makes sense to run queued jobs on a scheduled basis. In this kind of queuebased batching, a background process is
scheduled to wake up during nonpeak production system hours and service all outstanding requests. This enhances scalability by spreading system usage across time. Principles to apply in this strategy include D&C, asynchrony, and concurrency. Near real-time synchronization of data It is common for designers to assume that in Web applications transactional changes must be instantly reflected in all federated databases of record. The problem is that synchronous distributed transactions are costly. Solutions such as two-phase commits increase network load, create multiple points of failure, and generate multiple wait states where the transaction time is bounded by the slowest system. Transactions rarely have to be synchronously distributed across all involved systems. For example, a data warehouse update can typically withstand some transactional delay, because it is not part of a realtime business process. In this case, the delay could be on the order of a few seconds (or more), but even critical systems might be able to handle synchronization latencies of a few hundred milliseconds. Near real-time synchronization assists scalability by spreading the system’s transactional load across time. In converting real-time synchronous distributed transactions into near real-time asynchronous ones, the best recourse is to choose a single database as the primary database of record. This primary database serves as the synchronization resource for all others.4 Principles that apply in this strategy include D&C, asynchrony, and concurrency. Distributed session tracking Generally, session tracking is maintained on a Web server either through cookies or by server-specific internal mechanisms. This limits load balancing across servers because clients must always be routed back through the same server so that their session state is available. Routing users to any Web server on a request-by-request basis is preferable because HTTP is designed to optimize this kind of resource usage. One architectural solution, called distributed session tracking, places the shared view of session state in a persistent store visible to all Web servers. A client can be routed to any server, and the session state will still be available. Typically,
the persistent store is an RDB. Distributed session tracking results in better load balancing, but it comes at a cost. If a designer uses the same RDB system for session tracking and business-related transactions, the load on the RDB increases considerably. There is also the overhead of having to object-to-relational map session state. A better solution for implementing distributed session tracking is to use a secondary lightweight RDB, an object database (ODB), or, better yet, an object cache with shared visibility across VMs. Principles that apply in this strategy include D&C and concurrency.
End-user satisfaction with a site is highly correlated with the speed at which information is rendered.
Intelligent Web site load distribution Perhaps the prime characterization of an Internet application is the requirement to transport files of all kinds around the network in an efficient manner. End-user satisfaction with a site is highly correlated with the speed at which information is rendered. However, rendering covers a whole plethora of possibilities—simple HTML pages, pictures, streaming audio or video, and so on. A well-designed Web site can handle many concurrent requests for simple HTML files, but entities such as streaming video involve much larger demands. For a busy Web site, it could be literally impossible to handle peak loads in a reasonable manner. The solution is to replicate these static, highbandwidth resources to better manage the load. Incoming HTTP requests redirect to the mirrored facilities based on some combination of available server and network capacity. This can be accomplished internally or by subscribing to one of the commercial providers who specialize in this type of service. Principles that apply in this strategy include D&C, asynchrony, and concurrency. Keep it simple Donald A. Norman warns in the preface to The Design of Everyday Things, “Rule of thumb: if you think something is clever and sophisticated, beware—it is probably self-indulgence.”5 The software development cycle’s elaboration phase is an iterative endeavor, and customer requirements need constant reevaluation. Complicated solutions make refactoring harder. In accordance with Occam’s Razor, when faced with two design approaches, choose the simpler one. If the March/April 2002
IEEE SOFTWARE
39
ear: it is iterative, recursive, and sometimes anticipatory. Consequently, design does not progress through the following steps in a linear fashion. Rather it loops and cycles through them (see Figure 2).
Design conceptual view
Map basic topology
<<synchronize>>
Define domain components <
> <>
Scale domain components
Scale topology
Tune system
Figure 2. The scalability design process. To simplify the diagram, we chose stereotypes instead of branches. The stereotypes <> and <> should be clear from the main text. The <<synchronize>> stereotype guarantees that new domain component functionality translates back to the conceptual view.
simpler solution proves inadequate to the purpose, consider the more complicated counterpart in the project’s later stages. Putting it all together Although the complete mapping of a problem domain into a solution space is beyond this article’s scope, this section offers up a process for system partitioning. Each step in the process uses strategies presented in the previous sections. The nature of design is decidedly nonlin40
IEEE SOFTWARE
March/April 2002
Design conceptual view Create a conceptual view of the architecture, defining appropriate domain-specific functional blocks and connectors that follow good responsibility-driven design principles.6 This breaks down a solution space into functional components with an encapsulated set of responsibilities, appropriate communications protocols, and connectors to manage those protocols. Map basic topology Map the conceptual view into an initial system topology. This exposes all system stakeholders in the architecture—legacy systems, data sources, server hardware, middleware, and so on. Identify the role each system plays in the overall design and its responsibilities, needs, and constraints. For legacy systems, identify usage patterns. In keeping with the KISS principle, start with a minimal setup, such as one VM or process per tier, one Web server, and so on. Identify systems that are candidates for using replication, pooling, data-partitioning schemes, and so forth. Define domain components For each system in your topology, group functional blocks into domain components. It is at this stage that a designer should consider existing layering paradigms to assist in the grouping. In particular, consider a service-based layered architecture.7 Also, at this point, third-party software components should be considered because they present previously defined interfaces and usage constraints. Domain components should also be designed to best use available standards, so match a domain component with a standard’s APIs. In terms of initial scalability concerns, group the functional blocks initially to minimize communication bandwidth and coupling. Check created domain component candidates for possible queued background processing or, possibly, batched processing. Identify transaction flow between domain components and, whenever possible, avoid distributed transactions.
About the Authors Scale domain components Now relax the single VM or process per tier constraint. Refactor the domain components to optimize for data distribution and resource use. This might mean redefining functional blocks or their grouping into new domain components, placing different domain components in different processes, or replicating domain components into two or more processes. Load-balance work among the processes, and benchmark and measure process memory, CPU usage, and I/O bandwidth to identify well-balanced configurations. Analyze where some I/O-bound or computationally intensive domain components should have their own processes. Balance refactoring metrics against communication bandwidth between processes. Interprocess communication should not cause a huge performance hit compared to the single process phase (at some point, the price of communication outweighs the advantages of domain component distribution). Identify candidates for multiplexed resources. Keep an eye on the back end and see how it plays transactionally across your processes. Are transactional deadlines being met? Are resources across processes participating correctly in each transactional use case? Scale topology Now move back to your initial system topology definition and double the number of systems that you qualified as good candidates in mapping a basic topology. At this point, it might be necessary to incorporate some hardware routing scheme or some new component such as a Java messaging system queue. Replicating systems such as those contained in Web servers most likely will lead to replication of associated processes. For Web server replication in particular, consider distributed session tracking as an option for load balancing. Tune system Finally, based on previous refactoring observations, increase the number of qualified systems (domain servers, content servers, Web servers, databases of record, and so forth) and domain components to meet your production load demand. Ask how the current system and domain component distribution should be refactored to handle future load demand increases. At this point, the application space is partitioned in a scal-
Colleen Roe is chief architect at GemStone Systems and has been designing and developing software systems since kindergarten (or at least that’s how it feels). She has experience in several industry sectors including telecommunications, oil, manufacturing, software, and pharmaceuticals, and has developed everything from embedded systems to expert systems. Her current research focus is scalable architectures for large e-commerce J2EE-based systems. Contact her at GemStone Systems, 1260 NW Waterhouse Ave., Ste. 200, Beaverton, OR 97006; [email protected].
Sergio Gonik is a lead architect at GemStone Systems, where he currently focuses on next-generation large-scale e-business distributed systems. He has 13 years of experience in the field and has designed diverse software architectures for scientific, embedded, medical, music, and J2EE e-commerce systems. He is also a professional composer and performer who has written and executed musical pieces for theater, dance, and film. Contact him at GemStone Systems, 1260 NW Waterhouse Ave., Ste. 200, Beaverton, OR 97006; [email protected].
able manner, so there should be a clear path to handling load increases. If no such clear path exists, restart the process by redefining the architectural functional blocks or their grouping into domain components.
T
his article addresses many strategies that are important when designing scalable architectures, but our list is not exhaustive. Rather, it represents what we believe to be the most critical strategies for server-side scalability. Unfortunately, space limitations made it impossible to include an actual example of a well-designed scalable system built on these principles. The principles are important guidelines, but they are not a substitute for measurement. Throughout a system’s design and implementation, testing and benchmarking what you’re building to ensure that desired scalability is achieved is important. Software projects should always follow a motto— “benchmark early and benchmark often.” Acknowledgments We thank Anita Osterhaug and John Cribbs for their insightful comments and suggestions. We never would have made it without them.
References 1. S.W. Ambler, “Distributed Object Design,” The Unified Process: Elaboration Phase, R&D Books, Lawrence, Kan., 2000. 2. C. Hofmeister, R. Nord, and D. Soni, Applied Software Architecture, Addison-Wesley, Reading, Mass., 2000. 3. D.S. Linthicum, Enterprise Application Integration, Addison-Wesley, Reading, Mass., 2000. 4. C. Britton, IT Architectures and Middleware: Strategies for Building Large Integrated Systems, Addison-Wesley, Reading, Mass., 2001. 5. D. Norman, The Design of Everyday Things, Doubleday, New York, 1988. 6. R. Wirfs-Brock, B. Wilkerson, and L. Wiener, Designing Object-Oriented Software, Prentice Hall, Englewood Cliffs, N.J., 1990. 7. GemStone A3Team, “I-Commerce Design Issues and Solutions,” GemStone Developer Guide, GemStone Systems, Beaverton, Ore., 2000.
March/April 2002
IEEE SOFTWARE
41
focus
engineering Internet software
Usage-Centered Engineering for Web Applications Larry L. Constantine and Lucy A.D. Lockwood, Constantine & Lockwood
espite breathless declarations that the Web represents a new paradigm defined by new rules, professional developers are realizing that lessons learned in the pre-Internet days of software development still apply. Web pages are user interfaces, HTML programming is programming, and browser-deployed applications are software systems that can benefit from basic software engineering principles.
D
This model-driven approach to software engineering, focusing on user interface design and usability, proves especially appropriate for Web applications. 42
IEEE SOFTWARE
Nevertheless, development for the Web faces difficulties that standard development does not. Three challenges come to the fore. First, the Web development process must be highly accelerated—“Web time” has become synonymous with headlong desperation and virtually impossible deadlines. Second, many Web projects are “green-field” applications, which means they have few if any standards or precedents. Finally, much more so than standard software, Web applications must focus on the user experience. Systematic, disciplined practices might help developers face these challenges, but traditional approaches proliferate documents and deliverables and emphasize analysis of established practices—a poor match for novel applications with Web-time delivery schedules. Furthermore, user interface design and usability are generally weak points in both the heavyweight software en-
March/April 2002
gineering processes, such as the Unified Process,1 and the lightweight competitors, such as Extreme Programming.2 This article describes a flexible, model-driven approach for engineering Web applications that succeeds through a focus on user interface design and usability. Its simple, model-driven techniques work well for novel applications and integrate readily with lightweight or agile development processes3 under compressed development schedules. Web applications for use Usability and the user experience are emerging as critical determinants of success in Web applications. If customers can’t find what they’re looking for, they can’t buy it; a site that buries key information impairs business decision making. Poorly designed interfaces increase user errors, which can be costly. Mistakes made entering credit card billing information, for example, can re0740-7459/02/$17.00 © 2002 IEEE
Table 1 User-centered and usage-centered design: A comparison quire costly manual follow-up or lead to lost sales. Web-usability guru Jakob Nielsen estimates that billions of dollars in lost Web sales can be traced to usability problems. Technical and customer support costs in all areas of business are skyrocketing. Every time usability problems on a site or application prompt a telephone call or an email message to customer service, an inexpensive Web session becomes an expensive support incident. On corporate intranets, hard-touse applications require extensive training or go completely unused. Although this article focuses on Webbased applications—systems that deploy functional capability on the Web through a browser instance as a thin client—it is difficult to draw a hard and fast line separating Web-based applications from those Web sites that are intended for use, that is, for something more than entertainment, “branding,” or the like. The common thread is the delivery of useful services and capabilities over the Web. In addition to usability, aesthetic design aspects also figure in the user experience. Unfortunately, graphic design and aesthetic considerations are often established early and drive the entire development process at the expense of usability. For example, based on an “aesthetic brief” approved by a client’s CEO, a prominent graphic design house developed an innovative Web site for a major analyst group. Although the designers consulted extensively with the firm’s customers and conducted numerous marketoriented focus groups, the design proved a usability disaster, in no small part because of the aesthetic design. Last-ditch efforts to make improvements without modifying the basic graphic design enabled the site to go live but ultimately proved merely palliative and insufficient to satisfy customers. In the end, the firm redesigned the site completely, this time approaching the project with usage-centered design.4 Usage-centered design Usage-centered design uses abstract models to systematically design the smallest, simplest system that fully and directly supports all the tasks users need to accomplish. Developed in the early 1990s, the approach has been applied to systems ranging from industrial automation systems and consumer elec-
User-centered design
Usage-centered design
Focus is on users: User experience and user satisfaction Driven by user input Substantial user involvement • User studies • Participatory design • User feedback • User testing Design by iterative prototyping Highly varied, informal, or unspecified processes Design by trial and error, evolution
Focus is on usage: Improved tools supporting task accomplishment Driven by models and modeling Selective user involvement • Exploratory modeling • Model validation • Usability inspections Design by modeling Systematic, fully specified process Design by engineering
tronics to banking and insurance applications. The streamlined process, driven by simple models, scales readily; developers have used it on projects ranging from a few person-months to the five-designer, 19developer, 23-month project that produced Step7lite, a sophisticated integrated development environment from Siemens AG (see http://foruse.com/pcd). On the Web, groups have employed usage-centered design successfully for applications in fields including ecommerce, membership support, education, and medical informatics.5 User-centered or usage-centered? Usage-centered design evolved as a software engineering alternative to user-centered design. Table 1 summarizes some of the salient differences. User-centered design is a loose collection of human-factors techniques united under a philosophy of understanding users and involving them in design. It relies primarily on three techniques: user studies to identify users’ desires, rapid paper prototyping for user feedback on user interface design iterations, and usability testing to identify problems in working prototypes or systems. Although helpful, none of these techniques can replace good design. User studies can easily confuse what users want with what they truly need. Rapid iterative prototyping can often be a sloppy substitute for thoughtful and systematic design. Most importantly, usability testing is a relatively inefficient way to find problems you can avoid through proper design.4,6 Driving models. Role, task, and content models—simple, closely related abstract modMarch/April 2002
IEEE SOFTWARE
43
Withdraw Money The use case begins when the client inserts an ATM card. The system reads and validates the information on the card. 1. System prompts for PIN. The client enters PIN. The system validates the PIN. 2. System asks which operation the client wishes to perform. Client selects “Cash withdrawal.” 3. System requests amounts [sic]. Client enters amount. 4. System requests type. Client selects account type (checking, savings, credit). 5. The system communicates with the ATM network to validate account ID, PIN, and availability of the amount requested. 6. The system asks the client whether he or she wants a receipt. This step is performed only if there is paper left to print the receipt. 7. System asks the client to withdraw the card. Client withdraws card. (This is a security measure to ensure that Clients do not leave their cards in the machine.) 8. System dispenses the requested amount of cash. 9. System prints receipt. 10. The use case ends.
Figure 1. Conventional use case for getting cash from an ATM.9
Figure 2. Abstract use case in essential form—the “getting cash” task case.
els—drive usage-centered design. The role model captures the salient characteristics of the roles that users play in relation to a system. The task model represents the structure of the work users need to accomplish with the system. The content model represents the user interface’s contents and organization. A user role represents one kind of relationship users could have with a system. Among numerous possible aspects of this relationship are the purpose and frequency of interaction, the volume and direction of information exchange, and the attitude toward the system of users in the role. A task model comprises a set of task cases 44
IEEE SOFTWARE
March/April 2002
and a map of the interrelationships among those task cases. Task cases are a form of use cases,7 which conventionally are models of systems—namely, the “sequence of actions a system performs that yields an observable result to a particular actor.”1 As commonly written, use cases express the concrete actions and responses taken by the system and an actor with which it interacts. Figure 1, a published example of a conventional use case,1 tellingly omits the user’s single most important step: actually taking the cash. Task cases—also called essential use cases4—are essential models8 that are abstract, simplified, and technology-free—that is, they have no built-in assumptions about the user interface. For example, the task case of Figure 2, written in the style used in agile usage-centered design, is shorter, simpler, and closer to the essence of the user task than that of Figure 1. The advantages of essential use cases are widely recognized, and a consensus is building that this style is best for both user interface design and requirements modeling in general.9 The third model, the content model,4 is sometimes called an abstract prototype: It abstractly represents the user interface’s contents—independent of actual appearance or behavior—and how these are organized into the contexts within which users interact, called interaction contexts. Abstract prototypes can range from a simple inventory of a site’s or application’s accessible contents to a highly structured abstract model based on a canonical set of abstract components (see http://foruse.com/articles/ canonical.pdf). Regardless of the form, abstract prototypes are a bridge between task models and realistic or representational prototypes, such as conventional paper prototypes or design sketches. By adhering to a user perspective and leaving open the various implementation possibilities, abstraction encourages innovative visual and interaction designs.4,10 Process overview. Figure 3 shows a logical
overview of usage-centered design. The process separates system actors—other software and hardware systems—from human actors who are users. The role, task, and content models are developed in coordination with and interlinked with other models. These include a domain model (such as a
Task cases User roles Actors
Abstract prototypes Behavior Step1
Step2
Figure 3. Logical process of usage-centered design.
1. 1.Loquom Asdhf asdf ipso 2.Wertw rt bzc 3.Ouiaa ero
Visual and interaction design
System actors
glossary or another more elaborate data model), a business rules model that embodies the application’s underlying logic and constraints,11 and an operational model that captures salient aspects of the working environment or operational context.3 Conceptually, a straightforward and direct derivation links the final design back to task cases supporting user roles. Each page, form, or other interaction context corresponds to an abstract prototype supporting a cluster of interrelated task cases. The designer derives the actual buttons, links, tables, displays, and other features directly from abstract components that realize specific steps within supported task cases. These task cases, in turn, support the roles that users can perform. Usage-centered Web design Over successive Web projects, usagecentered design has evolved a lightweight form that takes certain modeling and design shortcuts (see http://foruse.com/articles/ agiledesign.pdf). Agile usage-centered design employs rapid-fire modeling techniques using ordinary index cards for brainstorming, sorting, and clustering,12 as in Extreme Programming.2 When practical, collaborative modeling with end users, application domain experts, and clients can save additional time. Figure 4 outlines an agile usagecentered design process. Essential purpose and preconception. Web
projects often suffer from vague objectives and unrealistic ambitions. Early clarification of objectives and exploration of fantasies can pay off enormously.13 We prefer
to kick off the process by involving managers, users, developers, and other stakeholders in framing the application’s purposes as external users and from a business perspective. The group can brainstorm onto cards and sort them to establish priorities. Similarly, we encourage participants to share their fantasies about the system’s features, functions, facilities, content, and capabilities, but we clearly label these ideas preconceptions and set them aside to be treated as negotiable fantasies rather than requirements. Exploratory modeling. Exploratory modeling jumpstarts the design process by identifying questions and areas of uncertainty or ambiguity in user requirements. The team does this by sketching out rough versions of the role and task models that it will develop in more refined form later. In this way, the team can identify needed, but missing, information. User interface architecture. Many agile processes3 abjure up-front design in favor of a more evolutionary approach—constructing a limited system and then elaborating it through successive iterations. Unfortunately, when code starts small and gradually grows with expanding requirements, the original architecture—the code’s underlying organization—often proves insufficient or inappropriate to support emerging needs. Refactoring,14 in which existing code is reorganized and rewritten to fit evolving and emerging requirements, is a key to successful evolution of complex software systems through iterative expansion. March/April 2002
IEEE SOFTWARE
45
Preliminary steps 1. Essential purpose and preconception—Clarify business and user purposes, then fantasize and set aside preconceptions of features, facilities, content, and capabilities. 2. Exploratory modeling—Identify questions, ambiguities, and areas of risk and uncertainty. First iteration 3. Role modeling—Inventory and prioritize all user roles and select initially targeted subset. 4. Task modeling—Inventory and prioritize all tasks and select initially targeted subset. 5. Task clustering—Group all tasks by affinity and draft overall navigation architecture. 6. Design—Draft visual and interaction scheme; review and revise aesthetic design. 7. Abstract prototyping—Develop content model for interaction contexts supporting selected subset of tasks. 8. Design—Develop detailed user interface design for selected interaction contexts. 9. Construction—Program designed portions of user interface. Successive iterations (similarly) 3a. Review role model and revise as needed, select next subset. 4a. Review task model and revise as needed, select next subset. 5a. Review navigation architecture and revise as needed. 6a. Review visual and interaction scheme and revise as needed. 7a. Develop content model for interaction contexts supporting next selected task subset. 8a. Design user interface for selected interaction contexts. 9a. Construct newly designed portions. Figure 4. Rough outline of steps in an agile usage-centered design process.
User interfaces are a different story. Late refinement of the user interface’s basic structure is not acceptable because it changes the system for users who have already learned or mastered an earlier version. Whereas refactored internal software components don’t necessarily affect users, a redesigned user interface architecture is unavoidably disruptive. For highly usable user interfaces, we design the overall organization, navigation, and look and feel to fit the full panoply of tasks to be covered. You don’t have to design every aspect of the user interface in advance, however. To avoid radical refactoring or inconsistency across design increments, you need to work out a well-conceived overall map of the site or application in advance.15 To insure that the basic visual style and be46
IEEE SOFTWARE
March/April 2002
havior are effective across the entire application, you must devise a comprehensive design scheme. Navigation architecture specifies how the overall user interface breaks down into interaction contexts, collections, and groups; how these are presented to users; and how users navigate them. Minimally, the navigation architecture identifies all the interaction contexts and their interconnections, but having more is advantageous. For example, the navigation architecture might organize parts of the user interface into tabbed notebooks—accessed through a set of command buttons on a central dispatch dialog—with sections within notebook pages reached through shortcuts in a menu-like bar across the top of each page. Another crucial facet of overall user interface design is the visual and interaction scheme, a sort of abstract style guide. It briefly describes the basic visual elements that will recur throughout the design as well as the common layouts, visual arrangements, or templates that apply to various interaction contexts.16 The scheme might, for example, specify a basic layout grid with a modified tree-view in the left-most column for primary navigation and a set of view controls across the top for secondary navigation. At a more detailed level, it might specify that a distinct color should identify all editable fields, which can be edited in place by double clicking. It might further spell out how certain collections of controls will be placed on slide-out tool panels that automatically open on mouse-over and close after use. Clearly, a sound navigation architecture and visual and interaction scheme requires a complete task model that includes all identified tasks, not just those to be tackled on the first or early iterations. Card-based modeling. The initial user role
model is a simple inventory of roles users can play in relation to the site or application. One of the quickest ways to develop such an inventory is to brainstorm it directly onto index cards. Once we’ve agreed on the initial inventory, we succinctly describe roles directly on their index cards using the characteristics most relevant to the design problem: the role’s context, characteristic patterns of interaction within the
role, and special support criteria. We then sort the completed cards to rank them by overall importance. After reviewing the roles in order of importance, we brainstorm an inventory of task cases, again onto index cards. As we review and refine this inventory—condensing, combining, eliminating, or adding tasks— we standardize the names to convey the basic purpose from a user’s perspective, such as “finding a replacement part for a specific product” or “adding an assessment to a lesson plan.” Once we’ve established the initial set of task cases, we sort the cards to rank them for expected frequency and for overall importance. Based on these two rankings, we deal them into three piles: required (do first), desired (do if there’s time), and deferred (do next time). At this point, we start to fill in the details, but not for all task cases. We skip task cases for which the interaction appears obvious or routine. For task cases that are critical, complex, unclear, or interesting, we write the interaction narrative on the index card. Working on small cards favors good modeling discipline. If the process narrative doesn’t fit on one card, either the narrative is not sufficiently abstract and simplified, or the card really covers multiple task cases that we must identify. Using all the knowledge built from creating, refining, and interacting with the task cases, we group the cards into affinity clusters on the basis of how strongly they seem to be related—particularly, how likely users in a role will perform tasks together. Each cluster represents a set of capabilities that the application must offer together—within the same page, form, or browser window, or at least within a set of closely connected interaction contexts. Each task cluster becomes the guide for designing a part of the user interface. We prefer to construct abstract prototypes but under pressure could move directly into sketching realistic paper prototypes. We then evaluate the paper prototypes, augmented by a description of how the various elements behave, to identify usability problems and areas for improvement. For speed and convenience, we prefer collaborative usability inspections,3 involving users and clients as well as designers and developers, but recognize the effectiveness of heuristic
inspections and other techniques. In practice The approach we took to designing a browser-resident classroom information management system illustrates the ad hoc accommodations an agile process often needs for development in Web time. A sophisticated performance-support system to empower K–12 classroom teachers, this system simplified key administrative, planning, and teaching tasks in an environment fully integrated with existing administrative systems and software and with Web-based learning resources. The application was deployed through a Web-based technology with the client-side user interface realized in HTML, XML, and Java JFC/Swing. Risk factors were numerous, including a new and unproven development team in a new company, an outside consulting team for user interface design, the need for technical breakthroughs in several areas, vague and ambiguous requirements exacerbated by frequent changes in scope, and management obsessed with early delivery, frequent demonstrations, and unpredictable changes of direction. The design team collaborated with a team of PhD-level educators with extensive classroom experience. This education team served as both domain experts and representative end users, while also functioning in what amounted to an XP-style customer role.2 Collaboratively with the education team, the design team developed the initial user role model and inventory of task cases. In total, we identified and prioritized 14 user roles. The initial inventory of 142 task cases included 35 task cases related to lesson planning, which emerged as a critical capability for the system. We wrote process narratives for only a handful of interesting task cases.1 We constructed a draft navigation map with just over 40 interaction contexts, based on task case clustering (see Figure 5). To resolve problems with the design and omissions, irregularities, and ambiguities in the requirements, we held frequent meetings and consulted continually with the education team—a pattern we now refer to as JITR, just-in-time requirements. One of the two designers concentrated on requirements and scope issues, while the other began working on the initial design.
Each task cluster becomes the guide for designing a part of the user interface.
March/April 2002
IEEE SOFTWARE
47
Figure 5. Webdeployed classroom information management system: Main portion of the navigation map.
48
IEEE SOFTWARE
The design team devised drafts of a navigation architecture, navigation map, and visual and interaction scheme, validated them with the education team, then revised them. Not entirely by plan, the design proceeded in two- to two-and-a-half-week cycles. Once full-time design work started, we completed an initial navigation architecture and visual and interaction scheme within the first cycle. We finished the first version of the complete visual and interaction design in the next cycle, after which we inspected it for usability defects, and we undertook a major design revision on the second iteration. In the third iteration, we continued to extend and refine the design, spread over the remainder of the project’s 17-week usage-centered design portion. Programming followed an agile, lightweight process that proceeded in parallel with visual and interaction design. We needed a design that would be immediately usable by ordinary teachers
March/April 2002
with little or no special training. It had to be flexible enough to accommodate a wide range of working styles and efficient to use, given that teachers may typically have less than 15 minutes a day to plan an entire day’s lessons. Technical goals that we identified based on the usability criteria included ■ ■ ■ ■
Minimize window management overhead. Minimize modal behavior. Allow simple and direct switching among contexts. Maximize available screen real estate for document and data display.
A discussion of the complete design is beyond this article’s scope (for details, see our design studies on the Web, www. foruse.com/articles/designstudy1.pdf, /designstudy2.pdf, and /designstudy3.pdf). However, the first design iteration’s results clearly illustrate the importance of early,
up-front consideration of the overall navigation architecture and the visual and interaction scheme based on a full understanding of user roles and tasks. Neither traditional software engineering practices nor the newer lightweight methods make this possible. Figure 5 shows the main portion of the navigation map, as finalized in the second iteration. The mock-up in Figure 6, adapted directly from actual design documents produced in the project’s first cycle, illustrates how we documented the overall navigation and visual and interaction design scheme. Together, these design documents specified much of the user interface’s overall architecture. Understanding the various roles classroom teachers play and thoroughly exploring their tasks revealed the importance of a fast, flexible, and easy-to-learn scheme for navigating the application’s various working contexts. Once we understood the task structure, we organized the various groups and interaction contexts for single-click switching among tasks within any given activity. Using simple visual metaphors based on thumb tabs, hanging folders, and file folders, we devised a three-level visible hierarchy that proved far easier to learn and
use than a conventional Windows-style tree view. Screen real estate, always a scarce resource, is particularly valuable in an application running within a browser instance on low- to moderate-resolution displays. To address this, the design scheme included components that would expand and contract or reveal and hide themselves automatically as needed. For example, the toplevel navigation panel (containing thumb tabs) contracts to provide more working space once the user selects a starting point. Figure 6 shows another example of screen space conservation in the dynamic workspace, which anticipated rather than mimicked the Apple OS-X dock. The workspace provides a compact holding bin that lets the user gather various documents or fragments from a variety of personal and public resources for assembly into a lesson plan or assignment. Any product design’s success is ultimately measured by its acceptance. Classroom teachers in a variety of settings made immediate use of this application with only minimal instruction. In fact, we were able to achieve the stringent design objective of enabling immediate, productive use of the system based on a single-page tutorial.
Figure 6. Navigation architecture and design scheme, classroom information management system.
March/April 2002
IEEE SOFTWARE
49
Further Reading L. Constantine, Constantine on Peopleware, Prentice Hall, Upper Saddle River, N.J., 2001. R. Jeffries, A. Anderson, and C. Hendrickson, Extreme Programming Installed, Addison-Wesley, Reading, Mass., 2001. J. Nielsen and R.L. Mack, eds., Usability Inspection Methods, John Wiley & Sons, New York, 1994.
O
ur experiences in agile usage-centered design have made clear that improved usability does not come without cost or risk, even when the methods are streamlined and the schedule is compressed. Special training and skills are necessary, and these may not be available to every development group, especially the lean teams typical in agile development. Moreover, the best design is easily corrupted by casual or even well-intentioned alterations and elaborations during coding. Close coordination between user interface designers and programmers is essential, and programmers must be fully committed to the process and convinced of the benefits even if they do not understand every design decision. A flexible and condensed usage-centered design process is a good starting point for collaboration, but for agile practices such as Extreme Programming, which abjures anything resembling BDUF (big design up front), some of the rules and philosophy can get in the way. Just which rules need to be
About the Authors Larry L. Constantine is an adjunct professor of information technology, University of Technology, Sydney, Australia, and director of research, Constantine & Lockwood, Ltd. A methodologist who pioneered fundamental concepts and techniques underlying modern software engineering theory and practice, he now focuses on methods for improving the usability of software and hardware. A consultant and designer with clients around the world, he shared the 2001 Performance-Centered Design award for his work with Siemens AG. He is a graduate of MIT’s Sloan School of Management, a member of the IEEE Computer Society and the ACM, and he has served on the boards of numerous professional journals, including IEEE Software. Contact him at [email protected]. Lucy A.D. Lockwood is president of Constantine & Lockwood, Ltd., the design, training, and consulting firm she cofounded. One of the developers of usage-centered design, she is the coauthor of a Jolt Award-winning book on the subject. With a passion for teaching and professional communication, she has chaired several professional conferences and is co-organizer of forUSE 2002, the first international conference on usage-centered, performance-centered, and task-oriented design. She is a graduate of Tufts University and a member of the IEEE and the ACM. Contact her at [email protected].
50
IEEE SOFTWARE
March/April 2002
bent or rewritten and what philosophy may need to be compromised is the subject of much current debate and experimentation. The outline presented here should be regarded more as a draft than as a definitive model.
References 1. P. Kruchten, The Rational Unified Process: An Introduction. Addison-Wesley, Reading, Mass., 1999. 2. K. Beck, Extreme Programming Explained, AddisonWesley, Reading, Mass., 2000. 3. M. Fowler, “Put Your Processes on a Diet,” Software Development, vol. 8, no. 12, Dec. 2000. Expanded version at www.martinfowler.com/articles/newMethodology.html. 4. L.L. Constantine and L.A.D. Lockwood, Software for Use: A Practical Guide to the Models and Methods of Usage-Centered Design, Addison-Wesley, Reading, Mass., 1999. 5. J. Anderson et al., “Integrating Usability Techniques into Software Development,” IEEE Software, vol. 18, no. 1, Jan./Feb. 2001, pp. 46–53. 6. A. Parush, “Usability Design and Testing,” ACM Interactions, vol. 8, no. 5, Sept./Oct. 2001. 7. I. Jacobson et al., Object-Oriented Software Engineering: A Use Case Driven Approach, Addison-Wesley, Reading, Mass., 1992. 8. S.M. McMenamin and J. Palmer, Essential Systems Analysis, Prentice Hall, Englewood Cliffs, N.J., 1984. 9. L.L. Constantine and L.A.D. Lockwood, “Structure and Style in Use Cases for User Interface Design.” M. van Harmelan, ed., Object Modeling an User Interface Design, Addison Wesley, Reading, Mass., 2001. 10. D. Corlett, “Innovating with OVID,” ACM Interactions, vol. 7, no. 4, July/Aug. 2000. 11. E. Gottesdiener, “Rules Rule: Business Rules as Requirements,” Software Development, vol. 7, no. 12, Dec. 1999. Reprinted in L.L. Constantine, ed., Beyond Chaos: The Expert Edge in Managing Software Development, Addison-Wesley, Reading, Mass., 2001. 12. R. Jeffries, “Card Magic for Managers: Low-Tech Techniques for Design and Decisions,” Software Development, vol. 8, no. 12, Dec. 2000. Reprinted in L.L. Constantine, ed., Beyond Chaos: The Expert Edge in Managing Software Development, Addison-Wesley, Reading, Mass., 2001. 13. L.A.D. Lockwood, “Taming the Wild Web: Business Alignment in Web Development,” Software Development, vol. 7, no. 4, Apr. 1999. Reprinted in L.L. Constantine, ed., Beyond Chaos: The Expert Edge in Managing Software Development, Addison-Wesley, Boston, 2001. 14. M. Fowler, Refactoring: Improving the Design of Existing Code, Addison-Wesley, Reading, Mass., 1999. 15. M.H. Cloyd, “Designing User-Centered Web Applications in Web Time,” IEEE Software, vol. 18, no. 1, Jan./Feb. 2001, pp. 62–69. 16. J. Pokorny, “Static Pages Are Dead: How a Modular Approach Is Changing Interaction Design,” ACM Interactions, vol. 8, no. 5, Sept./Oct. 2001.
For more information on this or any other computing topic, please visit our Digital Library at http://computer.org/publications/dlib.
focus
engineering Internet software
Objects and the Web Alan Knight, Cincom Systems Naci Dai, ObjectLearn
pplying software engineering principles, particularly object-oriented techniques, to the Web can be difficult. Many current Web technologies lend themselves to—or even encourage—bad practices. Scripting and server-page technologies can encourage cutand-paste reuse, direct-to-database coding, and poor factoring. Component models such as the Component Object Model (COM) and Enterprise JavaBeans (EJB) seek to construct building blocks for application assembly, but in doing so they sacrifice many of the advantages of objects. XML emphasizes
A Good design practices are increasingly important in Web development. Here, the authors apply such practices using a framework for layered architectures based on the Smalltalk GUI development pattern of Model-ViewController. 0740-7459/02/$17.00 © 2002 IEEE
technology-independent reuse and sharing of content, data, and messaging but at the expense of encapsulation and the association of behavior with state, which is central to OO. Scripting languages, common on the Web, are often optimized for rapidly creating simple functionality rather than for modular construction of large programs. Also, such languages typically lack the rich development environments of general-purpose languages. Some Web developers even deliberately disregard software engineering principles. They argue that if we’re just writing scripts, they don’t merit the kind of engineering techniques—object or otherwise—we apply to “real” systems. However, software engineering and OO techniques are gaining importance in Web development as Web applications become more complex and integrated with traditional server-side applications. To motivate
the need for these techniques, we examine some representative Web technologies and the issues they present in naive use. We describe a layered, OO architecture, based on the Model-View-Controller (MVC) pattern, which can overcome these issues to produce large, well-structured systems. Motivations If we consider scripts from an OO and layering perspective, the most immediate problem is that a single script has responsibilities spanning several layers (see the “Definitions” sidebar for an explanation of our terminology). The script must ■ ■ ■ ■
Accept input Handle application logic Handle business logic Generate output (presentation logic)
This couples all the layers together, making March/April 2002
IEEE SOFTWARE
51
Definitions Here we define our terminology and goals. In the main text, we present a layered architecture, implemented using both scripts and server pages. Most of these techniques can be applied to any technology in these categories, but where the details of a specific technology are important, we use servlets and JavaServer Pages as representative technologies. Both, while nominally Java specifications, can easily be applied to other languages, and implementations in both Smalltalk and C++ are commercially available.
ing business functionality. This code should be entirely unaware of the presentation being used. We also refer to business objects, which implement the business logic. In a complex application, business logic is likely to be the largest component and can include code that accesses external systems such as databases, external components, and other services. In the ModelView-Controller framework, this corresponds to the model.
Presentation This layer contains code and noncode resources (such as HTML text and images) used to present the application. It typiLayered architecture cally contains little code—code concerned only with formatting A layered architecture is a system containing multiple, and presenting data. An example of this in a Web context is a strongly separated layers, with minimal dependencies and interactions between the layers. Such a system has good separa- server page’s code fragments that print values into a dynamition of concerns, meaning that we can deal with different areas cally generated Web page. In the Model-View-Controller of the application code in isolation, with minimal or no side ef- framework, this corresponds to the view. fects in different layers. By separating the system’s different pieces, we make the software adaptable so that we can easily Scripts Many basic Web technologies can be grouped together in change and enhance it as requirements change. The layers we are concerned with here include input, application logic, busi- the category of scripts—small programs that perform HTTP processing. This term encompasses, among others, compiled CGI ness logic, and presentation. programs, files of scripting language code (such as Perl, Python, Ruby, and VBScript), and Java servlets. While there are Input The input layer contains the code concerned with processing significant differences among these technologies, all of them have the fundamental characteristic of being programs that acand syntactically validating input. In a Web context, this procept an HTTP request and send back a response. In basic uscessing and syntactically validating input includes HTTP input parsing and extracting parameters from an HTTP request. In the age, each script is stateless and independent of all others. An Model-View-Controller framework, this corresponds to the input important distinction within this category is whether scripts can share memory with other scripts (for example, servlets) or are controller. entirely independent (for example, CGI programs). Shared memory allows more effective use of system resources through Application logic pooling, and a simpler programming model through persistent The application logic code is concerned with the applicastates at the cost of a more complex infrastructure. tion’s overall flow. We often refer to it as the glue layer, separating business logic from input and presentation logic and Server pages managing the interface between the two. This requires some An alternative mode of HTML scripting is that of an HTML knowledge of both layers. For example, this layer would be involved in converting between presentation-level inputs and out- page annotated with small amounts of code. Many scripting lanputs as strings and the corresponding business object messages guages support this kind of facility in addition to pure scripts, and or state. This layer might also manage a multipage Web inter- representative examples include JavaServer Pages (JSP), Microsoft’s Active Server Pages, PHP, and Zope. There are also variaction as a sequence of steps. In the Model-View-Controller ations on this approach in which the pages are annotated with framework, this corresponds to the application controller. application-specific HTML or XML tags, and developers can specify code to be run when these tags are encountered. Examples of Business logic The business logic code is concerned only with the underly- this include JSP bean and custom tags and Enhydra’s XMLC.
it harder to modify or test any particular aspect in isolation. In addition, there are significant issues related to handling these responsibilities, as described below. For server pages used alone, the same issues apply (because we can consider a server as a script with some additional embedded HTML), and mixing code with the HTML also presents code management and debugging issues. 52
IEEE SOFTWARE
March/April 2002
Accepting input When accepting input, a script receives either the raw HTTP input stream or a minimally parsed representation of it. HTTP supports several different mechanisms for passing parameters (encoding into the URL, query values, or form data), and all of these pass the data as simple strings. Each script must know or determine the parameter-passing mechanism, convert the parameters to
appropriate types, and validate them. This causes code duplication between scripts. Handling application logic Another issue, which affects both input and application logic, is the lack of information hiding when accessing request and session data. The script must retrieve input data from the request by name. HTTP is a stateless protocol, so data used in multiple scripts must be either stored in a session identified with the user or reread from an external data source in each script requiring the data. For example, if a script passes login information as form data, the code to store that information in the session might be password = request.getParameter (“passwordField”); decrypted = this.decode (password); request.getSession().putValue (“password”,decrypted);
Both storage in the session and storage in an external data source are effectively global in scope, and the application accesses them in dictionary-like fashion using strings as keys. Normal programming mechanisms for controlling variable access do not apply to this data, and any scripts or server pages that wish to use this data must be aware of the naming conventions. We cannot easily find all accesses to the variables using programming-language mechanisms, so modifications become more difficult. If the script does not encapsulate these conventions, knowledge of them and of the HTTP protocol’s details can spread throughout the application, greatly hindering adaptation to new uses. Furthermore, this is a potential source of errors because of both spelling errors and different scripts using the same name for different purposes. As the number of scripts or server pages increases, these problems become overwhelming. When using server pages for application logic, we are adding potentially significant amounts of code to the page. Code management techniques are not usually available for code inside server pages. With many technologies, debugging code inside the server pages is difficult. Features of modern development environments for code authoring and interactive debugging might not be available, and for compiled languages we might need to
debug inside complex generated code. For these reasons, it is a good idea to minimize the amount of code in server pages and to keep application logic out of the pages. Handling business logic Because all of the code is grouped together, it is difficult to isolate the business logic from the other layers, particularly application logic. Furthermore, unless we can run portions of the scripts separately, it is impossible to test the business logic (or any of the other layers) independently. With server pages, business logic presents the same issues that we discussed for application logic. We can very quickly have too much code in the pages, and even pages with minimal code are difficult to manage and debug.
It is a good idea to minimize the amount of code in server pages and to keep application logic out of the pages.
Generating output In producing output, simple scripts mix the HTML encoding of the result with the dynamic data. This couples the page’s look and feel with the other layers. Changing the Web site’s look or adapting the application to multiple output devices becomes extremely difficult. The latter is becoming increasingly important as the Web expands to include devices such as WAP (Wireless Application Protocol)-connected mobile phones and other small devices. Server pages help address this last issue by letting Web designers design and maintain the pages and by letting programmers provide annotations. This is generally considered the most appropriate use for server pages. Model-View-Controller To overcome these difficulties, we can use a combination of scripts, server pages, and application code. The approach we present here is part of a family of possible approaches we have used1,2 to properly partition responsibilities and overcome weaknesses in the underlying technologies (see the “A Related Approach” sidebar for a comparison). MVC strongly influences our approach, so we first examine its history. MVC concepts MVC originated in the Smalltalk-80 system to promote a layered approach when developing graphical user interfaces.3 It emerged in the late 1970s and early 1980s, long before such March/April 2002
IEEE SOFTWARE
53
A Related Approach There are many frameworks and design patterns influenced by layering, the Model-View-Controller pattern, and object principles. In general, using servlets and server pages together and keeping code out of the server pages as much as possible is referred to in Java circles as model 2 Web programming (see http://java.sun.com/j2ee). The most widely known framework using these principles is the open source Jakarta Struts (see http://jakarta.apache.org/struts). Struts is a controller framework for Java to build JavaServer Pages-based views that are linked to business models using a single controller servlet. Struts is very close to many of our concepts and also uses a single input controller servlet, but it differs in some significant areas. Most notably, it does not distinguish a separate application controller, distinguishing only
Model
Notifications
input, action, presentation, and model. Application controller responsibilities are assigned to actions, model objects, or declarative aspects of the framework. Actions are command objects, and Struts suggests that they should delegate as much behavior as possible to the model, but coding examples include application control or even model behavior in some actions. The transition to the next page after an action is controlled by the developer by editing configuration files. One other difference is that Struts classifies model objects into “bean forms” that have only state, and more normal objects with both function and behavior. These bean forms are used to store data for input or presentation processing. We have no corresponding layer and are unclear as to the role of pure state objects in an OO framework.
View
Display
Controller
Keyboard, mouse
(a)
Application Notifications controller Business objects
View
Notifications Input controller
Business logic layer
(b)
Problem space
Figure 1. (a) The original Smalltalk-80 Model-View-Controller pattern and (b) the revised Model-ViewController.
Tool layer
View layer
User interface
interfaces had become mainstream. It defined three different components (see Figure 1a): ■ ■ ■
The model handles application and business logic. The view handles presentation logic. The controller accepts and interprets keyboard and mouse input.
The intention was to separate the model (meaning nonGUI) code from its presentation. The model code didn’t contain any GUI information, but it broadcast notification of any 54
IEEE SOFTWARE
March/April 2002
state changes to dependents, which were typically views. This is similar to many current GUI component models, of which JavaBeans is perhaps the most widely known. This scheme provided a good separation between these three layers but suffered from two weaknesses. First, it had a simplistic view of the model and did not account for any difference between application logic (for example, flow of control and coordination of multiple widgets in a GUI) and business logic (for example, executing a share purchase). Second, most GUI libraries and windowing systems combined the view and controller functions in a single widget, making the logical separation into view and controller less useful. Later versions of Smalltalk with operating system widgets chose not to use a separate controller. All of the Smalltalk versions eventually introduced an additional layer to handle application logic, distinct from the business objects. Perhaps the most elegant of these is Presenter, as used in, for example, Taligent and Dolphin Smalltalk.4,5 Together, these revisions changed the common understanding of the framework, such that the term controller now refers to the object handling application logic and the term model is reserved for business objects (see Figure 1b). To distinguish these two interpretations of MVC, we use model to refer to business objects, and we use input controller and application controller to refer to the two types of controllers. MVC for the Web To apply MVC to the Web, we use a combination of scripts, server pages, and ordinary objects to implement the various components in a framework we call Web Actions. In this context, both versions of the
WAP
MVC are relevant, particularly the dual uses of the term controller. For HTTP applications, input and presentation are entirely separate, so an input controller distinct from the view is useful. For applications of any complexity, we also need an application controller to separate the details of application flow from the business logic. Figure 2 shows the framework’s basic object structure. Input controller. We implement the input controller as a script. One of the framework’s important features is that there is a single input controller for all pages in a Web application. The input controller parses input, determines the parameter-passing mechanisms, extracts any necessary information from the request, cooperates with the application controller to determine the next action, and invokes that action in the correct context. By having a single script as an input controller, we localize any knowledge of HTTP or naming conventions at the request level, reduce code duplication and the total number of scripts, and make it easier to modify any of the input processing, because there is a single point of modification. Note that the input controller class is shown as an abstract class, with one implementation for accessing the applications over HTTP with a regular Web browser and another implementation for accessing the applications using a WAP-enabled device. Application controller. We implement the application controller as a regular object—not as a script or server page. It coordinates logic related to the application flow, handles errors, maintains longer-term state (including references to the business objects), and determines which view to display. We store the application controller in the session using a key known to the input controller. This relies on a naming convention, with the disadvantages described earlier, but because this is the only thing stored directly in the session, the impact is minimized. A single application controller is responsible for multiple Web pages. In a simple application, it might be responsible for all pages; in a complex application, there are multiple application controllers for different areas of the application. By using a single, well-encapsulated ob-
WAP input controller Input controller
HTTP
HTTP input controller
Application controller
Finds and executes
Action
XML view
XML
JavaServer Page view
JavaServer Page
View
Figure 2. The Web
ject as the central point of reference for any Actions framework. persistent information, the application controller resolves the issues of information hiding and naming conventions. Rather than storing isolated pieces of information in the session, we store them in business objects and access them using messages from the application controller. Programminglanguage mechanisms let us track use of the application controller and business objects and more easily modify our code. If we are using a statically typed language, we also get static type checking as an additional validation of data usage. Action. The single input controller will invoke one of many possible actions on each request. One of its responsibilities is to determine which one. This depends on both the input from the user and on the application’s current state, so it must be determined in conjunction with the application controller. We represent the result of this determination as an Action object (an implementation of the Command pattern described elsewhere6). Business objects. We implement business objects as normal objects that contain only business logic, so they should have no knowledge of any other layers. The application controller is the only thing that manipulates the business objects, and for this reason they are not shown in Figure 2 but are inside the application controller. Both of these attributes make it much easier to develop and test the business logic in isolation from the Web infrastructure. Because the business objects March/April 2002
IEEE SOFTWARE
55
Figure 3. Java code for an input controller servlet.
public void service( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { ApplicationController controller = this.appControllerFor(req); this.setValuesIn(controller, request); String actionID = req.getPathInfo(); Action action = controller.actionFor(actionID); appController.performAction(action); View view = this.viewFor(req); view.forwardPage(controller.nextPage()); }
might be isolated, we should be able to use the same implementation for a thin-client Web application, a more rich-client implementation, or even a traditional GUI. View. We implement views as server pages, which can access the application controller and business objects. Views should contain as little code as possible, delegating most functionality to the application controller or business objects. Only code directly related to presentation in the current page should be used in a page. If supported, we prefer to use a tag mechanism such as JavaServer Pages (JSP) custom tags to remove code from the pages altogether. Figure 2 shows two different view mechanisms. The first uses a server page implementation, appropriate for a Web browser or WAP device. The second generates the same information in an XML format, which could be sent to an applet, an ActiveX control, or a GUI application.
Web actions: Control flow By organizing the scripts, server pages, and regular objects as we’ve described, we’ve overcome many of the issues associated with simple Web development. We have minimized code duplication and reliance on the protocol’s details or naming conventions by using a single input script. We have also achieved good information hiding by maintaining the data in objects rather than as flat session data. We have confined our use of server pages to the view layer, maximizing the amount of code we can manage and debug using standard programming tools and methods. By keeping each layer separate, we can test each in isolation. Overall, our application remains well-factored and easy to maintain and extend. To see how this works in more detail, let’s examine the flow of control from a single Web request in an online banking appli56
IEEE SOFTWARE
March/April 2002
cation. First, we see how the input controller accepts input, finds and sets up for the action, executes the appropriate application controller code, and then delegates to the view. Figure 3 shows sample code for this, and here we examine each of the steps. Find the controller The input controller must determine which application controller is responsible for the current request. Active application controllers are stored in the session. We assume that we can determine the controller using a lookup based on the request’s path. For example, in our banking application, we might have an account maintenance application controller, which is used for any pages related to account maintenance. Accept input Once we have determined the application controller, we must extract the appropriate information from the request and transfer that information to the application controller. Most notably, we need to find any input parameters. These can be encoded as part of the URL, as query parameters listed after the URL, or as form data. Regardless of the transmission mechanism, this input consists entirely of strings and must be converted into the appropriate types and validated. The input controller extracts the information and, in cooperation with the application controller, performs basic syntactic validation and informs the application controller of the values. For example, we might have a user request to transfer funds such as https://objectbank.com/ InputController/transfer?from= 123&to=321&amt=$50.00
The string /transfer identifies the action to
the input controller. The remainder of the string holds query parameters for the two account numbers and the amount to transfer. On the other hand, a request to update account holder data might be submitted from an HTML form and would carry many pieces of data as form data, invisible in the URL: https://objectbank.com/ InputController/updateAccountData
Find the action The application controller can keep track of a set of acceptable sequences of operations and the previous steps the user has taken. On the basis of this and the information submitted in the request, we can determine which action to take and whether this action is legal in the current context. This logic is particularly important because Web users can use the Back button to throw off the sequence of operations in a way that is not possible in a GUI. In the simplest version, we might define actions for login, logout, transfer between accounts, account update, and bill payments. Any account activity is allowed once the user has logged in, but actions transferring money have a confirmation page. We only let the user confirm a request if the immediately previous operation was the request. We also detect the situation in which the user backs up and resubmits the same request or hits the Submit button repeatedly while waiting for the first submission to be processed. Perform the action Once we have determined which action to take, we must execute it. The exact procedure for this varies depending on which implementation we choose. We might choose to have heavyweight actions, implemented as objects inheriting from a class Action and implementing a trigger method. public void trigger(Controller controller){ BankController ctrlr= BankController)controller; Account acct=ctrlr.readAccount (context.getAccountNo()); ctrlr.setCurrentAccount(account); ctrlr.setNextPage(“account Summary”); }
This lets us separate the different actions, but it does not give us as much encapsulation in the application controller as we might like. Rather than the standard OO usage of multiple methods on a single object, we have many action objects that manipulate the controller’s state, providing little encapsulation. As an alternative, we can represent the action simply as an indicator of the application controller method to be performed. This is particularly easy in a system such as Smalltalk, with simple reflection techniques. Given a Smalltalk servlet and JSP framework,7 we can simply set the action action := #deposit.
and then execute controller perform: action.
Keeping code and data together in the application controller provides better encapsulation but raises potential team issues of having many people collaborating on a single object.
In this case, the action is simply the action method’s name, and we invoke it with the standard perform: method. No action class or trigger operation is necessary. Which of these choices is preferred depends on the situation. Keeping code and data together in the application controller provides better encapsulation but raises potential team issues of having many people collaborating on a single object. In a GUI presentation, it might also be desirable to use the full Command pattern to better support Undo functionality. Forward the request We use forwarding to divide responsibility among different components. Once the action is complete, we determine the URL of the next page to be displayed and forward the request. In a simple system, the actions can directly determine the next page. In a more complex system, the application controller might coordinate this using internal state management such as a state machine. Variations We have described one particular implementation of this type of framework. Many others are possible, and in our uses of this framework, we have made significant variations depending on the application’s precise needs. Complex views We have described views as mapping diMarch/April 2002
IEEE SOFTWARE
57
rectly to server pages, but in more complex applications, this can become more sophisticated. First, we can assemble larger views from multiple smaller views. So, we might have a main page with subsections in frames or with subsections determined by including the results of other Web requests. We could model this structure either by explicitly issuing new requests or by using an internal mechanism to forward the request to another partial page. We might also want to interpose another layer of objects at the view level. For example, if handling multiple possible forms of output, we might need an explicit View object that handles the output details on a particular device. For example, we might store a key that lets the View identify the appropriate output. This might delegate to different server pages for HTML and WAP presentation and to a screen identifier for a GUI. Using a View object helps isolate the presenta-
tion of an action’s results from the action itself. It is also an appropriate place to implement functionality such as internationalization of the result. Action context We have described an architecture with very lightweight actions in which state is associated directly with the application controller. In some situations, it might be useful to dissociate some of this state from the controller. In particular, if we do not have a simple mapping from the URL to the application controller, we might need to extract more of the request state to determine the correct controller. We can do this by introducing an ActionContext object, which stores the request state in a standardized form. In this case, we would create the context, use it to find the appropriate controller, and then apply actions to the combination of controller and context.
COMPUTER
PURPOSE The IEEE Computer Society is the world’s
MEMBERSHIP Members receive the monthly magazine COM PUTER , discounts, and opportunities to serve (all activities are led by volunteer members). Membership is open to all IEEE members, affiliate society members, and others interested in the computer field.
BOARD OF GOVERNORS Term Expiring 2002: Mark Grant, Gene F. Hoffnagle, Karl Reed, Kathleen M. Swigger, Ronald Waxman, Michael R. Williams, Akihiko Yamada Term Expiring 2003: Fiorenza C. AlbertHoward, Manfred Broy, Alan Clements, Richard A. Kemmerer, Susan A. Mengel, James W. Moore, Christina M. Schober Term Expiring 2004: Jean M. Bacon, Ricardo Baeza-Yates, Deborah M. Cooper, George V. Cybenko, Wolfgang K. Giloi, Haruhisha Ichikawa, Thomas W. Williams Next Board Meeting: 10 May 02, Portland OR
IEEE
OFFICERS
President: RAYMOND D. FINDLAY
President-Elect: STEPHEN L. DIAMOND* Past President: BENJAMIN W. WAH* VP, Educational Activities: CARL K. CHANG * VP, Conferences and Tutorials: GERALD L. ENGEL* VP, Chapters Activities: JAMES H. CROSS VP, Publications: RANGACHAR KASTURI VP, Standards Activities: LOWELL G. JOHNSON †
†
(2ND VP)*
VP, Technical Activities:
DEBORAH K. SCHER-
RER(1ST VP)*
Past President: JOEL B. SYNDER
Secretary: DEBORAH M. COOPER* Treasurer: WOLFGANG K. GILOI* 2001–2002 IEEE Division VIII Director:
Secretary: HUGO M. FERNANDEZ VERSTAGEN Treasurer: DALE C. CASTON VP, Educational Activities: LYLE D. FEISEL VP, Publications Activities: JAMES M. TIEN VP, Regional Activities: W. CLEON ANDERSON VP, Standards Association: BEN C. JOHNSON VP, Technical Activities: MICHAEL R. LIGHTNER President, IEEE-USA: LeEARL A. BRYANT
WEB
SITE
COMPUTER SOCIETY O F F I C E S EXECUTIVE COMMITTEE President: WILLIS K. KING* University of Houston Dept. of Comp. Science 501 PGH Houston, TX 77204-3010 Phone: +1 713 743 3349 Fax: +1 713 743 3335 [email protected]
President-Elect: MICHAEL S. ADLER Executive Director: DANIEL J. SENESE
SOCIETY
The IEEE Computer Society’s Web site, at http://computer.org, offers information and samples from the society’s publications and conferences, as well as a broad range of information about technical committees, standards, student activities, and more.
largest association of computing professionals, and is the leading provider of technical information in the field.
THOMAS W. WILLIAMS
2002–2003 IEEE Division V Director: GUYLAINE M. POLLOCK†
Executive Director:
DAVID W. HENNAGE†
*voting member of the Board of Governors
Headquarters Office 1730 Massachusetts Ave. NW Washington, DC 20036-1992 Phone: +1 202 371 0101 • Fax: +1 202 728 9614 E-mail: [email protected] Publications Office 10662 Los Vaqueros Cir., PO Box 3014 Los Alamitos, CA 90720-1314 Phone: +1 714 821 8380 E-mail: [email protected] Membership and Publication Orders: Phone: +1 800 272 6657 Fax: +1 714 821 4641 E-mail: [email protected] European Office 13, Ave. de L’Aquilon B-1200 Brussels, Belgium Phone: +32 2 770 21 98 • Fax: +32 2 770 85 05 E-mail: [email protected] Asia/Pacific Office Watanabe Building 1-4-2 Minami-Aoyama, Minato-ku, Tokyo 107-0062, Japan Phone: +81 3 3408 3118 • Fax: +81 3 3408 3553 E-mail: [email protected]
EXECUTIVE
STAFF
Executive Director: DAVID W. HENNAGE Publisher: ANGELA BURGESS Assistant Publisher: DICK PRICE Director, Volunteer Services: ANNE MARIE KELLY Chief Financial Officer: VIOLET S. DOAN Director, Information Technology & Services: ROBERT CARE Manager, Research & Planning: JOHN C. KEATON 11-FEB-2002
58
IEEE SOFTWARE
March/April 2002
Business logic and components We have not devoted a great deal of discussion to the business logic layer, because we consider it to be a normal OO program. However, it can be quite complex and involve any number of other technologies. One that frequently comes up is the relationship between this layer and components, particularly Enterprise JavaBeans. Some frameworks (when using Java) use entity EJBs as a standards-based solution for the business object layer. We do not generally consider this to be a good solution. Although the business logic layer might have good reasons for accessing components—most notably session beans encapsulating access to transactional or legacy systems—using entity beans to represent the business logic seems ill-advised. Entity beans offer some automation potential for issues such as transactions, security, and persistence, but they impose serious limits on our design and have significant performance constraints, even relative to normal Java performance. In particular, the absence of inheritance is a burden, and other frameworks exist to deal with transactions, security, and performance in the context of normal business objects. Finally, it is harder to test EJBs in isolation because they require a container to run; this imposes increased overhead on development. Using session beans to wrap business objects or forgoing EJBs altogether are both reasonable alternatives.
U
sing our framework, developers primarily focus on writing application code rather than dealing with servlets, requests, or session variables. We have used this in a variety of different Web applications, using both Smalltalk and Java. In this, we have achieved better software quality because good OO principles were not dictated but followed naturally from the structure of the framework. Paradoxically, it also allowed developers inexperienced with OO techniques to produce high-quality code without knowing the details of the framework too well. These frameworks could be extended in several different areas, including customization to particular domains, conversion into a full-fledged framework rather than a set of patterns, and more detailed adaptation to particular technologies.
Acknowledgments The authors thank Michael Ellis and Martin Fowler for their contributions to the ideas expressed here.
References 1. M. Ellis and N. Dai, “Best Practices for Developing Web Applications Using Java Servlets,” OOPSLA 2000 tutorial; www.smalltakchronicles.net/papers/Practices. pdf. 2. N. Dai and A. Knight, “Objects versus the Web,” OOPSLA 2001 tutorial; www.smalltalkchronicles.net/papers/ objectsXweb10152001.pdf. 3. E. Gamma et al., Design Patterns, Addison-Wesley, Reading, Mass., 1994. 4. G.E. Krasner and S.T. Pope, “A Description of the Model-View-Controller User Interface Paradigm in the Smalltalk-80 System,” J. Object-Oriented Programming, vol. 1, no. 3, Aug. 1988, pp. 26–49. 5. M. Potel, MVP: Model-Viewer-Presenter, tech. report, IBM, 1996; www-106.ibm.com/developerworks/ library/mvp.html. 6. A. Bower and B. MacGlashan, “Model-View-Presenter Framework,” 2001, www.object-arts.com/EducationCentre/Overviews/ModelViewPresenter.htm. 7. VisualWave Application Developer’s Guide, tech. report, Cincom Systems, 2001.
For more information on this or any other computing topic, please visit our Digital Library at http://computer.org/publications/dlib.
About the Authors Alan Knight is a senior software developer at Cincom Systems, where he works on Smalltalk Web tools. He was also recently the technical lead on a high-performance Smalltalk Web application server supporting full implementations of ASP, JSP, and servlets. His research interests include Web development, object-relational mapping, and team programming systems. He received his BS and MS in computer science from Carleton University in Ottawa, Canada. He is a member of the ACM. He coauthored Mastering ENVY/Developer (Cambridge Univ. Press, 2001). Contact him at 594 Blanchard Cr., Ottawa, Ontario, Canada K1V 7B8; [email protected]. Naci Dai is an independent mentor and an educator. He teaches object technology, Java, design patterns, and distributed computing. He leads and mentors Web development projects. He has a background in applied engineering and computational physics. He has received his PhD in mechanical engineering from Carleton University. He is a member of the ACM. Contact him at Acarkent C75, 81610 Beykoz, Istanbul, Turkey; [email protected].
March/April 2002
IEEE SOFTWARE
59
focus
engineering Internet software
Going Faster: Testing The Web Application Edward Hieatt and Robert Mee, Evant
esting is a fundamental aspect of software engineering, but it is a practice that too often falls by the wayside in today’s fast-paced Web application development culture. Often, valuable software engineering principles are discarded, simply because they are perceived as being too time-consuming and lacking a significant payoff, and testing is a common casualty. Testing is often last in developers’ minds when pressured to deliver something, anything, before the competition jumps on
T This article documents test-first design and the creation of testable code for Web applications. The authors explain how testing has been critical to building Evant’s application at speed while maintaining a high degree of quality. 60
IEEE SOFTWARE
it, the market changes, or the money runs out. Despite developers’ hard work, the resulting code is often precisely the opposite of what is desired: unstable and fragile, profoundly slowing development. Furthermore, adapting to new requirements is difficult because existing functionality must be manually retested whenever a change is made. We know that testing is the answer, but is there a way to adhere to doing it while still developing at a fast pace? We have found that a radical approach centered on testing proves highly effective in achieving rapid development. We have spent the last two years using the Extreme Programming methodology to develop e-commerce software at Evant, a Web-based ASP company. Focusing on testing and testability results in a code base that can be built on quickly and that is malleable to the extent that it can easily be changed to accommodate new customer re-
March/April 2002
quirements and the latest Internet technologies. We describe how testing helped and how we established it as the development centerpiece. Testing first First, let us briefly describe what we mean by “test-first” programming. To test-first means to write a unit test for a new piece of functionality before the new code is written. For example, if one were writing a “shoot-’em-up” computer game, one might write a test asserting that the player initially has three lives before the code is implemented to make that the case. Using unit testing to drive development rather than relying solely on a quality assurance team profoundly changes several aspects of software engineering. Designs often differ from what they might have otherwise been, technology choices are influenced, and the way in which the application code is integrated with third-party frameworks is significantly altered. 0740-7459/02/$17.00 © 2002 IEEE
Though it might seem a tautology, practicing test-first programming leads to a pervasive quality of testability throughout the code base. By having tests in place, the overall code design is positively affected. More tests, and more features, can easily be added to designs and beneficial characteristics arise as a natural consequence of its testability. One such beneficial characteristic is reusable code. Code that is developed for a single purpose usually services a single client (meaning client code); a test provides a second client for the interface. Thus we force reuse by testing. This secondary interface exercise, and its resultant refactoring, tends to flush out design flaws that might exist in single-use code. For instance, a class B written to support class A might make inappropriate assumptions. Writing unit tests specifically for B, independent of its relationship with A, removes these assumptions, which leaves classes A and B more loosely coupled and therefore more reusable. Testable code tends to have a cleaner interface, and classes tend to be defined at a more appropriate granularity than they might otherwise be. Writing tests simply makes it more apparent when a single class’s scope is too ambitious or too reliant on the workings of another. Testing the servlet Unit testing has beneficial effects on removing dependence on a particular technology. Often software teams are directed to minimize their reliance on a single tool, technology, or vendor in an effort to maintain long-term flexibility. This is easier said than done. Take the ubiquitous Java servlet as an example. The instructions are simple: override the “service()” method and have it do what you want. Several outcomes are possible as the complexity of request processing grows or the types of different requests increase. The “service()” method might become long or break up into several methods in the class, there might be many utility methods added to the servlet subclass, or a whole hierarchy of different subclasses might arise in an attempt to specialize and still reuse code. The problem is that servlets are extremely awkward to test, primarily because they are used as a component in a specific environment—the servlet container. Creating instances of servlets and testing individual methods is quite difficult. The test-first approach leads to a different implementation. When it comes to servlets, it
is quite apparent that the easiest way to test the code that handles requests is to remove it from the servlet entirely, and put it into its own class. In fact, not only is that the easiest thing to do with respect to the tests, but the resulting design itself is then more flexible and quite independent of the servlet technology. Our code in “service()” is three lines long. It has just one responsibility: create another object that will do the work and then hand control to it. In our case, we call this object a dispatcher. Its job is to decide what kind of request is being submitted and create yet another object (an instance of a command pattern) to handle the request. The dispatcher and each command object have their own unit tests, in which creating the needed fixture is trivial. Now we not only have a testable design, we have one with a more appropriate division of labor and one that is more technology-independent.
Though it might seem a tautology, practicing test-first programming leads to a pervasive quality of testability throughout the code base.
Aggressive refactoring: Swapping technologies With the support of extensive unit tests it is possible to radically change code without breaking existing functionality. This gives the developer the ability to make major changes, even in integral frameworks, very quickly. For example, it allowed us to refactor our persistence code just weeks before we went live with our first client. We used Enterprise JavaBeans, specifically Entity beans, to manage our database persistence. We wanted to remove the entire EJB layer because of poor performance and difficulties in testing the internals of the Beans, among other reasons. Without tests, we would not have attempted to remove the Beans, but given that our unit test suite included extensive coverage for testing persistence, and that we had experience in performing this kind of major change, none of us was too concerned. In fact, we would be getting a performance improvement, more testable code, and the removal of an expensive technology. We executed the refactoring in less than a week, and performance did increase markedly. We saved money by removing the EJB technology and ended up with more testable code because we had removed a part of our system that was essentially a black box. Bugs Testing has radically changed the way we March/April 2002
IEEE SOFTWARE
61
The existing tests might not be extensive enough, in which case the programmers would add more tests.
deal with controlling bugs. We think of bugs differently from the traditional view—we use our bug count to help pinpoint areas that need more testing. That is, we consider bugs as feedback about how we are doing with our testing. When a bug is found, we write a test that “catches” the bug—a test in the area where the bug was found that asserts what should be the case and fails. We then change the code until the test runs successfully, thus fixing the bug. In this way, we fill in holes in our suite of tests. We do not stop there, though. We look for the case where several bugs in a related area entered our bug tracking system during a short time period. This information tells us that something more seriously wrong exists with our tests—perhaps the testing framework in that area needs some work. For example, perhaps three bugs are found one day all in the area of logging out of the application. Rather than trying to “pinpoint fix” each bug—that is, assign each bug to a different developer, thinking of each as an independent problem— we assign the group of bugs to a single pair of programmers. This pair looks at existing tests for that area, which clearly did not do their job well. The existing tests might not be extensive enough, in which case the programmers would add more tests. Or they might refactor the testing framework in that area to allow better, more accurate support for the kinds of tests that we can then proceed to write. The result is a suite of tests that constantly grows and changes to cover as much of the application as possible. The tests are run whenever code is changed, catching problems before the changes are even deemed worthy of being added to the main code base. Armed with such a weapon, as little time as possible is spent maintaining existing functionality. In short, development occurs faster. Tests as documentation Our methodology does not encourage writing documentation for code or even writing comments in the code. Instead, we rely on tests to document the system. This might seem strange, but because tests are written first, they completely define what the code should do. (In fact, the definition of the code being in working order is that the tests all run.) We write our tests with readability in mind. The best way to learn what the code is supposed to do is to read
62
IEEE SOFTWARE
March/April 2002
the tests. New developers, or developers who are inexperienced in a particular area, can get up to speed far quicker this way than by trying to wade through requirement and design documents. It is more effective to learn by reading a short, simple test than to try and decipher code that might contain problems or is very complex. This method of learning through reading tests is especially important in our company as the development team grows. As new developers come on, they are paired with existing developers to gain knowledge, but a large part of their learning comes from reading tests. New team members come up to speed quickly because of well-factored, readable tests. Acceptance tests and their extensions In addition to writing unit tests, our process has a mechanism called acceptance tests that allow product managers to express tests at the scenario level. We began by writing a testing tool that expressed operations in the system and the expected results as XML. Product managers, with the assistance of programmers and quality assurance team members, wrote extensive acceptance tests that, along with our suite of unit tests, are run at every integration. The acceptance tests provided a clear value, but it was soon apparent that the supporting frameworks for the XML had several other benefits. As we neared deployment for our first customer, the need arose to integrate with other e-commerce systems and with the client’s legacy mainframes. We employed an industry-standard EAI (Enterprise Application Integration) tool configured to generate XML as an adaptor to transform data from these external systems to our own. The XML test framework required little modification to act as the interface to this integration tool. We even used the framework assertion capabilities to verify incoming transactions. As our need for acceptance tests grew, writing them more quickly and making them easily maintainable became important. XML, though workable, is awkward for a human to write, especially for nontechnical staff. To solve this, we implemented a domain-specific language, called Evant Script Programming (ESP), which is more compact and readable than XML. Once ESP was available, we recognized uses beyond acceptance tests and
For More Information
data loading, including an easy way to configure system parameters, and that we could use a suite of ESP scripts to create a tailored demo environment. Testing challenges and solutions In the Web environment, the client and the server are very different beasts. The user’s machine runs a Web browser, which understands HTML and JavaScript (we will not discuss the extra complications of applets, plug-ins, or other client-side technologies here). The only ability this browser has is to send information through a map of strings over a stateless protocol to a Web server, which deals with the submission by passing it off to an engine running application code (for example a servlet container running Java code or a Perl program). We would typically like to test the client and the application server part of this set up, but it would be difficult to come up with a testing framework that could do both at once—to test JavaScript and DHTML on the client and Java on the application server. However, splitting tests into two distinct parts is not a good idea because we need to test how the two sides interact. This is the classic Web application testing problem. We recommend the following remedy. First, test those parts of the server-side code that are not directly concerned with being part of a Web application, without involving the Web peculiarities. For example, if one were testing the manipulation of a tree object, one would test manipulating the object directly, ignoring the fact that the visual representation of the tree is collapsed and expanded by the user through the UI. Test the business logic with such low-granularity unit tests. That is, of course, the goal no matter what the UI, but it becomes particularly important to be strict about it when working with a Web application. Second, test those parts of the client-side code that have no server interaction. This is typically code that contains little or no business logic. Examples of such code, typically written in JavaScript, might include functions to pop up error messages, perform simple field validations, and so on. The immediate problem we faced in writing tests for this sort of code was that no testing framework existed for JavaScript inside the browser. In fact, we had already heard from
Books Extreme Programming Explained, Kent Beck, especially Chapter 18 (”The introduction to Extreme Programming“); a good place to start. Extreme Programming Installed, Ron Jeffries et al., Chapters 13, 14, 29, and 34; contains a lot of information addressing common problems and questions about unit testing. Web sites Testing frameworks: www.xprogramming.com/software.htm www.junit.org Extreme Programming sites: www.xprogramming.com www.extremeprogramming.org
other developers that testing JavaScript was too hard, and that JavaScript itself should therefore be avoided entirely. Undeterred, we wrote a testing framework for JavaScript called JsUnit, now a member of the XUnit family of unit testing frameworks (www. jsunit.net). JavaScript is notoriously difficult to write and debug, but using JsUnit has greatly helped our rapid development of client-side code. Third, write functional tests of a low grain in the server-side language (for example, Java or C++) that simulate the request/response Web environment. We wrote a framework for such tests that extends our usual unit-testing framework to allow the test author to think of himself or herself as being in the Web browser position, putting keys and values into a Map object (which corresponds to