The Principles of Extreme Programming
- Apr 5, 2002
The practices of Extreme Programming are designed to work together to provide more value than each one could individually.
We will explore the activities of XP in the coming chapters. Before we do, however, we should mention the principles that codify XP. Understanding these principles is important to obtaining an understanding of the forces that drive XP. Some of these principles are simple common sense. Others will quickly become the basis of most future software development processes.
Perhaps the most extreme of the XP principles is its insistence on having a real customer working directly with the project. This person should be one of the eventual users of the system. They should also be able to represent the needs of their fellow users.
Our customer must be empowered to answer questions and make decisions regarding feature priority, risks, and so forth. They will take part in planning by writing and prioritizing stories and deciding release content. By having them work along with the development team, they get to provide constant feedback, as well as answer any questions the team may have regarding what they really meant when they wrote the stories.
The customer is also responsible for writing the acceptance tests (see Chapter 5) that verify that each story is implemented correctly (that is, has the desired behavior). Customers are generally not software developers, so they will need help from the team if the acceptance tests are to be automated (which is strongly suggested).
A metaphor is a powerful way to relate a difficult idea in an unfamiliar area. Every XP project should use one or more metaphors to guide it and to provide a shared context. By having a shared metaphor, everyone involved shares an understanding of the big picture view of the system and the problem that it is to solve. This gives a conceptual framework in which to talk and a source for terminology. The metaphor also serves as a definition of the overall conceptual architecture of the system.
Note that the metaphor may be a simplistic representation of a concept or feature. Metaphors are not meant to be accurate in every respect. Rather, they provide a context for discussing problems and their solution.
Here's a quote from the Chrysler Comprehensive Compensation project regarding the use of metaphors [Garzaniti 1997]:
"Chrysler is a manufacturing company; we make cars. Using a manufacturing metaphor to define the project was an important first step in getting the team (and management) on a level playing field. The concepts of lines, parts, bins, jobs, and stations are metaphors understood throughout the company. The team had the benefit of a very rich domain model developed by members of the team in the project's first iteration. It gave the members of the project an edge in understanding an extremely complex domain.''
This example metaphor uses the elements of an assembly line to describe posting rules in a payroll application. Posting rules specify the way that accounts are debited and credited when creating paychecks. Like parts from a set of bins on the assembly line, money is taken out of each account (expense reports, salary, and bonuses), and a paycheck is written. Unlike an assembly line, however, the paycheck may be debited so the money is moved back into accounts (such as a medical plan or 401K). Metaphors are especially useful when developers from one domain are asked to work on a project that falls in another.
Software development projects of any significant size have to be planned. Planning serves to provide a mutual understanding to all parties of approximately how long the project will take. It also serves as a bellwether of the size of the project versus the capability of the organization. Is it larger than the capability of the team? Approximately how long will it take the team to complete? You need to have some direction and shared understanding.
Planning does not mean that you spend six months or more doing a detailed analysis and a week-by-week plan for the next five years. However, you do have to do some minimal planning to know where you are going, roughly how you will get there, and what risks might be encountered along the way.
You must understand one thing about your plan (and there's no escaping this). It is inaccurate, and it will change as you learn more, as requirements change, and as new risks are discovered. In fact, the farther in the future that something is planned the more likely it is to be inaccurate and the greater the inaccuracy is likely to be. It is simply too difficult to predict too far into the future. By the time you get there, chances are, your plan will be totally unfounded in reality.
However, you must do some planning. The next question is who gets to make the planning decisions? The answer is those who know. That means that the customer gets to make decisions about the business and the business problems that the project is aiming to solve, and the development team gets to make decisions about technical issues. Kent Beck breaks it down as follows [Beck 2000]:
Business people get to decide the following:
ScopeWhat does the system have to do?
PriorityWhat pieces are more important? When the scope turns out to be too big, what gets dropped or deferred?
Release contentsWhat makes up each release?
Release datesWhen do we deploy releases? These can be driven by marketing concerns.
Technical people get to decide the following:
EstimatesHow long will it take to implement things?
ConsequencesWhat are the implications of making a particular decision? These are the technical results from certain decisions, such as tools, hardware, subsystems, and so forth.
ProcessHow will we build the system? This refers to team organization, culture, facilities, and so forth.
Detailed schedulingWhen will a task be completed? This is the scheduling within a release. Often, the riskiest things are tackled first to minimize the overall risk.
Meetings. Most developers hate them because they are often long, drawn-out, and boring affairs that keep you from the code. Meetings are invaluable as a communication forum, though, but only as long as they are short and focused. To this end, XP uses standup meetings. The concept is a simple one: No chairs allowed. If everyone has to stand, it ensures that the meeting will be quick and to the point.
The best time for a standup meeting is daily, early in the morning after everyone has shown up for the day. Get the team in a circle with the caffeinated beverage of their choice in hand.1 Go around the circle and have each team member give a brief status update: what they did yesterday, what they'll be doing today, and any issues or announcements that the team should be made aware of.
Keep the meeting focused. Don't let discussions start up. If people need to talk, they can meet up later in the day.
Testing is another core piece of XP philosophy. Testing occurs on multiple levels. The first level is the unit tests, written by the software developers. Unit tests are written before the code that runs them. A unit test asserts that a small piece of functionality works as expected. You can't consider a task completed unless there is a comprehensive set of unit tests for it. An implication of this is that you can't integrate functionality until all of its unit test cases are written and have passed.
In fact, we strive to write the unit tests first, and then implement the feature to make the test pass: test, code, test, code, and so on. Several advantages can be gained by adopting a test-first approach to development:
Tests provide a definition or documentation of the desired behavior.
You know when you have the functionality fully and correctly implemented. When the test passes, you can say confidently, "Done... next!"
Too much code is deployed without being thoroughly tested with the idea that you'll get bug reports informing you of the problems that really occur. However, that's an expensive and unstable way to go. Part of the reason (based on personal experience) is that it's a real drag to write test code after putting in a stellar performance writing the problem-solving code. That's another reason to adopt a write-the-test-first approach to coding. The test then becomes part of the solution. You have to spec the behavior anyway, either on paper or in your head, so just write it down using the syntax of the language you're coding in. Also, working this way means that you never have to spend any great amount of time writing only test code. You write a little bit of test code and then write some production code. You alternate between these two activities until the task is completed.
What do you test? The simple answer is everything that could possibly break. That makes it easy, right? Maybe it's better to think of it as don't test things that could not possibly break. What might that be, you ask? Accessors and mutators (otherwise known as getters and setters) are a good example. There's no way that returning or setting an instance variable can go astray. Simple creation constructors2 are another class of things that can be safely ignored. On the other hand, when you are just starting out using test-first development, it might help to test these simple things just to get into the groove of this style of development.
You should understand one more thing about tests. They have to run at a 100 percent pass rate in the production code. This lets you integrate with confidence and know that, if a test fails during integration, your newly added code is to blame. The test that failed will point you to the culprit.
Having this comprehensive collection of tests gives you a great feeling of confidence. This confidence lets you be aggressive because, if you break anything, you'll know the next time that you run the tests.
Speaking of running the tests, when and how often should you run them? You should run them as often as you can. This implies that tests should be fast and lightweight. Ideally, you should run them (at least those for the subsystem you're working in) each time that you successfully compile. At the very least, you should run them (all of them preferably) before and after you make any changes. Running them before gives you confidence that you have a stable platform to work on, and running them after proves that you didn't break anything.
"Keep it simple" are words to live by. They are words to design by, too. Being extreme, XP keeps the design as simple as possible. There are several things at play here:
Design for today.
Do the simplest thing that could possibly work.
Continuously simplify the design.
If we keep the design simple and clean, it stays nimble and malleable. By deferring design until it is required, we can get something valuable working now. We call this "just in time design." This flies in the face of conventional practices that prescribe generalized design. Generalized design pushes making your design flexible enough to handle tomorrow's requirements. Why not wait until you need it before designing for it? There's a good chance that you won't need what you think you will and an even larger chance that you'll need something completely unanticipated.
By continually revisiting the design, you can keep it simple and flexible. One major criticism leveled against XP is that "You guys don't do design." This isn't true. XPers don't do big up-front design, but we are doing design all the time, continuously [Fowler 2001A].
Just what is a simple design? In order of increasing importance, a simple design [Beck 2000] includes the following:
Runs all the tests (This is pretty obvious.)
Reveals all the intention (The design is clear and understandable.)
Involves no duplication (This is obvious. Watch for parallel hierarchies.)
Contains fewest number of classes or methods (Again, this is obvious.)
Robert Martin's advice on design simplicity is to not get too hung up about it [Newkirk 2001]. It is more important to be willing and able to refactor as the opportunity and the need present themselves than to get it one hundred percent right, sparkling clean, and as simple as possible the first time.
Two heads are better than one, but, in XP, two heads together are better than two separately. In XP, people develop in pairs. Every line of production code is written by a pair of developers. The driver has control of the keyboard and mouse, and the partner watches and helps. The driver is actively writing tests, coding, integrating, or refactoring. The partner watches, keeping in mind strategic goals; catching spelling and syntax mistakes; and offering insight, ideas, and suggestions. The partner can see the forest while the driver sees the trees. This puts the partner in a great position to be able to see when things are swerving off track or getting too generalized. The partner can also be thinking about other tests that could be written. If the partner has an idea of how something can be implemented, the pair can swap roles. The general way to initiate role swapping are two phrases: "Here, you drive," and "Can I drive?"
Figure 1-1 Pair programmers.
To support working together effectively, you should adopt a teamwide set of coding standards. All team members should voluntarily agree to adhere to them. It doesn't matter what they are, as long as they are used consistently. It's generally best to use some common standards. For example, if you are working in Java, use the standards from Sun Microsystems [JavaConv 1999];3 for Smalltalk, use Smalltalk with Style [Skublics 1996].
If you have consistent standards, other practices such as the following work:
You can pair program because both partners work in the same format.
Anyone could own the code because you all write it in the same style.
You can refactor because you understand the format of the code that you are changing and others will understand the format of your work.
Finally, you can go at full speed because you don't need to reformat code as you go, or be constantly having to switch from one style to another, or be trying to understand the formatting and style as well as the code itself.
Traditional software development either assigns ownership of classes to specific team members or requires a developer to get an exclusive write lock on any classes that they will be modifying. Both practices create problems. Let's look at each practice in turn.
When developers own classes, anyone that needs a change made to a class that somebody else owns has to request that change of the class owner. Eventually, they have to wait until the owner has time and/or gets around to it. Alternatively, they make their own local changes and submit them to the class owner. Both of these approaches create problems. To compound matters, let's say that they request the required change (which, by the way, may not be quite right the first time), get tired of waiting, and make a local change to the class. Eventually, the class owner makes the change. Now, there are parallel changes: one that is official (made by the class owner) and one that works (made by the developer who needs it and knows best what it has to do).
We would like to revisit the idea of class ownership by individual developers for a minute. Class ownership tends to create specialized knowledge in the development team. Joe over there knows the communications subsystem, Mary knows the Graphical User Interface (GUI) components, and so forth. Few people have any deep knowledge of the entire system, other than at an Application Programming Interface (API) level. What happens if Joe gets an offer that he can't refuse? His specialized knowledge goes with him. A less devastating situation might be if Mary goes to a conference for a week or on vacation. Does GUI related development stop?
Let us remove the class owners but keep the source control tools that provide that level of functionality. Now you can make changes to anything, but have to get a write lock first. This leads to problems as well. You can make the change, but, if another developer has a lock on that file, you have to wait for them to finish working and to release the lock. Most source control tools let you know who has write locks, so at least you know who to whine at. However, this causes bottlenecks and delays, either due to waiting for access to a class or due to the time required to integrate local changes into a newly released version.
XP prescribes team ownership of everything. If a change needs to be made (either to add functionality or to improve the design), the person who is in the best situation to make it (that is, the developer who sees the immediate need for the change) can.
A good source control system is a must in order to make collective ownership work. Exclusive write locks don't work. Every developer must be able to modify anything at any time. You can't have people waiting for write access.
A modern configuration management tool goes a long way to making collective ownership work. To really make this work, you have to be doing continuous integration as well. That way, if there are conflicts, they will be small and will be discovered as soon as possible (remember that you should be integrating at least daily).
In order to be working aggressively at full speed, you need to be integrating your work into the production codebase continuously. At the very least, you should be integrating daily. Ideally, it should be more often. You should integrate after each task you complete. The tests allow you to know with confidence that, if all (for the entire system) run successfully, you can release the new version of the codebase. By integrating this frequently, the chance of your changes conflicting with someone else's is minimized. If there is still a conflict, it will be minimal. These short integration cycles also keep everyone converging. The system doesn't have time to diverge between integrations.
What is refactoring? According to Martin Fowler [Fowler 1999], it is the following:
"Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code, yet improves its internal structure. It is a disciplined way to clean up code that minimizes the chances of introducing bugs."
There are many simple refactorings as well as several complex ones. Go read Martin Fowler's wonderful book [Fowler 1999] for a catalog of refactorings, as well as discussions of other related issues.
You should be refactoring all the time. When you are writing tests, you may want to refactor to make something easier to test. When you are coding some functionality, you may want to refactor to make it easier to add or to remove duplication. Another great way to use refactoring is to take existing code and restructure it so it conforms to a design pattern.
According to Ron Jeffries [Jeffries 2001A] the following is true:
"There are two moments when one considers refactoring as part of doing a task: before the task begins, to "make a space" for the new code, after the task is implemented, to make the new code and old code as clear and simple as possible."
"Release early; release often." "Frequent, tangible, and working results." These are two phrases that sum up the idea of using small releases. A small release is between one and several months worth of work. Six months is generally too much. By having small releases, you get new functionality into the hands of real users as early as possible.4 This provides you with invaluable feedback on whether you're headed in the right direction. It also catches any defects as early as possible.
Having small releases lets you plan more accurately as well. The further into the future that you try to plan means the more inaccurate you will be. Planning a couple months in advance makes much more sense than trying to plan six months or a year ahead.
Just don't forget that a release has to be complete and make sense. Releasing with a feature half implemented isn't of much use to anyone. The rule in an XP shop is that it's not considered shippable until it is completely finished. Remember, as well, that release dates are sometimes influenced by external factors. For example, marketing may want a release with support for the latest feature in time for a large industry conference. Sales may need to have online order taking live in time for the Christmas shopping season.
Nobody does their best work when they are stressed, under pressure, and overtired. It's funny that those exact conditions are so prevalent in our industry. If you do XP, stress and pressure will be dealt with by the planning practices. It's easy to prevent people from becoming overtired. Send them home after eight hours. Encourage a reasonable amount of playtime at work. This is also a great stress buster and keeps everyone relaxed and in top form. The 40-hour week and 8-hour day are conceptual. The idea is not to work longer than works for you.
Changing requirements are a given in the rapidly evolving business environment in which we all work. If we are adaptive, change is beneficial. It sneaks up on our competitors and bites them on the bottom. If we embrace change, it works to our advantage. The key concept for a successful XP project is to help the customer and development teams understand that change is the norm. We never know everything up front whether we belong to the customer team or the development team.
Traditional approaches to change in requirements suggest that we can make predictions and freeze the requirements accordingly. Requirements documents commonly view the world as capable of turning change on and off in discrete intervals. In the real world, change happens continuously.
The XP solution is simple: Stay flexible. Do the Zen thing, and be like water, adjusting your course as required. Rigid projects break and get canceled. Flexible projects bend with the wind (OK, no more Zen), accepting changes rather than ignoring or fighting them. By assuming that not everything is right or known up front, an XP team can accept changes with little or no pain. Remember short iterations, release early and often, and to keep the customer in the loop. These practices give the team invaluable feedback as to where they are and where they are going, and, most important, where they have gone off course. Feedback like that is used to make adjustments to the plan. Keeping the code clean and simple ensures the code is also flexible and allows it to bend when confronted by changing requirements.