My name isn't Hank, but it's not being used on Wiki right now, and I'd rather not use my own name. This is the story of my project, how I tried to introduce some ExtremeProgramming practices into it, and how that worked out.
It was a small project: just me and the technical lead, who doubled as the project manager. Our funding was limited, our goals were diffuse, and we were anything but green-field: the code I worked on had been written by a (shudder) Mechanical Engineer.
The code was in Java, and a lot of it was GUI code -- something like 60% of the classes were GUI-related, the rest being domain-model stuff or simple file I/O stuff.
Our environment definitely qualified us for the ExtremeProgrammingInEnemyTerritory merit badge: the Company had a Software Process, and everyone had to follow it. Mostly they followed it by writing memos about why they were varying it in key ways -- that is to say, they followed it by explaining why they weren't following it. They did so Repeatably, which apparently made their customers happy.
We hid our copies of ExtremeProgrammingExplained, which made the process people happy.
DonWells says, "Identify your biggest problem; then solve it the XP way." Our biggest problem was that we were doomed, but we didn't know that, so we didn't solve it the XP way.
Our biggest problems that we knew about were:
An analysis of what we did, practice-by-practice, follows -- including how well we followed each practice, and how well it seemed to work:
The PlanningGame: we managed to do this on the sly. First, we took the list of bugs and feature requests we'd been given and turned them into UserStories. Then we brainstormed a couple more UserStories, or gleaned them from notes we'd made on previous forays.
Then we estimated all of them. We decided to first come up with an estimate in ideal programmer hours, then multiply by 2.5 to get actual programmer hours, then write that figure on the card -- we didn't want to have to explain (and justify) load factors. We also marked the cards according to risk: we just tagged the ones where we weren't very sure of the estimate as "risky".
Next, we went to Business: our manager. We took him on a brief tour of all the cards, explaining what each one was, and how it related to the list he'd given us. Then we asked him to sort them into three piles: stuff that had to get done, stuff that would be good to get done, and stuff that wasn't really important.
It actually surprised me when he resisted. Maybe it was that there were so many cards; maybe it was that they weren't in a spreadsheet; maybe it was just an unfamiliar process, and therefore he thought it would be hard. But we held our ground, and stressed that it could be done quickly, with us right there to answer questions, and he did it.
As I recall, his must-do pile added up to right around 160 real hours of work. So we promised him that, in exactly two weeks, we'd have all those things done, and would start in on the next pile.
Since we were a small team, we didn't do iteration planning; we just treated UserStories as tasks, and a two-week iteration as a release. Releasing for us is just tossing everything into a jar file and burning a CD, so this wasn't crazy.
We never really had anyone tracking us, but our estimates were mostly on-target. We finished one big task much sooner than we thought we would, and spent time improving it. In retrospect, we should have split it into smaller tasks, and asked which ones were more important. But we did get informal steering from Business, which provided much the same information. Incidentally, it was one of the tasks we'd marked as "risky", so we didn't feel bad about having overestimated it.
Total time spent doing the PlanningGame: 9 or so man-hours. Very reasonable, in our view.
SmallReleases: This was one of the things that helped me sell XP to my teammate. Since we were never quite sure how much effort we'd be able to put in, it was a Good Thing to always have a stable system not too far behind us (the product of the last release), or not too far ahead of us (if we cut the current release's scope to what's already been done, and spend our last hours wrapping it up).
Metaphor: We never really had a "simple shared story of how the whole system works". However, (a) we all knew pretty well how the system had worked in previous releases, and (b) the Domain Model was very straightforward, so much so that I'm not sure there is a simpler metaphor for it. Anyway, all of our disagreements were about details of what it should do, which the system metaphor wouldn't have helped with (but a real Customer would have).
SimpleDesign: Too late, in some places. But it wasn't a forbiddingly large system, and we did keep the parts we added simple. Hell, we ended up using Strings for something we never thought they'd work for.
UnitTests: We loved these. I'd used JUnit on another project, and taught my teammate about it in our first PairProgramming session.
We followed the advice in ExtremeProgrammingExplained of writing UnitTests only for the code we were going to modify. When we used them, it made us a lot more confident that our changes weren't going to break anything. On the other hand, sometimes we had to refactor just to make writing the tests possible; we tried to use only obviously safe refactorings for this (e.g. MoveMethod, plus a search of the code to find the couple of placed the method was called).
Truth be told, we slacked off on UnitTests for a lot of our maintenance work. Our attitude was basically, "If we make this change without a UnitTest, we can't be sure it'll work afterwards -- but we're not that sure it works now, so what the hell."
On the new pieces, though, we used them religiously. We didn't write a single line in the new parts unless we needed it to make a unit test pass. We even came up with clever ways to test hard-to-test things. In retrospect, CodeUnitTestFirst made us DesignForTestability. We fell off the wagon a little bit by making some of our unit tests spit out internal state for our perusal -- they still checked themselves for passing or failure, but we had more info to look at if we wanted to. We cleaned these tests up at the end.
AcceptanceTests: None. Probably our biggest deviation from XP.
Refactoring: This was our trusty machete in the jungle of existing code. (In fact, it was this program that had led me to engage in a major refactoring episode for the first time, before I'd even known there was a name for it.) We tried to do it safely, which didn't always mean using UnitTests (see above). We also weren't afraid to intensively refactor certain smallish areas of the code.
The difference in changeability was immediate and obvious. Many of our optimizations wouldn't have been possible had we left the code structured as it had been. And we ended up taking a lot of pointless old optimization attempts out.
We did a lot of the refactoring solo. It didn't feel slower at the time, but I kind of wonder what great time-saving leaps my teammate would have spotted if he'd been there while I was refactoring.
PairProgramming: The morale boost alone would have made this worth it. Having a very smart human being to talk to about what exactly the problem at hand is and how to simply solve it made me much more enthusiastic about coming into work. I'm not sure why my teammate also became more enthusiastic :-).
The peer-pressure aspects were heightened for me, since I was trying to introduce my teammate to XP: it Just Wouldn't Do for me to skip a UnitTest, or let one fail, or put some code in on speculation.
I'm also convinced we were much more productive in our new development. We spent about 30 hours developing one new chunk; I'm very sure it would have taken me at least two weeks (80 hours) to develop the same functionality solo, and I'm far from sure it would have been of the same quality (we found no bugs in that piece that got past our UnitTests).
Even so, we probably spent fewer than half our programming hours doing pair programming. We both had some other commitments; travel got in the way; maybe we even had so much fun that we felt the need to go do real (i.e. tedious) work.
CollectiveCodeOwnership: There were two of us. We sort of did this by default -- although we did consciously avoid saying, "You do that stuff, and I'll do this stuff." And it helped that we were allowed to change anything we wanted (i.e. no other group owned any of the code).
ContinuousIntegration: Again, there were two of us. We had a single development stream, so even when we did some solo work, we were integrating it with what little other change was going on. In fact, we were even more continuous than most XP projects -- but that's just because there was so little to integrate.
FortyHourWeek: Not a problem. We would have been in trouble if we'd tried to work more than forty hours, since we had to charge every hour to the program. And not all of our forty per week were on this project.
OnsiteCustomer: To the extent that we had a customer, he was on-site. Our manager basically knew the problem domain, and was familiar both with the program and with a couple real-world applications of it. But he never did see eye-to-eye with us about how to apply the software. On the plus side, he was usually available to answer questions (e.g. "Is this fast enough, or will the users get annoyed?"; "Do you need to able to control these things, or not?"; "Can you give us some realistic data?"). So the problem was more with the customer part than with the on-site part.
CodingStandards: We never really hashed any out; we just left the braces where the text editor put them. The one thing the old code had going for it was that it followed Sun's suggested coding standards. Since we had no real conflicts, and we resolved those amicably and arbitrarily, I think we got all the benefit of Coding Standards without the overhead of actually specifying one.
Back to a chronological account: we got done with our first iteration (yay).
Then we discovered a new kind of risk in software projects. We all know about technical risk: will this technology work? And market risk: are there people who really want this software? What we faced was accounting risk: is this money that looks like it's in our budget really in our budget, or did some scum-sucking weasel swipe funds from our last funding source, leaving us with large past obligations that we have to pay out of our current budget?
The developers in the audience won't be surprised when I reveal that the latter was, in fact, the case. So we're part-way into the second iteration when we discover that it's going to be very, very short.
We kind of ditched PairProgramming at this point, since we couldn't afford to keep both of us plugging away. And the PlanningGame: I just picked a few things that I thought would improve the product and be quick to implement, and did them. Right before an actual release is the right time to do optimizations (which is mostly what they were), anyway.
SmallReleases was very helpful at this point: first, I knew it wasn't critical to finish the second iteration, since the product of the first iteration was a stable, working system with none of the bugs left from the prior version, and some useful new functionality. Second, since we were only a little ways into an iteration that was going to be short anyway, cutting it shorter was easy. Plus we felt like we'd planned for this (though we'd expected it somewhat later), which took some of the dazed shock out of our faces (leaving more room for annoyed grimaces).
And, of course, the PlanningGame (even to the small extent that we'd played it) had ensured that the most valuable set of stories was already finished. We didn't have to watch the baby get defunded with the bathwater.
Even so, morale took a hit. And the team, having no more funding, is no more.
Well, that's my story, and I'm stickin' to it. To summarize:
Sad story, but admirably told. Thanks.
AcceptanceTests: None. Probably our biggest deviation from XP.
You literally had no automated acceptance tests of the system? That sounds kind of scary.
What about tests from earlier (non-XP) versions of the system? Were any of them automated? Could you have automated them? For instance: test 17 says when you process this data, you should get this output; to automate, just make a file for the input, a file for the expected output, and byte-for-byte compare them.
How did you end up proving that the system did what it was supposed to when you were done? Manual tests of everything? Manual tests of just the areas you (think you) changed?
On another note: the manager who resisted the Planning Game (more precisely, the Commitment Schedule phase) -- what did he think of your estimates? Usually, I find that managers think perfect-time estimates are reasonable (!), and load-factored ones (even using 2.5, instead of 3) are padded. (Although I remember working for one small company where the non-tech people joked that writing software always seemed to take exactly three times as long as programmers said it would...)
I realize it must have been a tough project all around; thanks for sharing your experiences and lessons learned.
-- GeorgePaci
For some reason, this subject and story brings to mind PjPlauger's satirical "book review" for The Programming of Art's Computer. As Plauger points out in a roundabout way, this sort of project is doomed from the start, either running out of money before anything worthwhile is done, or turning into a CreepingFeaturitis quagmire as JustOneMore feature request is added again and again. It sounds as if you did as well as could be done under the circumstances.