I needed some practice with Java and Eclipse, and I love untangling legacy code, so a coworker recommended me the Gilded Rose Kata. I used Wouter Lagerweij’s Java port as my starting point.
The kata’s README specifies the system’s current behavior, constraints on changing it, and a desired new behavior. Since the spec wasn’t executable, I couldn’t trust its accuracy; since no executable spec existed, I couldn’t verify its accuracy; and since the code was carefully written to be opaque, I didn’t think it was worth making an effort to convince myself I understood it. At this point, I knew only two things:
- Not going anywhere without some characterization tests
- Disallowing something makes me really, really motivated to carry out its nearest permissible approximation
After writing enough characterization tests to load the domain lingo into my head and support (mostly) my reading of the spec, I looked again at the system and at the desired new behavior. Not seeing an obvious place to put it, I instead took the opportunity — afforded by my new tests — to perform some obvious refactorings until I spotted an opening just big enough to test-drive through. And then I chopped up the concerns scattered throughout the characterization tests and reconstituted them, filtered through my newfound understanding of the system, as the one-new-thing-at-a-time unit tests I probably would’ve written to begin with.
None of this is original or special. Anyone who’s danced with legacy code knows the ungainly rhythm of this dance. And while Gilded Rose is a very nicely designed exercise, it doesn’t compare in scope or risk to a real production system. What might be interesting, since it was just an exercise, is how I decided when to stop.
Given the state of my code, I don’t love:
- The stupid not-allowed-to-get-rid-of-or-change-in-any-way
Item
class anditems
property - The Primitive Obsession that remains
- The
qualityIncrement
action-at-a-distance - The hardcoded names of items with special behaviors
- The domain concepts (like “legendary” or “conjured”) not yet explicitly represented in tests or code
And I’m happy with:
- The small size of each step I took
- The utility of my tests as they existed at each step
- The increase in my work speed (partly from tests, partly from plain old practice with the IDE)
- The new feature as safely delivered
- The unit tests as they now stand
- The likelihood that, given some new desired behavior, I or someone else could quickly and safely implement it
Remember, the assignment was to implement a new behavior. All that other stuff I did was enzymatic, enabling the assignment to be digested by my limited arrangement of brain cells. See how mixed that metaphor got? That’s why.
Anyway, I not only implemented the new behavior, but I did it in a way that felt safe throughout and that, were there a next developer, would afford them safety too. If this were a real production system, nothing in the “I don’t love” list would make me want to keep the new behavior out of people’s hands.
So that’s where I stopped. Because software development is always an exercise. We pick a point when we’ve learned enough to produce enough value, a point where more learning isn’t worth more cost, and we stop there. Every product and project leaves value on the table. I won’t pretend my “I don’t love” list doesn’t concern me (it does) or that it doesn’t matter (it does), but I’ll admit it doesn’t concern me or matter enough to be worth more cost. Every chance I get to make that decision wisely is worth practicing.
This has immediately become one of my favorite posts of yours, and one of my favorite posts about software development, and one of my favorite posts about judgment. Thank you!
(Also ikiwiki still needs more mobile-friendliness for the comment form.)
…to you, until I realized it wanted to be a blog post. Happy to hear this expanded version works for you in so many ways, especially because in retrospect I see how personal it is to share code and decisions and invite judgment about them, as is my admission that I only see that in retrospect.
(And yes to both ikiwiki UI concerns.)
J. B. Rainsberger posted a non-legacy variation on the same theme at approximately the same time: You Have to Know When to Stop.
This is good stuff. Once the craftsmanship movement got under way, I started buckling, because of exactly this issue: an overbearing perfectionism leads very easily to a going out of business sale.
On the first day of the classes, in the first hour, I give people what I call “the money principle”.
”We’re in this for the money. None of what we’re about to do and learn is about anything other than shipping more function faster. This is not about being an artist, or a fine craftsman. It’s not about good citizenship. It’s not about preferring “clean” to “dirty”. It’s not about intellectual purity. It’s most assuredly not about ethics or morality. This is about making more money. Everything we learn here we’re learning because it will make us stronger and faster and more successful. Everything in here has been tried and measured and found to do just that. At various points throughout, we’ll re-visit the idea, because it’s so important. For now, just repeat after me: we’re in this for the money, we’re in this for the money.”
An aside: really excellent writing! I get so tired of bad writing in service of good ideas, and this one shines nicely.
Thanks! GeePaw