views:

933

answers:

10

Our code sucks. Actually, let me clarify that. Our old code sucks. It's difficult to debug and is full of abstractions that few people understand or even remember. Just yesterday I spent an hour debugging in an area that I've worked for over a year and found myself thinking, "Wow, this is really painful." It's not anyone's fault - I'm sure it all made perfect sense initially. The worst part is usually It Just Works...provided you don't ask it to do anything outside of its comfort zone.

Our new code is pretty good. I think we're doing a lot of good things there. It's clear, consistent, and (hopefully) maintainable. We've got a Hudson server running for continuous integration and we have the beginnings of a unit test suite in place. The problem is our management is laser-focused on writing New Code. There's no time to give Old Code (or even old New Code) the TLC it so desperately needs. At any given moment our scrum backlog (for six developers) has about 140 items and around a dozen defects. And those numbers aren't changing much. We're adding things as fast as we can burn them down.

So what can I do to avoid the headaches of marathon debugging sessions mired in the depths of Old Code? Every sprint is filled to the brim with new development and showstopper defects. Specifically...

  • What can I do to help maintenance and refactoring tasks get high enough priority to be worked?
  • Are there any C++-specific strategies you employ to help prevent New Code from rotting so quickly?
+1  A: 

I recommend keeping track of how many bugs and code changes involve your "old code" and present this to either your manager or to your fellow developers at your next team meeting. With this in hand it should be simple enough to convince them that more needs to be done to refactor your "old code" and bring it up to par with your "new code".

It would also be prudent to document the parts of your "old code" that are most difficult to understand. These would also be the parts of your "old code" that you should be refactoring first once you get the approval.

Bernard
+2  A: 

It is hard to tell much from the information you give. Some questions I would have is a logical reason to be writing new code is to replace the old code. If that is what you are doing, abandon the old code.

Is it also old code that has showstopper defects? If so where are they coming from? Old code does not have "showstopper" defects, it just grinds closer and closer to a halt usually. It is old code after all - it should have the same old defects and the same old limitations, not stuff that has to be looked at right away. Showstopper defects are new code defects. It sounds like there is active development on in the old code.

If you are writing all this new code on top of old code that sucks, with no plans to fix it once and for all, sorry, there is only so much you can do when you are too busy burying yourself to dig yourself out.

If the latter is the case. you should recognize where you are headed, and try to detach a little. It is going to all collapse eventually, if you plan on being around save your strength for a worthwhile battle.

In the meantime try to pick up some design patterns. There are several that can at least help shield you new code from the old stuff, but still, ultimately it is just hard to write good code against bad code.

And your sprints sound maybe confused. Is there not an overall direction? That should determine how much backlog you have, although things can change month to month, is there not a clear sense of moving towards some final goal?

And new code rotting? The way you prevent that is you have a meaningful design, a meaningful direction, and a quality team that is committed to both the quality of their work and the vision of the design. If you have that, discipline is what maintains quality. If you don't have that sorry, you basically were writing code with no purpose already. It was basically rotten on the vine.

Not being critical, just trying to be honest. Take a deep breath. Slow down. You seem like you need it. Look at what you have written here. It tells nothing. You talk of refactor, scrums, showstoppers, defects, old code, new code. What does any of that mean? It is all jumbled up.

What about "new initiatives versus legacy systems"? "Need to refactor early sprint cycle code in terms of latest understanding etc." Are showstoppers in fact "Early components of the current enterprise initiatives have been released but are experiencing problems and no time is budgeted because of new development".

These would be meaningful concepts. You've given us nothing. I understand it is intense. My sprints are crazy too, we add a lot of back;pg items because we could not get many requirements up front (a lot of my new requirements result from having to also contend with external regulatory bodies, the normal business process is not always available).

But at the same time I am ground down by the sheer magnitude of what has to be done and the time to do it. Everything that is added to my backlog needs to be there. It is crazy, but at the same time I have a very clear idea of where I have been, where I need to go, and why the road is getter harder.

Step back, clear your thoughts, figure out the same - where you have been and where you are going. Because if you know that, it sure is not obvious. If you cannot communicate anything your peers can understand, how far are you going to get with a business manager?

Sisyphus
+16  A: 

Your management may be focused on getting working features into the product, and keeping them working. In this case, you will need to make a business case for refactoring the old stuff, in that by X investment of time and effort you can reduce necessary maintenance time by Y over period Z. Or your management may be fundamentally clueless (this happens, but less often than most developers seem to think), in which case you'll never get permission.

You need to see the business point of view. It doesn't matter to the end user whether the code is ugly or elegant, only what the software does. The cost of bad code is potential unreliability and additional difficulty in changing it; the emotional distress it causes to the programmer is rarely considered.

If you can't get permission to go in and refactor, you can always try it on your own, a little bit at a time. Whenever you fix a bug, do a little rewriting to make things clearer. This may turn out to be faster than the minimum possible fix, particularly in verifying that the code now works. Even if it isn't, it's usually possible to take a little more time on a bug fix without getting into trouble. Just don't get carried away.

If you can leave the code just a little better each time you go in, you'll feel a lot better about it.

David Thornley
I agree this is a good strategy, and it's one that I've employed in the past on the Old Code that was causing me such headaches yesterday. It's a lot better than it used to be, but it's still painful. One developer can only improve so much in baby steps.
Kristo
I've used the "little more than just the fix" with good results before. I also agree on the don't get carried away, I just loathe badly written code :/
Matthieu M.
+1 for *but less often than most developers seem to think*
Binary Worrier
technical debt!
oberhamsi
A: 

You could create diagrams and sketches of how the new code works and how the classes and functions are related to one another. You could use FreeMind or maybe Dia. And I definitely agree with Documenting and commenting your code. I once had a problem with this too. I wrote a font class for J2ME for my own language. It was awful for these reasons that maybe you might also see in your code.

  • No Comments or documentation
  • Less object oriented
  • bad variable / function names
  • ...

But after a few months I was forced to write the whole thing again. Now I've learned to use meaningful variable names that are sometimes VERY long. write comments more than writing codes. And using diagrams for the project's classes and their relationships.

I don't know If it was a real answer but it definitely worked for me. and for old codes you might actually have to reread the whole thing and add comments when you remember the functionalities.

Hope it helped.

Auxiliary
Hudson runs a Doxygen build for us nightly, so like I said, we're in pretty good shape on the new stuff. It's the old code that could really use some clear documentation - and we don't have any time to write it.
Kristo
+1  A: 

Something to try: group your class into - say - worst 10%, best 10%, and the rest. Deliver the lists to your management, saying, "I predict the majority of bugs over the next quarter will be found in the first set." Based on length, cyclomatic complexity, test coverage - whatever tools are handy and comfortable to you. Then sit back and watch - and be right. Now you've got some credibility, some leverage when you say, "I'd like to invest some resources in making our bad code better, to reduce bugs and maintenance costs - and I know where to invest that energy, see?"

Carl Manaster
+6  A: 

Working Effectively with Legacy Code

Don Roby
Definitely +1. It won't magically solve all the problems, but it will provide much better tools for getting there.
Joe White
+9  A: 

Stand Up Meetings

I might go to my mechanic, and we have a little stand-up meeting in the morning:

I tell him I want my wheels aligned, my tires rotated, and my oil changed. I mention that "Oh by the way my brakes felt a little soft on the way in. Could [he] take a look at them? How soon can I get my car back because I need to get back to work?"

He pops his head under my car, pops back up and says my brakes are leaking oil and starting to fail. He will need a part that will arrive at 10:30am. His man won't finish before lunch, but I should get my car back by 1:30pm or so. He's booked solid so he won't be able to do any of the other stuff today, and I will have to book another appointment.

I ask if he can do the other stuff and I come back for the brake. He tells me he really can't let me drive out of there without fixing the brakes because they might cause an accident, but if I want to go to another mechanic, he can call for a tow.

Since the car will be done so shortly after lunch, I ask if his man can take a late lunch so I can get my car back an hour earlier.

He tells me his men come in at 8am and often work into the evening. They earn every break they get, and his man deserves to take his lunch with everyone else.

None of that is what I wanted to hear. I wanted to hear that I would drive out of there in a half hour with my wheels, tires and oil done.

My mechanic was just straight up and honest with me. Are you straight up and honest with your management? Or do you avoid telling them things they don't want to hear?

Unit Testing

I wouldn't touch a line of code I didn't understand, and I wouldn't check in a new line of code I didn't test thoroughly. (At least, not intentionally.)

Your question seems to imply that somehow a large corpus of poorly documented code made it past review without any unit tests. Maybe you participated in that, and maybe you didn't. Everyone involved needs to accept responsibility for that--including management. Regardless, what's done is done. You cannot go back and change it.

However, right now, in the present time, it is everybody's responsibility to stop the behavior that led to the problem in the first place. You say you spent a year working in code that you find difficult to understand and that has no unit tests. During that year, as you worked hard to improve your understanding, how many unit tests did you write to document and to verify that understanding?

As you struggled through the code slowly gaining understanding, how many comments did you add so you wouldn't have to struggle next time?

Scrum Backlog

Personally, I think the term "Scrum backlog" is a misnomer. A list of things to do is just a list--a shopping list if you will. I had a list when I went to the mechanic. My stand up meeting with the mechanic was really more of a sprint planning meeting.

A sprint planning meeting is a negotiation. If your management is time boxing without that negotiation, they aren't managing anything. They are simply trying to cram 10 lbs of shit into a 5 lb sack, and it's your responsibility to tell them so.

When you show up to a sprint planning meeting, you are expected to commit to a body of work, and it's your responsibility to prepare for that. Preparation means having some idea of what you will have to do to complete each item on the list--including the time it takes to understand obscure code and the time it takes to write unit tests.

If someone invites you to a planning meeting where you won't have time to prepare, decline the meeting and suggest when to reschedule so you will have time.

If you have an existing body of code with no unit tests and a feature might conceivably affect the operation of that code, you need to write unit tests for as much of the old code as might be affected. When you commit to writing the feature, you are committing to doing that work. If that leaves you too little time to commit to some other feature, just say so. Don't commit to the other feature.

When you commit to fix a defect, you commit to testing your work. Obviously, that means writing a unit test for the defect. But if it involves old code with no unit tests, it also means writing unit tests for things that aren't broken yet, but might break due to your change. How else will you test the fix?

If your defect list remains a constant size, your team regresses as much as it fixes. Politely explain to whomever needs to understand that unit tests prevent the regressions that currently keep your defect list from shrinking.

If you fail to write those unit tests because you commit to too many features, whose responsibility is that?

Refactoring

When you refactor code, you have to test all of it, and that means writing unit tests for all of it. If you have a large body of code with no unit tests, you will have to write all of those unit tests before you refactor.

I suggest you hold off on refactoring until those unit tests are in place. In the meantime, if you insist on including unit tests in your estimates for the work you commit to, eventually all those unit tests will be there. And then you can refactor.

The one exception to that is refactoring for testability. You may find that some of the code was not designed for test and that you have to refactor for things like dependency injection before you can create your unit tests. When you commit to writing the feature that requires the unit test, you commit to making the code testable. Include that in your estimate when you commit to the feature.

Commitment + Responsibility = Power

You say you are powerless. When you accept responsibility and commit to doing what needs doing, I think you will find you have all the power you need.

P.S. If anyone complains about anybody "wasting time" writing multiple unit tests when fixing a single defect, show them this video on the 80:20 rule and pound "defect clusters" into their brains.

bbadour
I don't disagree with your honest assessments. I'm not blaming anyone for how we got here. Agile software development (scrum + TDD + CI) is a culture shift for our team. We're moving in the right direction, albeit very slowly.
Kristo
"If your defect list remains a constant size, your team regresses as much as it fixes." - I assumed that the list remaining constant size means that novel feature requests are coming in and defects identified as fast as they're processed, not necessarily that new defects are *created* at that rate. Obviously long-term this matched source/sink rate is ideal - you can't clear *more* items than have been raised, so break-even is as good as it gets. At question is whether the typical queue size "should" be 140 items, or 14, or 1400.
Steve Jessop
Kristo, my answer has nothing to do with blame. It's about power. Responsibility, commitment and power.Steve, it's not the constant 140; it's the constant "dozen or so defects." Certainly, it's better that it's stable than if it were growing. But stable still means the team introduces as many as it fixes. Otherwise, as the total number of defects shrinks, the number found would shrink too.
bbadour
+ 1 for the line "Are you straight up and honest with your management? Or do you avoid telling them things they don't want to hear?" It's a hard but absolutely important lesson for developers to learn.
Dan
@Dan, we are, but it doesn't matter. A good portion of New Code is from pop-up tasks that appear one sprint before their deadline. We can push back, but the implication is "do it or we'll find someone who can."
Kristo
A: 

Talk to your Product Owner! Explain that time invested in refactoring the old code will bring him benefit of higher team velocity on new features once this obstacle is removed.

Andy
+1  A: 

Old code always sucks. There's probably some rare exceptions written by people with names like Kernighan or Thompson but, for the typical "code written in an office" stuff, over time it's gonna stink. Developers get more experienced. Newer practices, such as continuous integration, change the game. Stuff get's forgotten. New maintainers fail to grasp designs and wish for re-writes. So best accept this as normal.

Some random things that might help...

  • Talk about it with your team. Share your experiences and your concerns, while avoiding "man your old code sucks" (for obvious reasons) and see what the consensus is. You're probably not alone.
  • Forget about your managers. Don't expose them to this level of detail - they don't need to think about new vs. old code and probably won't understand if they do. This is a problem for your team to tackle and, if necessary, to make your PO aware of
  • Be open to the possibility that you may be able to throw stuff out. Some of that old code probably relates to features that are no longer being used or failed to be adopted by users in the first place. To make this work for you, you really need to go a level higher and think in terms of where the code really delivers user or business value vs. where it's just a ball of mud that no one is brave enough to take a decision on. Who dares, wins.
  • Relax your view of architectural consistency. There's always a way to tap into a working system with new code somewhere, and that may allow you to slowly migrate to a newer, smarter approach, while preserving the old long enough not to break existing things.

Overall, winning in this kind of situation is less about coding skills and much more about smart choices and handling the human aspects.

Hope that helps.

HarryF
A: 

Other than the approaches mentioned above which are good, you can also try these:

For keeping future code clean

  • Try pair programming, at least for parts that make sense. It's an effective way of getting reviewed, refactored code a practice.
  • Try to get refactoring onto the definition of "done". Then it will be part of the estimation process and allotted accordingly. So the definition of done might include: coded, unit tested, functionally tested, performance tested, code reviewed, refactored, and integrated (or something like this).

For Cleaning up the old code:

  • Unit tests are great for helping you refactor and figure out how things work.
  • I agree with the comments that a business case needs to be made for large-scale refactoring. But, small-scale refactoring could be easily included in the estimate and will provide immediate return. i.e.: I spend 2 hours rewriting a piece but I would have spent that time looking for bugs anyway.

You may also want to consider getting the product owner and scrummaster to capture a separate velocity for the old code vs the new code, and use that accordingly.

Angelok