views:

1708

answers:

26

Say there are two possible solutions to a problem: the first is quick but hacky; the second is preferable but would take longer to implement. You need to solve the problem fast, so you decide to get the hack in place as quickly as you can, planning to start work on the better solution afterwards. The trouble is, as soon as the problem is alleviated, it plummets down the to-do list. You're still planning to put in the better solution at some point, but it's hard to justify implementing it right now. Suddenly you find you've spent five years using the less-than-perfect solution, cursing it the while.

Does this sound familiar? I know it's happened more than once where I work. One colleague describes deliberately making a bad GUI so that it wouldn't be accidentally adopted long-term. Do you have a better strategy?

+13  A: 

This is a major issue when doing deadline driven work. I find that adding very detailed comments about why this way was chosen and some hints at how it should be coded help. This way people looking at the code see it and keep it fresh.

Another option that will work is add a bug.feature in your tracking framework (you do have one, right?) detailing the rework. That way it is visible and may force the issue at some point.

Craig
Agreed. Add an item to the tracking system. The problem is that usually the interim solution will not be addressed unless there is a significant problem with it related to functionality. Perhaps this is OK, unless you find yourself maintaining unmaintainable code. How do you raise that issue?
s_t_e_v_e
Yes. If you know that a piece of code is going to fail, then it's a bug even if it hasn't failed yet, and it should be tracked as one. Another psychological advantage to this is that your project's bug count goes up when you make sloppy fixes.
Robert Rossney
+4  A: 

It is a hard call. I have done hacks personally cause, sometimes you HAVE to get that product out the door and into the customers hands. However, the way that I take care of it is to just do it.

Tell the project lead or your boss, or the customer: There are some spots that need to be cleaned up, and coded better. I need a week to do it, and it is going to cost less to do it now, then it will be to do it 6 months from now, when we need to implement an extension onto the subsystem.

MagicKat
What in your programming experience leads you to believe that a poorly programmed solution will be quicker to implement, test and release than a well programmed solution? Seriously. I've seen people say it, usually when they don't know how to do it right.
Bill K
@Bill K: Poorly programmed was never in my statement. Less desirable solution perhaps. Its about debt, you take no debt, I take small debts that I can pay off quickly. I can always refactor later, the customer doesn't care, but they care about the report they need for the board in one hour.
MagicKat
@Bill K: so in this case, sure the code base isn't DRY, but you are a hero to the customer. Then once in the customers hand, you can easily refactor the code. Whats more important in the LONG TERM, a nonDRY code base for an hour but a happy customer, or a DRY code base and an unhappy customer.
MagicKat
+5  A: 
  • I make sure that I'm vocal about the priority of the long term fix ESPECIALLY after the short term fix has gone in.
  • I detail the reasons why it's a hack and not a good long term solution and use those to get the stakeholders (managers, clients, etc) to understand why it needs to be fixed
  • Depending on the case, I may even inject a bit of worst case scenario fear in there. "If this safely line snaps, the whole bridge could collapse!"
  • I take responsibility for coming up with a long term solution and make sure that it gets deployed
Wayne
+17  A: 

Strategy 1 (almost never selected): Don't implement the kluge. Don't even let people know it's a possibility. Just do it the right way the first time. Like I said, this one is almost never selected, due to time constraints.

Strategy 2 (dishonest): Lie and Cheat. Tell management that there are bugs in the hack, and they could cause major problems later on. Unfortunately, most of the time, the managers just say to wait until the bugs become a problem, then fix the bugs.

Strategy 2a: Same as strategy 2, except there really are bugs. Same problem, though.

Strategy 3 (and my personal favorite): Design the solution whenever you can, and do it well enough that an intern or code-monkey could do it. It's easier to justify spending the small amount of code-monkey money than to justify your own salary, so it might just get done.

Strategy 4: Wait for a rewrite. Keep waiting. Sooner or later (probably later), someone is going to have to rewrite the thing. Might as well do it right then.

John Biazo
Only problem with Strategy 4 - when you finally get around to doing the rewrite, there are also major time and budget constraints - so the hack gets copied over - or other kludgy solutions get put in the rewrite.
Ken Ray
A problem with strategy 4 is that after 5 years of work-arounds and fixes, something will get lost in translation when you are ready for the re-write.
Giovanni Galbo
Exactly. Nothing will make business stakeholders angrier than paying for a 6-month re-write that basically gets them the same application they had, but with extra bugs. Startegy 5: gradually and carefully refactor things when it makes sense to do so.
Eric Z Beard
+4  A: 

Usually problems like this arise from bad communication with management or the customer. If the solution works for the customer then they see no reason to ask for it to be changed. So they need to be told about the tradeoffs you made beforehand so they can plan extra time to fix the problems after you've implemented the quick solution.

How to solve it depends a bit on why it's a bad solution. If your solution is bad because it's hard to change or maintain then the first time you have to do maintenance and have a bit more time then that is the right time to upgrade to a better solution. In this case it helps if you tell the customer or your boss that you took a shortcut in the first place. That way they know that they can't expect a fast solution next time around. Cripling the UI can be a good way to make sure the customer comes back to get stuff fixed.

If the solution is bad because it's risky or unstable then you really need to talk to the person doing the planning and have some time planned in to fix the problem asap.

Mendelt
+1  A: 

Great question. This bothers me a lot, too - and most of the time I'm the sole person responsible for prioritizing issues in my own projects (yep, small business).

I found out that the problem that needs to be fixed is usually just a subset of the problem. IOW, the customer that needs an urgent fix does not need the whole problem to be solved, just a part of it - smaller or larger. That sometimes enables me to create a workaround that is not solution to the complete problem but just to the customer's subset and that allows me to leave the bigger issue open in the issue tracker.

That may of course not apply at all to your work environment :(

gabr
+7  A: 

YOU DON'T DO INTERIM SOLUTIONS.

Sometimes I think programmers just need to be told this.

Sorry about that, but seriously--a hacky solution is worthless and even on the first iteration can take longer than doing a portion of the solution correctly.

Please stop leaving me your crap code to maintain. Just ALWAYS CODE IT RIGHT. No matter how long it takes and who yells at you.

When you are sitting there twiddling your thumbs after delivering early while everyone else is debugging their stupid hacks, you'll thank me.

Even if you don't think you are a great programmer, always strive to do the best you can, never take shortcuts--it doesn't cost you ANY time to do it right. I can justify this statement if you don't believe me.

Bill K
There are a few rare cases where a hack is right - generally where the Gantt chart says that while you do it properly, 23 other people will be sitting around doing nothing. Also, the discussion applies to prototypes as much as to hacks IMO.
Steve Jessop
There is no such thing as a prototype. Prototype is another name for production code.
Greg D
Note in particular that although it takes you longer in total to do the hack first, then do it right, it might shorten the project time. Everyone else can at least start testing their code against your hack, which might for example be functionally complete but with unacceptable performance.
Steve Jessop
@Greg D: A prototype is *a* product, but it isn't *the* product. Unless of course you have no means of preventing what the questioner want to prevent, which is why IMO the issues are similar.
Steve Jessop
Don't make a prototype, make a spike. A spike is a vertical piece of production code that implements a function, and it should never take more time than prototype code--Hacking being quicker is a complete illusion, even in the short term.
Bill K
Well, for example I've occasionally seen useful prototypes made in 10 minutes out of pieces of paper. So I can't believe no prototype ever helps, or that no "hack" ever speeds the critical path. I'm prepared to accept that "spikes" might be the better strategy on average.
Steve Jessop
A GUI description on paper is really a design, not a prototype (Although I've heard it called that, it's completely different from what we're discussing). If you are going to prototype a GUI on your computer, you should code it correctly.
Bill K
+6  A: 

Suddenly you find you've spent five years using the less-than-perfect solution, cursing it the while.

If you're cursing it, why is it at the bottom of the TODO list?

  • If it's not affecting you, why are you cursing it?
  • If it is affecting you, then it's a problem that needs to be fixed NOW.
James Curran
Often the business's needs are not aligned with your pain. IT serves the business. If what we do isn't advancing their goals, then we risk a dangerous disconnect. Occasionally we get the chance to do, IT-Only projects, but even then...
Agreed, management doesn't care if it's affecting us, as long as their business model continues to function. You have to come up with a business case as to why it needs to be fixed NOW or your pained wailing will fall on deaf ears.
Adam Bellaire
If the business needs are not aligned with your pain, then you "shouldn't" be replacing the hack with the proper version, and this question comes down to "how can I subvert my employer's ends to serve my own?". Answer: form a union.
Steve Jessop
+2  A: 

I try to build the hacky solution so that it can be migrated to the longterm way as painlessly as possible. Say you got a guy who is building a database in SQL Server cuz that's his strongest DB, but your corporate standard is Oracle. Build the db with as few non-transferable features (like Bit datatypes) as possible. In this example, it's not hard to avoid bit types, but it makes transitioning later an easier process.

+12  A: 

The only time you can ever justify fixing these things (because they're not really broken, just ugly) is when you have another feature or bug fix that touches the same section of code, and you might as well re-write it.

You have to do the math on what a developer's time costs. If software requirements are being met, and the only thing wrong is that the code is embarrasing under the hood, it's not really worth fixing.

Whole companies can go out of business because over-zealous engineers insist on a re-architecture every year or so when they get antsy.

If it's bug-free and meets requirements, it's done. Ship it. Move on.

[Edit]

Of course I'm not advocating that everything be hacked in all the time. You have to design and write code carefully in the normal course of the development process. But when you do end up with hacks that just had to be done quickly, you have to do a cost-benefit analysis on whether or not it's worth it to clean up the code. If over the lifetime of the application you will spend more time coding around a messy hack than you would have fixing it, then of course fix it. But if not, it's way too expensive and risky to re-code a working, bug-free application just because looking at the source makes you ill.

Eric Z Beard
This is very true.
MagicKat
"If it's bug-free and meets requirements, it's done. Ship it. Move on." I disagree. I am currently working with a codebase that is so convoluted that any simple change takes a matter of days rather than hours. It doesn't have "bugs", but its poor structure imposes an ongoing cost.
Kristopher Johnson
Well, you don't want to just hack *everything* in, then you do end up with a mess. It's all cost-benefit: if it's slowing you down so much then it does need to be re-coded, if the time it will save over the lifetime of the app works out to outweigh lengthy bugfixes without a redesign.
Eric Z Beard
I think your comment is right, Eric. Maybe you could edit your answer to reflect it?
Tommy Herbert
+14  A: 

Here is a great related article on technical debt.

Basically, it is an analogy of debt with all the technical decisions you make. There is good debt and bad debt... and you have to pick the debt that is going to achieve the goals you want with the least long term cost.

The worst kind of debt is small little accumulating shortcuts that are analogous to credit card debt... each one doesn't hurt, but pretty soon you are in the poor house.

Mike Stone
That's a great article - thanks. When I asked the question, I was thinking of debts of type II-A-1 (the car loan analogy), rather than II-A-2 (credit cards). I see now that such debts can be good and that the organisation can explicitly plan for them.
Tommy Herbert
+4  A: 

Good luck. In my experience this is almost impossible to achieve.

Once you go down the slippery slope of implementing a hack because you are under pressure then you might as well get used to living with it for all time. There is almost NEVER enough time to re-work something that already works, no matter how badly it is implemented internally. What makes you think you will magically have more time "at some later date" to fix the hack?

The only exception I can think of to this rule is if the hack completely prevents you from implementing another piece of functionality that is needed by a customer. Then you have no choice but to do the re-work.

unintentionally left blank
"What makes you think you will magically have more time 'at some later date' to fix the hack?"I now know how to answer this: the business can make a decision to retire the technical debt when the cost of servicing it outweighs the cost of fixing it. See the article that Mike Stone linked to.
Tommy Herbert
A: 

We had to do this once - make a short term demo version that we knew we did not want to keep. The customer wanted it on a winTel box, so we developed the prototype in SGI/XWindows. (We were fluent in both, so it wasn't a problem).

Dan Hewett
+33  A: 

Write a test case which the hack fails.

If you can't write a test which the hack fails, then either there's nothing wrong with the hack after all, or else your test framework is inadequate. If the former, run away quick before you waste your life on needless optimisation. If the latter, seek another approach (either to flagging hacks, or to testing...)

Steve Jessop
That is possible the best solution. Failed tests are often high priority.
Gamecat
Exactly because failed test are high priority, this is not a solution to the problem but a good way to antagonize your team and/or manager.
Michael Borgwardt
If it antagonises my team, then evidently they disagree with my "plan to start on the better solution". If they're right, then the "quick" version is not a hack, it's a valid solution. Trying to design a test fail is the best way to work out whether my code is good enough as it is.
Steve Jessop
... my general point being that if the "quick" fix is good enough, then the question poses a false problem, that should not be solved: "how should I do work that doesn't need doing?". If the "quick" fix is not good enough for release, then colleagues will not be antagonised by a test which says so.
Steve Jessop
... unless my colleagues are wrong and/or stupid, of course. But "how do I persuade my colleagues that they're wrong and I'm right" is IMO a much broader and more general subject, and not worth specialising to this individual case. So I'll assume my colleagues are behaving fairly reasonably.
Steve Jessop
What if it's a UI problem? Good luck writing a test case. What if it **does** work perfectly right now, but it's an unreadable, unmaintainable Rube Goldberg machine that you could never document properly and is poorly encapsulated and therefore affects thousands of lines of code? Good luck the next time any of those thousands of lines of code need to be modified. What if the hack makes parts of the code virtually impossible to test?
dsimcha
@dsimcha: if it's a UI problem then some kind of acceptance testing will fail. Nasty code isn't a test failure, but any bug fixes will be dependent on cleaning it up, and those bugs will have associated tests. If the code is impossible to test, then a "missing test, cannot ship without" is as good as a failure. My general point is that code faults can be captured, and a standard set for how you'll know when the code no longer has that fault. You can always do better than just making a note somewhere that there's a problem, and forgetting about it, which is what the questioner wants to avoid.
Steve Jessop
Also please note that I am not proposing, in 8 words, to provide a silver bullet development methodology which stupid people can read once and then follow mindlessly and thereby track and fix all possible code defects ;-) Just use the mechanism you have (tests), intelligently, to measure defects, which after all is what they're for. If you truly can't write a test, OK, look for some other way of determining whether the code contains the defect. If the code can ship with the defect, think about "optional" tests. But above all, quantify the problem objectively.
Steve Jessop
A "quick fix" is a fix that the writer knows is likely to present new and additional problems down the road. While YAGNI tells us not to implement solutions now that we would only need further down the road, it does not necessarily apply here. Code with lack of currently-unneeded features is OK. Code with problematic edge cases, which may work with today's inputs but not with tomorrow's, is not OK, and should be *documented* with a failing test case.
Justice
A: 

Confession:

I have used '#define private public' in C++ in order to read data from some other code layer. It went in as a hack but works well and fixing it has never become a priority. It is now 3 years later...

One of the main reasons hacks do not get removed is the risk that one introduces new bugs while fixing the hack. (Especially when dealing with pre-TDD code bases.)

James Dean
+1  A: 

This reminds me of the story of "CTool". In the beginning CTool was put forward by one of our devs, I'll call him Don, as one possible way to solve the problem we were having. Being an earnest hard-working type, Don plugged away and delivered a working prototype. You know where I am going with this. Overnight, CTool became a part of the company work flow with an entire department depending on it. By the second or third day, bitter complaints started streaming in about CTool's shortcomings. Users questioned Don's competence, commitment and IQ. Don's protests that this was never supposed to be a production app fell on deaf ears. This went on for years. Finally, someone got around to re-writing the app, well after Don had departed. By this time, so much loathing had become attached to the name CTool that naming it CTool version 2 was out of the question. There was even a formal funeral for CTool, somewhat reminiscent of the copier (or was it a printer?) execution scene in Office Space.

Some might say Don deserved the slings and arrows for not making it go right to fix CTool. My only point is that saying you should never hack out a solution is probably unjustifiable in the Real World. But if you are the one to do it, tread cautiously.

Andrew Cowenhoven
A: 

My answer is a bit different from the others. My experience is that the following practices help you stay agile and move from hackey first iteration/alpha solutions to beta/production ready:

  1. Test Driven Development

  2. Small units of refactoring

  3. Continous Integration
  4. Good Configuration management
  5. Agile database techniques/database refactoring

And it should go without saying you have to have stakeholder support to do any of these correctly. But with these products in place you have the right tools and processes to quickly change a product in major ways with confidence. Sometimes your ability to change is your ability to manage the risk of the changes and from the development perspective these tools/techniques give you surer footing.

DanielHonig
+1  A: 
  • Get it in writing (an email). So when it becomes a problem later management doesn't "forget" that it was supposed to be temporary.

  • Make it visible to the users. The more visible it is the less likely people are going to forget to go back and do it the right way when the crisis is over.

  • Negotiate before the temp solution is in place for a project, resources, and time lines to get the real fix in. Work for the real solution should probably begin as soon as the temp solution is finished.

CrashCodes
+2  A: 

Educate whoever is in charge of making the final decision why the hacky way of doing things is bad in the long-run.

  • Describe the problem in terms they can relate to.
  • Include a graph of cost, productivity, and revenue curves.
  • Teach them about technical debt.
  • Regularly refactor if you're pushed forward.
  • Never call it "refactoring" or "going back and cleaning up" in front of non-technical people. Instead, call it "adapting" the system to handle "new features".

Basically, people who don't understand software don't get the concept of revisiting things that already work. The way they look at it, developers are like mechanics who want to keep taking apart and reassembling the entire car every time someone wants to add a feature, which sounds insane to them.

It helps to make analogies to everyday things. Explain to them how when you made the interim solution, you made choices that suited building it quickly, as opposed to being stable, maintainable, etc. It's like choosing to build with wood instead of steel because wood is easier to cut, and thus, you could build the interim solution quicker. The wood, however, simply can not support the foundation of a 20-story building.

Jonathan Tran
+1  A: 

You file a second very descriptive bug against your own "fix" and put a to-do comment right in the affected areas that says, "This area needs a lot of work. See defect #555" (use the right number of course). People who say "don't put in a hack" don't seem to understand the question. Assume you have a system that needs to be up and running now, your non-hack solution is 8 days of work, your hack is 38 minutes of work, the hack is there to buy you time to do the work and not lose money while you're doing it.

Now you still have to get your customer or management agree to schedule the N*100 minutes of time required to do the real fix in addition to the N minutes needed now to fix it. If you must refuse to implement the hack until you get such agreement, then maybe that's what you have to do, but I've worked with some understanding people in that regard.

dlamblin
+1  A: 

The real price of introducing a quick-fix is that when someone else needs to introduce a 2nd quick fix, they will introduce it based on your own quick-fix. So, the longer a quick-fix is in place, the more entrenched it will become. Quite often, a hack takes only a little bit longer than doing things right, until you encounter a 2nd hack which builds on the first.

So, obviously it is (or seems to be) sometimes necessary to introduce a quick fix.

One possible solution, assuming your version control supports it, is to introduce a fork from the source whenever you make such a hack. If people are encouraged to avoid coding new features within these special "get it done" forks, then it will eventually be more work to integrate the new features with the fork than it will be to get rid of the hack. More likely, though, the "good" fork will get discarded. And if you are far enough away from release that making such a fork will not be practical (because it is not worth doing the dual integration mentioned above), then you probably shouldn't even be using a hack anyways.

A very idealistic approach.

A more realistic solution is to keep your program segmented into as many orthogonal components as possible and to occasionally do a complete rewrite of some of the components.

A better question is why the hacky solution is bad. If it is bad because it reduces flexibility, ignore it until you need flexibility. If it is bad because it impacts the programs behavior, ignore it and eventually it will become a bug fix and WILL be addressed. If it is bad because it looks ugly, ignore it, as long as the hack is localized.

Brian
+1  A: 

Some solutions I've seen in the past:

  • Mark it with a comment HACK in the code (or similar scheme such as XXX)
  • Have an automatic report run and emailed weekly to those that care which counts how many times the HACK comments appear
  • Add a new entry in your bug tracking system with the line number and description of the right solution (so the knowledge gained from the research before writing the hack isn't lost)
  • write a test case that demonstrates how the hack fails (if possible) and check it into the appropriate test suite (i.e. so that it throws errors that someone will eventually want to cleanup)
  • once the hack is installed and the pressure is off, immediately start on the right solution

This is an excellent question. One thing I've noticed as I get more experience: hacks buy you a very short amount of time, and often cost you a huge amount more. Closely related is the 'quick fix' that solves what you think is the problem -- only to find when it blows up that that it wasn't the problem at all.

mm2001
+1  A: 

Setting aside the debate about whether you should do it, let's assume that you have to do it. The trick now is to do it in a way that minimizes long range affects, it easily ripped out later, and makes itself a nuisance so you remember to fix it.

The nuisance part is easy: make it issue a warning every time you execute the kludge.

The ripped out part can be easy: I like to do this be putting the kludge behind a subroutine name. That makes it easier to update since you compartmentalize the code. When you get your permanent solution, you're subroutine can either implement it or be a no-op. Sometimes a subclass can work nicely for this too. Don't let other people depend on whatever your quick fix is, though. It's difficult to recommend any particular technique without seeing the situation.

Minimizing long range effects should be easy if the rest of the code is nice. Always go through the published interface, and so on.

brian d foy
+1  A: 

Try to make the cost of the hack clear to the business folks. Then they can make an informed decision either way.

David Plumpton
+1  A: 

We use Java and Hudson for continuous integration. 'Interim solutions' must be commented with:

// TODO: Better solution required.

Every time Hudson runs a build it provides a report of each TODO item so that we have an up to date, highly visible record of any outstanding items that need improved.

johnstok
+1  A: 

You could intentionally write it in way that is overly restrictive and singe purposed and would require a re-write to be modified.