Wednesday, April 04, 2007

Why You Won't Fix It Later

We've all been there. The deadline is looming, everything is behind schedule, and you're in a rush to finish the FooBar module. You're puzzling over one last glitch. You know how to fix it, but it looks like it will take a minor redesign of the module... probably 4-5 hours of work. You just don't have that kind of time.

Suddenly a clever idea strikes you. Hmmm... it just might work. You realize deep down it's not the right way to do it. Maybe it means adding some temporal/implicit dependencies. ("as long as no one starts calling foo() before initBar(), everything should keep working.")

Maybe it means throwing in a magic string that will only work until January 3 next year. ("No problem, I'll just come back to this code after the deadline. We shouldn't be too busy then.")

Maybe it means breaking the design and making the code untestable. ("Well, it would be nice to have automated tests around this, but it seems to be working. Hopefully no one makes any changes to this code before the deadline.")

Maybe it means living with intermittent bugs. ("Hmmm. The system only times out 8% of the time. We need to figure out why before we go into production, but that should be good enough for testing.")

Maybe it means removing one bug and introducing another one. ("Well, at least we can submit the page now. Hopefully none of the users double-clicks the submit button until I've had a chance to revisit the code after the deadline. I'll fix it later.")

That's the magic word. Later. It makes a great warning signal that you may be heading down a dangerous path. When you catch yourself thinking "I'll fix it later", stop for a minute. You're feeling that little twang of guilt for a reason (even if it's masked by the little ego boost you get from coming up with such a clever workaround). Think about the real consequences of this decision. Will you really get back to it later? What will happen if you don't? What are the risks you're introducing? Ask another developer for an opinion. Ask the customer for an opinion (if you can phrase it in customer language). Think a little longer about other solutions.

There are several popular variants of "I'll fix it later":
  • I'll fix that bug later.
  • I'll verify with the customer that I've built what they actually need later.
  • I'll write unit tests later.
  • I'll remove the fragility from the unit tests later.
  • I'll make the unit tests readable later.
  • I'll make the unit tests fast later.
  • I'll integration test later.
  • I'll usability test later.
  • I'll remove that copy/paste duplication later.
  • I'll bounce my idea/design/code off another developer later.
  • I'll remove that workaround/hot fix/complete hack later.
  • I'll make the code readable/maintainable later.
  • I'll worry about performance/reliability later.
The problem is, we usually don't get around to doing any of those things we plan to do "later". After dealing with the consequences of "I'll fix it later" a few too many times, my friend Dave LeBlanc coined LeBlanc's Law:

"Later equals Never."

Why is this? There are a few reasons that I've noticed:
  1. When you cut corners in order to deliver on time, you're giving management and your customer a false sense of how fast you can reliably deliver. Agile teams use the term 'velocity' to describe the estimated amount of customer value they can deliver per iteration. If there is still work left to be done, you are effectively lying to your customer about how fast you can deliver value. Since your customer thinks you can deliver more than you really can, you will be overloaded with work again next time. You will start accumulating technical debt. There is no easy cure for technical debt (the most common cure being a complete re-write), so prevention is the best medicine. The best way to prevent technical debt from accumulating is to establish realistic expectations about how fast you can effectively work.

  2. When you skimp on automated tests, and even when you write tests but don't ensure they are readable, atomic, and easily-maintained unit tests, you limit your ability to effectively refactor. When you can't easily refactor, it begins to get hard to write readable, atomic, and easily-maintained unit tests. Not only that -- because it's harder to evolve your design, you will face a stronger temptation to fix bugs with workarounds and hacks that will come back to bite you later. You will spend more time debugging and bug fixing, leaving you less time to write tests and refactor. It's a downward spiral that results in reduced velocity.
Agile developers often work with what they call a "definition of done". You are not finished with a feature until it meets the definition of done. It acts as a checklist or set of heuristics that help you realize (and admit) when you have more work to do. A definition of done might include things like these:
  • unit tested
  • verified by customer & customer tests
  • usability tested
  • integrated
  • integration tested
  • documented
  • performance tested
  • peer reviewed (via pair programming or some other mechanism)
  • refactored, readable, duplication-free
  • bug-free
Of course, when you first introduce this idea, your definition won't be this comprehensive. Start small (coded, unit tested, peer reviewed, and refactored makes a good start). Every few iterations, if you are successfully meeting your current definition, add something to it. Eventually you will have a pretty comprehensive definition of done, and each time you finish a feature, you'll have a lot less stuff left over to finish "later".

Do you have any other "I'll fix it later" variants to add to my list? Stories about how planning on fixing something later came back to haunt you, or how adhering to a definition of done saved a lot of potential pain? When is it ok to "fix it later"? Where's the fine line between LeBlanc's Law and YAGNI? Please share your thoughts in the comments section!

16 comments:

MyDarkSecret said...

Good article Ryan. Very true. I think Dave should check for "prior art" on Leblanc's Law though.

Dave LeBlanc said...

Sweet article dude :) Thanks for attributing me btw. I'm actually using this in a discussion about the merits of refactoring. This article is really well thought out, and helps make the point for continual refactoring quite nicely.

I think the whole "later == never" thing started as an admission of my own tendency to procrastinate - but it does seem to have an amazingly broad applicability. A practice I now try is to have a list of "must do" items right under my monitor, and I don't consider the task done until they're all checked off. Things like "fix that kludge you put in BlahClass, ensure code coverage is high, test on Linux, etc".

Oh and "later equals never" comes up with 3 google hits, one of which actually talks about this exact form of procrastination. But I think I started quoting it first :)

James said...

Agile Guru R.P. Cooper hits the nail on the head once again.

Anonymous said...

Where I am, later == never because there is zero time or business justification to fix a problem that is visible only to developers. Once a hack is in it's done until something breaks "later". No matter that it will take ten times as long to correct it then. The project schedule will never and could never contain line items for "fixing the code". No one (except the developers (well only one, me) cares. So at the time it's broke, it's "now or never" not "now or later".

Alex said...

Great post! I posted some comments on my blog at Pure Danger Tech.

fsilber said...

It depends upon your goals. If you want to remain a programmer, then follow this advice. If you want to make money then you _should_ give your employer a dishonest exaggeration of the speed at which you can get things done. Then he'll give you a raise, assign you new development, and turn your buggy monstrosities over to the "slower" programmers for maintenance. Then, before it all catches up with you, move into management. Tell people, "I got the code out the door while the window of opportunity was still open."

Stucco said...

Please create a printer stylesheet or an easy way to print. I use firefox and can't print your article.

Ryan Cooper said...

Thanks for the suggestion, Stucco. I'm running into some kinks trying to create a print stylesheet that works with my blogger template, but I'll put more time into it later this evening.

Anonymous said...

Horror stories that come back to bite?
How about three that collide?
Mine: writing C code for the first time, needed a library up ASAP, didn't know which flag to set on file create, so passed a "-1" to it and set all flags.
Including global write and setuid.
Bad, but not yet a disaster.
Second person: has lots of permission problems with a system, so he says "the app needs to run as root".
Worse, but not yet a disaster.
Third person: we need to be able to access this from anywhere on the internet, so lets NFS export world mount world write.
Now we have: NFS world mount world write, process creating files as root, with other write and setuid set. Result: owned box. All three were a Temporary fix, we'll come back to fix this before it goes into production.

scawa said...

The attitude that "we will fix it later" is why I no longer work for someone else as a developer.

After retiring from 20 years of service in the military, I searched for a job where "doing it right the first time was a priority." Everyone said that it was, but I found that it was never the case.

Deadlines would loom, and my part of the project was done correctly and Unit tested and worked, so I would be assigned to help other younger programmers meet their deadlines. Their solutions were usually a "quick fix to get it out the door."

When the deadline was met, the younger programmers usually went on to other projects, while I was again assigned to fix the parts of the project that didn't work.

I got tired of fixing other people's code, so I now develop my own software, for my own purposes. It is speced out, tests are written before the code, the code is Unit Tested and when implemented, it works first time every time. In the military, I also learned the concept that it should be fully documented, so if something happens to me, someone else can take up the torch and run with it.

And did I say that I don't have to fix someone elses code :)

Ryan Cooper said...

Thanks, all, for sharing your comments and stories!

P.S. Stucco (and anyone else wondering): I've managed to wrestle my blogger template into a print style. You should now be able to print articles from On Agile pretty cleanly.

Cheers!

Travis said...

But Ryan, you just made yourself a liar...

You said you'd fix the stylesheet "later"... and then you did!

:)

Great article, BTW.

Ryan Cooper said...

Thanks Travis!

I guess the important thing is I designated a specific later ("later this evening"), not just the nebulous catch-all "later".

Neill said...

Very pertinent advice, thank you! I think it extends to everything and not only development! We run a fairly complex IP PBX, which I reconfigure to cater for various connection options, such as softphones. I test from my side and ask the user to test from their side as soon as they are set up. Frequently, they don't, I don't hear from them until weeks later and then they find it doesn't work! In one laughable case, the user phoned me, by an alternate means, to say his softphone wasn't working, so I made a connection and he answered with the silence of surprise, which all goes to show ....! (I have a saying too: The client is not usually right; but he or she has rights! And one of those is to be kept accurately informed of progress, problems and limitations.)

J. B. Rainsberger said...

Have you tried using a reminder service like backpackit to help with this? I haven't yet, but I imagine it'd be a little harder to ignore those e-mails than the stack of "we should really do this" cards on the wall.

Pawn said...

Well, what can I say... there are 1,129 "todo" items in the 5500 classes of the project I am currently working on, and there are probably even more things left for "later"