Exploration Through Example

Example-driven development, Agile testing, context-driven testing, Agile programming, Ruby, and other things of interest to Brian Marick
191.8 167.2 186.2 183.6 184.0 183.2 184.6

Wed, 06 Apr 2005

Introducing Agile to a legacy project

I was talking with a potential client about what I might do for them. Suddenly I realized I'd had this conversation before. So I decided to write down my talking points.

I'm almost always contacted about testing in an Agile project. My biases about that are described in "A Roadmap for Testing on an Agile Project". A problem with that roadmap is that it really assumes either a greenfield project or an Agile project that's well underway. But some people I talk to are just starting with Agile, working on a legacy code base that really needs cleaning up, and don't have much in the way of testing. That makes that roadmap less applicable.

My talking points follow. Notice how few of them are about testing. When I first started consulting with Agile projects, I tried - well, some - to stick to my testing knitting. But either it made no sense to wall off testing from other concerns or the existing wall was clearly a problem. So now I stick my nose into all sorts of business.

  • Have at least half the programmers read Michael Feathers' wonderful Working Effectively with Legacy Code before bringing me in. It describes many tricks and ways of thinking for getting your arms around legacy code - mainly by wrapping it with tests. And if you bring Michael in instead of me, that's fine.

  • Be wary of large-scale code cleanups. Ron Jeffries has an excellent analogy: cleaning up legacy code is like cleaning up a horrendously messy kitchen. You can set aside a day, work like a dog, and leave it pristine. Great. What happens next? It gets dirty again, dish by dish. You don't have the habits that help you keep it clean.

    The better alternative is to clean up gradually. Every day, wash the dishes you dirty, plus a couple more. Over time, the kitchen will get clean - and you'll have the habit of immediately cleaning up your mess.

    A dirty kitchen has an advantage over software projects: you can see it getting cleaner. When projects declare that they're going to spend the next N months making the code right, then get back to adding features, that's N months where no one outside the project can see anything happening. That's an unstable situation, and those who pay the bills quite often put a stop to the cleanup partway through - which usually means the code is not a whole lot more tractable.

    For that reason, I recommend weaving cleanup into the delivery of business value. Suppose you're fixing a bug. You might have occasion to look at several classes as you follow the execution path that leads to the failure. Leave each of those classes slightly better than you found it, even if the change has nothing to do with fixing the bug. Over time, the system will get better, and you'll have the right habits.

    That latter point is important. Being a good Agile programmer means learning how to incrementally grow a good design while at the same time doing something else (like adding features). You don't learn how to do that by doing something different, like a rewrite.

  • Suppose you have a team that's not doing programmer testing, especially not doing test-driven design. I've come to believe the test-writing part of TDD is actually easier to learn than the refactoring part. A surprising number of programmers have no visceral dread of duplication, especially its more subtle forms like boolean parameters. Or they think of picking intention-revealing names as just a help for someone later, not a tool for thinking more clearly about the code in front of them. And so on.

    So the team has to commit to learning those and other similar things. The project has to explicitly become a learning project. I recommend pair programming and colocation as ways of spreading learning quickly. Use Big Visible Charts to nudge people toward more learning. (For example, consider putting up a list of Feathers' tricks and having people initial those they've used.) When people do some clever cleanup, they should announce it at the daily standup and offer to demonstrate. Actually, I'd rather they be so jazzed about what they've just done that they immediately grab someone to show. Even working on legacy code, even in learning mode, an Agile project should have a buzz.

  • When the project is ticking over smoothly, programmers will test out of desire, not duty. They will be nervous enough about not having a strong safety net of tests that they will make sure it's always strong. But getting there can be tough. You're not starting with a safety net, so the first tests you write won't help much. And writing them will be slow and painful because of all the dependencies. (See Feathers' book.) It's easy to give up halfway. All I can recommend is to consciously (if subjectively) track whether things are getting better. If they're not, do something about it.

  • It probably makes sense to pursue programmer testing and automated whole-product testing at the same time. Since the whole-product tests test from the outside, they're less affected by a rat's nest of dependencies. If they're faster to write, they'll provide confidence sooner. But avoid having certain programmers assigned to whole-product test automation. Testing should belong to everyone on the team, and everyone should be willing and able to add new tests. All the programmers should be ready to extend the test harness or other test support code.

  • Testers on the team should focus on stabilizing the product. It is not enough for tests to be good at finding bugs; they must also support the programmers. Mainly that means they should be automated, can run on any machine, and run quickly. You want the programmers to run them frequently to bolster their confidence as they change code.

    When the programmers sign up to work on a task, that means the testers are also signed up to create tests that stand a good chance of finding unintended consequences of the particular changes the programmers are going to make. That will involve much more communication than before, including pair work. Therefore, if the programmers are in a bullpen, the testers should be there too. Also, the testers should teach the programmers how to "think like a tester" so that they can avoid bugs in the first place.

    In this way, the testers serve the programmers (a role some are uncomfortable with). In return, the programmers should be prepared to make changes to the code that are directly in service of making whole-product tests easier to write. (If programmers are unwilling or grudgingly willing, the team has a task: turning duty into desire.)

    Where there's a Customer or product owner on the project, the tester also serves her. One way is to help her express what she wants in a clear form that can be turned into checkable examples (that is, tests). The tester should also try to think of questions the programmers aren't asking. That implies boning up on the business domain. (Testers often already know it well, if idiosyncratically). I often think of testers as shepherds of the conversation between Customers and programmers. It's a more social role than a lot of testers have had before.

  • Sometimes teams are jumping into Agile to avoid the horror of the previous release. The code was too buggy, or too late, or cost too much per feature, or all three. In such a case, the programmer team is probably not trusted by the business people. If so, trust is an essential deliverable. It's not enough to be better; you have to be visibly better soon. Delivering tested, working features at frequent intervals is a key way to get trust back. Another way is close cooperation with a product owner that demonstrates that the team's orientation is toward helping her meet her goals. But more generally, the team should pay active attention to how well they're doing at building trust, not just at building code.

That's all I can think of now. What else?

## Posted at 21:01 in category /agile [permalink] [top]

array[array.size], (car nil), and a certain sense of balance

Last night, I was disappointed by a bug. It was in some Python code I was writing for the Agile Alliance web site. In Python, indexing one past the end of the array throws an error. In Ruby, it returns nil. Since I'm used to Ruby, I unconsciously took advantage of its behavior.

To fix the problem, I added an if statement. A small price to pay, a Pythonist might say, for conceptual clarity. How is it sensible to talk about the value of something that isn't there?

Perhaps so. But I was at that moment reminded of a wonderful email. I think it was from George Carrette to some Common Lisp mailing list, circa 1984. He wrote of a dream in which he was inspired to eschew all the hackish shortcuts of regular Lisp and translate a program into Scheme. First he stopped taking advantage of the way traditional Lisp lets you ask for the first element of an empty list (that is, (car nil) is nil). Then he stopped treating "false" and "the empty list" as the same thing. Etc.

As he went on, the code kept getting bigger and not so nice, but he maintained the pretense of being an enthusiastic convert to logical purity. It was a tour de force of sarcasm, I thought, and I'd love to get a copy if anyone saved it.

That makes him seem just snarky, but a Google search shows Carrette also involved as a contributor to the Scheme intellectual family tree. The Great Lispers of the Past had that ability to flip rapidly between the ideal and the practical: understanding the power that comes from conceptual simplicity and uniformity, but also realizing that there are non-logical special cases with pragmatic value. Able to be at the most ethereal levels (the Y combinator) at one moment, in amongst PDP-10 assembly code the next.

It's a balance I'd like to have.

(See also the Null Object pattern. How often do null objects actually make sense when you think about them, and how often are they just ways to remove if statements?)

## Posted at 07:20 in category /misc [permalink] [top]

About Brian Marick
I consult mainly on Agile software development, with a special focus on how testing fits in.

Contact me here: marick@exampler.com.

 

Syndication

 

Agile Testing Directions
Introduction
Tests and examples
Technology-facing programmer support
Business-facing team support
Business-facing product critiques
Technology-facing product critiques
Testers on agile projects
Postscript

Permalink to this list

 

Working your way out of the automated GUI testing tarpit
  1. Three ways of writing the same test
  2. A test should deduce its setup path
  3. Convert the suite one failure at a time
  4. You should be able to get to any page in one step
  5. Extract fast tests about single pages
  6. Link checking without clicking on links
  7. Workflow tests remain GUI tests
Permalink to this list

 

Design-Driven Test-Driven Design
Creating a test
Making it (barely) run
Views and presenters appear
Hooking up the real GUI

 

Popular Articles
A roadmap for testing on an agile project: When consulting on testing in Agile projects, I like to call this plan "what I'm biased toward."

Tacit knowledge: Experts often have no theory of their work. They simply perform skillfully.

Process and personality: Every article on methodology implicitly begins "Let's talk about me."

 

Related Weblogs

Wayne Allen
James Bach
Laurent Bossavit
William Caputo
Mike Clark
Rachel Davies
Esther Derby
Michael Feathers
Developer Testing
Chad Fowler
Martin Fowler
Alan Francis
Elisabeth Hendrickson
Grig Gheorghiu
Andy Hunt
Ben Hyde
Ron Jeffries
Jonathan Kohl
Dave Liebreich
Jeff Patton
Bret Pettichord
Hiring Johanna Rothman
Managing Johanna Rothman
Kevin Rutherford
Christian Sepulveda
James Shore
Jeff Sutherland
Pragmatic Dave Thomas
Glenn Vanderburg
Greg Vaughn
Eugene Wallingford
Jim Weirich

 

Where to Find Me


Software Practice Advancement

 

Archives
All of 2006
All of 2005
All of 2004
All of 2003

 

Join!

Agile Alliance Logo