|No such thing as a small change|
Failure To Refactorby stephen (Priest)
|on May 15, 2002 at 00:23 UTC||Need Help??|
Or, How I Lost My Battle With The Blob
This is a cautionary tale about implementing refactoring in the workplace. I have always been in favor of refactoring bad code, and I think getting others to work with you in doing this is a great idea. However, if you don't exercise a little caution, it can become a major disaster.
I looked up from my screen one day to find my project manager leaning over me. "Stephen," he said, "I need your help." It seemed that management had promised several clients that we would add a huge number of features to The Blob by the end of the month.
Now, "The Blob" is what I'll call the company's main project. It was a sprawling application that had originally been written in Perl 4. It was originally supposed to be a quick one-off application, but it had been growing steadily for the past two years, feeding on a rich diet of quick customer feature requests. Inside, it was a tangled, seething mass of global variables, if-then chains, and copy-and-paste code. Most of the programmers who had ever worked on it refused to ever face it again. A huge effort was underway to rewrite The Blob from scratch, but it had hit significant delays, since the Blob kept growing and changing shape.
It sounded like fun.
See, I had been studying more and more about refactoring. I was pulling together my collection of Perl refactorings, and I figured that this project was a golden opportunity to learn more. It would be absolutely impossible to add the features the customer wanted without some severe refactoring going on. Management wanted to assign me to this project for a week. I figured that that meant three weeks, at which time I'd be back on my own project again. And hey, they needed my help.
There were two programmers who had been working on the code already. Both were very bright. Neither of them knew anything about refactoring, and were very much of the "slap a patch and run" style of coding. I decided on a two-pronged approach. On one level, I would teach them as much as I could about refactoring, automated unit tests, CVS, and all the other tips and tricks I'd picked up. On the second level, I would practice what I preached. I would write unit tests for any code that I touched. I would refactor the complex code I encountered into orthogonal, easy-to-comprehend code that we could then extend more easily. Easy, I figured. I learn by teaching, these guys pick up some valuable skills, the code gets improved, the changes get made. Everybody wins.
I'd been working alone WAY too long. Down in the murky depths, The Blob was chuckling softly to itself.
At first, everything went extremely well. The other coders thought refactoring was a great idea. One of them spent a day reformatting a huge module to suit how he liked his code; I hadn't the heart to explain that that wasn't what I meant by refactoring. Still, the intent was good, and I sent out a steady stream of e-mails explaining what I felt refactoring was. In the meantime, I was pulling apart gooey piles of Blob-flesh into neat, object-oriented modules. I wrote compatibility interfaces to keep the unrefactored code happy, built my automated unit tests, and was pleased to see them run.
The reversal began slowly, almost imperceptibly. The other coders proclaimed my style as an excellent one-- but never adopted it for their own. Automated unit tests were a great idea-- but no one but me ever wrote them. Even more frighteningly, no one but me ever ran them. No one had the time, it seemed. Then my refactoring started to take the blame for things. Despite the fact that I had automated unit tests backing me up, somehow I was always the one called in to fix emergency problems-- and since I had fixed the problems, somehow it was assumed I had caused them.
Blame culture started seeping in everywhere. I would spend time refactoring an area-- then discover that another programmer had reversed the changes. The two other coders and I, normally calm and reasonable people, started sniping behind each others' backs. Sick of hearing false alarms about errors supposedly caused by my code, I became increasingly defensive, and found myself blaming people right back. Communication between the programmers became more and more difficult. The Blob, far from being defeated, was now in our very company culture-- where, in fact, it had been from the start.
It took a long time for me to realize that, although the other coders were intelligent people, they saw the world very differently than I did. I found the pell-mell use of globals disturbing and dangerous, and refactored them into constants and method calls. They were used to the global variables, had never used references, still used 'local'. They found my constants and method calls just as scary as I found their global variables. They weren't used to comments, so they didn't read mine. They agreed with my ideas, but I was asking too much.
I was asking them to adopt and understand my style. And we'd all been working alone for far too long.
The Blob lives on to this day. No one has yet tamed it. Now, long after the blob-taming project has ground to an ambiguous halt, I have some ideas on what I should have done that might have done a better job at Blobicide. Here they are:
Devise A Common Goal
In the beginning, I thought that the other programmers shared my goal-- a well-factored, object-oriented system, easy to expand and debug. However, the other programmers didn't see it that way. They liked my ideas, but they'd been working with this code for months and were used to it. Warnings which set off screaming alarms in my head were peace and quiet to them. My code, on the other hand, was not comprehensible to them. Much as I prided myself on my easy-to-read style and verbose commenting, it might as well have been an obfu to them.
If we had, from the outset, agreed on a certain style, it could have worked. It's easy to think, after one has been working alone for months upon months, that one's coding style is the ultimate one. After all, I understand it as easy as reading; why can't they? (Because they didn't write it, that's why.)
Don't expect your coworkers to change their style if you aren't willing to change yours.
Who's The Boss
In trying to radically change the practices of this little coding team, I was essentially trying to take it over without admitting it. Small coding groups don't always need a clearly defined leader, but subtle power struggles can easily lead to escalations in blame culture. If I had shared my goal of a well-factored system in the beginning, and asked for them to follow me-- and been willing to follow their lead if they weren't willing to follow mine-- perhaps things would have ended differently. We might not have tamed the Blob. But we would have had a goal we all were working toward.
We were hampered by the fact that our official project manager was so hamstrung by management that he was, essentially, reduced to the role of go-between. Management buy-in would've been nice, but management couldn't understand a word of the entire debate that was going on.
Don't Force Knowledge
For me, this is the hardest one to learn. I always like to teach people things that I've learned. However, I've gradually come to realize that no one-- programmers especially-- will learn things when you tell them actively. However, if you indicate that you have this knowledge, give a little demo, then wait for them to ask for more, then you'll never have better students. It's risky. They may never ask. They may learn on their own. They may get something wrong. But if they don't ask, they probably don't want to know.