IBM Skip to main content
Search for:   within 
      Search help  
     IBM home  |  Products & services  |  Support & downloads   |  My account

developerWorks > Java technology
developerWorks
Demystifying Extreme Programming: Just-in-time design
Discuss67 KBe-mail it!
Contents:
Resisting the urge to be stupid
From story cards to code
The power of being myopic
Resources
About the author
Rate this article
Related content:
Demystifying Extreme Programming series
An interview with Kent Beck
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
Why do today what you can put off till tomorrow?

Level: Intermediate

Roy W. Miller (mailto:rmiller@rolemodelsoft.com?cc=&subject=Just-in-time design)
Software Developer, RoleModel Software, Inc.
1 July 2003

Column iconPeople who aren't familiar with XP are bothered by the concept of just-in-time (JIT) design -- designing and implementing what you know you need right now and not worrying about future design issues until absolutely necessary. While this approach might seem unwise or even reckless, XP advocate Roy Miller wraps up his series by showing you how safe and easy JIT design is -- and how it just might revolutionize the way you write code. Share your thoughts on this article with the author and other readers in the accompanying discussion forum. (You can also click Discuss at the top or bottom of the article to access the forum.)

In Extreme Programming Explained (see Resources), Kent Beck described a "simple" design as one that:

  • Passes all the tests
  • Communicates developer intent clearly
  • Contains no duplicate code
  • Has the minimum number of classes and methods necessary to accomplish the first three

That definition is good, but it also makes some assumptions. Most importantly, it assumes you have programmer tests -- and that they all pass all the time. XP demands that you write tests before you write code, so a simple design is one that grows over time in a test-first manner. This approach implies that the design doesn't emerge fully formed from a design phase, which in turn begs the question: if you're designing as you go, where do you start? That question resurfaces at multiple points throughout the project -- every time you need to make a design decision.

The answer is that you design just what you need to complete the customer stories for the current iteration -- not a bit more. That goes for business logic and infrastructure, too. If a user needs to create employees in the system for this iteration, then build the business objects and the infrastructure you need to support adding employees. Don't add the capability to remove an employee yet. Don't even think about that piece of the design for now. Remember the YAGNI (You Aren't Gonna Need It) principle: don't build in features, or "hooks," that you don't know you need yet.

Resisting the urge to be stupid
In all the years I've been in IT and using XP, I can't think of a single case outside of a committed XP team where I could say, "Ignore that part of the design for now," and escape unharmed. It seems like a radical idea -- like you're ignoring a problem and hoping it will go away. In a way, that's sort of true. But it's not as bad as you might think, as I'll discuss shortly.

I worked on a system recently that required "integrated user help." Look carefully at the requirement: Users need to get help within the application. Does it matter what form that help takes? Not right now. Pop open a window with static help text in it and you're done. We could assume that context-sensitive help is a requirement, but that's much harder to implement. We also don't know we need it yet. That's the key. Don't design (and work on) features that you don't know you need yet. As it turned out, we didn't need context-sensitive help. Users of the system were technically savvy. They just needed a ready reference. A PDF was perfectly acceptable, and cost much less to build in.

But saying you should design for today doesn't mean you should be foolish. There are plenty of cases where you have to think a bit beyond today in order to keep from shooting yourself in the foot. This is where layering can help, and where it's wise to think ahead. For example, imagine you're building a system that's data intensive. You'll have to hit a particular database quite a bit. You've done this many times before in your career, but don't assume the answer you came up with last time is exactly the right answer this time. Apply the principles you know, but leave room for learning. In essence, I'm suggesting that you apply patterns (such as Gang of Four, among others) without getting stuck.

In the case of persistence, for instance, you probably should create a "persistence layer" in your application. That's just good encapsulation and smart layering. But don't try to create a generic, all-purpose persistence layer, since at this point you're not sure you need it. Instead, build the simplest persistence layer that could possibly work at this point. Maybe have a class that hides the raw details of your persistence mechanism, but for now uses SQL strings underneath it. That will probably change, but it's a good start. You aren't being stupid, but you aren't assuming too much either -- a difficult but necessary balance.

From story cards to code
Let's use a simple example to illustrate deferring decisions until you need to make them. As we did last month, we'll create a basic banking system for "Roy Miller's Savings and Loan" (deposits are now being accepted). Suppose we have the following story cards, prioritized in this order:

  1. Check the account balance.
  2. Deposit funds.
  3. Withdraw funds.
  4. Transfer funds to another account.

You and your partner start working on the first story, "Check the account balance." According to the story, you need an Account class, and you need to be able to ask it for its balance. That's all. You might feel compelled to ask what the user interface needs to look like, but there aren't any cards for that so don't even think about it. You can add it later. Since you should be writing tests first, you should have a failing test that tells you what the Account class needs to look like. Your class should look similar to Listing 1:

Listing 1. Account class

package com.sample;

public class Account {
  protected int balance;

  public int balance() {
    return balance;
  }

}

You now have all you need to say that the first card -- Check the account balance -- is done. While you know that eventually you'll need to be able to deposit funds, you can add that capability when you work on the "Deposit funds" card. Resist the urge to do that too soon. In many ways, JIT design is a matter of disciplining yourself to keep from taking steps that are too big.

According to the next card, you need to deposit funds. No problem. Write the test, see the code fail, then get it to pass. Your code should look something like Listing 2:

Listing 2. Account with deposit capability

package com.sample;

public class Account {
  protected int balance;
  
  public int balance() {
    return balance;
  }
  
  public void deposit(int amount) {
    balance += amount;
  }

}

You're now done with the second card. We still can't withdraw anything, but so what? That's another card, which you're now free to tackle. Write a test that the code in Listing 3 will pass.

Listing 3. Account with withdraw capability

package com.sample;

public class Account {
  protected int balance;
  
  public int balance() {
    return balance;
  }
  
  public void deposit(int amount) {
    balance += amount;
  }
  
  public void withdraw(int amount) {
    balance -= amount;
  }

}

You're done again. You can now move on to what is potentially the most dangerous card in the stack so far: the "Transfer funds to another account" card. This is the most dangerous card for two reasons. First, it's the last card, so we can't see what's coming up. Not knowing increases the temptation to build in some "extras" that we don't know we need yet. Second, the feature itself can quickly get fancier than necessary. This is where it pays to be disciplined. We have to resist the urge to create too much, no matter how much we want to.

The first thing to do is write a test for the simplest possible transfer behavior, such as the code in Listing 4:

Listing 4. JUnit test for transferring funds

package com.sample;

import junit.framework.TestCase;

public class TC_Account extends TestCase {

  public TC_Account(String name) {
    super(name);
  }

  public void testTransferFunds() {
    Account accountOne = new Account();
    accountOne.balance = 1;
    Account accountTwo = new Account();
    accountTwo.balance = 1;

    accountOne.transferFundsTo(1, accountTwo);      

    assertEquals(0, accountOne.balance());
    assertEquals(2, accountTwo.balance());
  }
}

Getting the test to pass should be simple and will help you to resist getting too fancy. You should write a transferFundsTo() method so that Account looks similar to Listing 5:

Listing 5. Account with transfer capability

package com.sample;

public class Account {
  protected int balance;
  
  public int balance() {
    return balance;
  }
  
  public void deposit(int amount) {
    balance += amount;
  }
  
  public void withdraw(int amount) {
    balance -= amount;
  }

  public void transferFundsTo(int amount, Account account) {
    withdraw(amount);
    account.deposit(amount);
  }

}

Notice what our code has so far: first, our Account class isn't a bean (it essentially has a getter -- but no setter -- for its one data member). Based on the cards we've got right now, however, it doesn't need to be. Second, what happens when somebody tries to transfer funds from one account to another, but the source account doesn't have a large enough balance? Currently, the balance will just go negative. Of course we'll need to address this issue, but not yet. Third, we don't validate the incoming amounts for deposit(), withdraw(), or transferFundsTo(). It's not hard to handle that situation, and adding the code won't take long, but we'll resist the urge to do more than we have to for right now. Finally, we don't have a user interface, and we can't save any data yet. We can't have a "real" system without them, but we'll create them when we need them, not before.

The power of being myopic
When you try to design to an extremely low level of detail before you start writing code you plan to keep, you're assuming you can know what the end result should look like. That's a dangerous assumption. If things change while you're in the middle of coding, your design becomes invalid. There are three ways you can handle this likely situation: you can deny it will happen and just rigorously adhere to your design. The result, however, is that users get the system they thought they wanted when the project started, not the system they end up wanting when the project's done.

Where do we go from here?
We've come to the end of our tour of Extreme Programming; I hope you've enjoyed learning about this methodology as much as I have enjoyed writing about it -- and using it in my own projects. While the column will end with this installment, I'll still be manning the associated discussion forum to address any questions you may have on XP, and if it becomes clear that another installment is warranted, I'll be happy to accommodate.

Another common approach is to do the up-front design anyway, then do a comprehensive redesign when change happens. However, this approach disrupts the project's rhythm and just repeats the problem at multiple points during the project (big design up front, big redesign two months later, and so on).

The wisest choice is to be intentionally shortsighted. Remember, though, being intentionally short-sighted is not the same as being stupid, irresponsible, or unprofessional.

If you approach design with a JIT mentality -- writing tests for the current iteration's cards, then making those tests pass -- you can avoid over-designing and having to redesign. Instead, defer design decisions you don't have to make. When you defer design decisions, you're reserving your option to change. This approach leads to better systems.

Resources

  • Participate in the discussion forum on this article. (You can also click Discuss at the top or bottom of the article to access the forum.)

  • Are you joining this column late in the game? See where it all started, with the first article co-written with Chris Collins, "XP distilled" (developerWorks, March 2001). Then, take a look at the complete set of articles from this column.

  • Kent Beck started the XP craze with Extreme Programming Explained: Embrace Change (Addison-Wesley, 1999).

  • In this interview (developerWorks, June 2003), Kent Beck offers insight on design patterns, the Java platform's utility for producing quality software, pair programming, and what developers can do to improve their software efforts.

  • Design patterns (Addison-Wesley, 1995) by Erich Gamma, et al., is a classic and a must for any programmer.

  • Web Services and Service-Oriented Architectures is one of the best Web sites for information about database issues, namely OO systems. You'll find links for OR mapping, OODBs, and so on, and also many good ideas for layering your persistence and why that makes sense.

  • Read Java Tools for Extreme Programming (John Wiley & Sons, 2001) by Richard Hightower and Nicholas Lesiecki for a book-length description of various Java tools that support XP, including JUnit and Ant.

  • Find hundreds more Java technology resources on the developerWorks Java technology zone.

About the author
Roy W. Miller has been a software developer and technology consultant for almost ten years, first with Andersen Consulting (now Accenture) and currently with RoleModel Software, Inc. in North Carolina. He has used heavyweight methods and agile ones, including XP. He co-authored a book in the Addison-Wesley XP Series (Extreme Programming Applied: Playing to Win) and is currently writing a book about complexity, emergence, and software development (the working title is Growing Software: Debunking the Myth of Prediction and Control). Contact Roy at mailto:rmiller@rolemodelsoft.com or mailto:roy@roywmiller.com. You can also visit his personal Web site at http://www.roywmiller.com/


Discuss67 KBe-mail it!

What do you think of this document?
Killer! (5) Good stuff (4) So-so; not bad (3) Needs work (2) Lame! (1)

Send us your comments or click Discuss to share your comments with others.



developerWorks > Java technology
developerWorks
  About IBM  |  Privacy  |  Terms of use  |  Contact