(Note: This is a post from my online journal, imported into this blog because it tells the story of a real software project I worked on. Who says software developers don’t have war stories? -TimK)
I know why good people don’t tell the truth. That’s not to say they outright lie. They’re not dishonest, just not… honest. They fudge. Maybe that’s what I should’ve done. After all, that strategy is time-tested and approved. Software projects slip for months, one day at a time. By the time the whole story eventually is clear, everyone’s gone on to something else, and no one knows what really happened, because no one was keeping track, and no one thinks twice about it. What made me think speaking the truth up-front was going to feel good?
Why good people don’t tell the truth is because honesty sucks. We pretend honesty like love is something that, if you find it, will make your life all sunshine and freshly mowed grass and the gentle caressing of the breeze against your face. But honesty isn’t happy, and it isn’t friendly. Honesty punches you in the gut and takes your lunch money. Honesty is a sulphuric-acid enema. It pumps your blood hard through your veins. Your hands shake, and you can’t stop them. Then it bears down on you so hard you feel yourself starting to cry.
I was anxious to discover why the software I had designed was not working correctly. I had understood the requirements, written my unit tests, designed a solution. Each piece worked, but the overall system was still corrupting the data.
Too bad for me, I’m working on a legacy system, the most poorly designed software I’ve ever worked with. Over the past decades, I’ve seen some pretty bad designs. I’ve even created a few myself. But none compares to this. The code has no focus, no cohesion. Zero. It’s a complex mass of confusion. Each module takes on at least half a dozen responsibilities, but never a whole responsibility. Each block is coupled via myriad vaguely named symbols with numerous other pieces of the system using confused and sometimes incorrect logic. This is worse than spaghetti code. No, spaghetti you can eat. This is like opening your cupboard, expecting to find spaghetti, instead facing a moving blanket of cockroaches.
When half-way through August I did open that cupboard, I jumped. I freaked. I panicked. Then I decided it was more work than I had expected, and I doubled my estimates.
I spent days just deciphering a single function I needed to work on, a modified copy of another function. Each was tailored for a particular type of data. I eventually enumerated five distinct things this one function was supposed to be doing. It’s like having a single switch that controls your refrigerator, dishwasher, garbage disposal, microwave, and the back porch light. Though the function did some of what I needed, it also had behaviors I did not want. And some of the logic in it I couldn’t even figure out. Whenever I looked at it, I took frequent breaks to keep my blood pressure down. It was an evil, evil function.
I decided to refactor the evil function into an engineered design rather than to try to work inside it, which I thought would make a bad situation worse. Of course, then I’d have to convince the other developers on the team to build off this design and to adopt the engineering practices that will maintain the good design. Still, it was the right thing to do. And it was one of those situations in which the right thing to do was also exciting and satisfying.
September the first arrived, the first day of my next iteration. I spent the morning digging into the legacy code, trying to discover why my new design was corrupting data. I made some headway, but some data was still being corrupted. Given the nature of the system, this could have been a bug that was always there, lurking unnoticed until I came along with a new kind of data more tasty to those particular bugs.
The afternoon I spent on a frank write-up on the status of my work, in detail never before attempted at this company. Given the measured rate of progress and the amount of work, I expect the project to take until at least mid-November. There’s a 15% chance, however, it could take through December. I measured actual progress, did the statistical analysis. Those are my best estimates. I guess that’s not what the company executive wanted to hear.
But was it the right thing for me to do? Isn’t it more useful to know now what our best information is, so we can plan sales and deployment efforts appropriately? I even provided options. We can reduce scope in order to finish earlier. We can rearrange tasks and deploy in phases in order to get high-priority features out earlier. We can even add people to some extent, in order to speed up development.
I expected to be congratulated for having the process under control, for having good information available early, rather than after the fact. Usually, we don’t know how bad things are until after shipment has been delayed by months and an engineer has spent a week in Wisconsin to try to track down some bug for a big customer.
I expected to be congratulated, but I had silly expectations. Even though I was providing more accurate and timely information, I was challenging the status quo. Challenging the status quo I do naturally; managing the repercussions I have to work at.
My manager, when he used the words “got us in trouble,” what was I supposed to think? He’s actually more like a team leader. He’s a senior engineer who does actual software development as well as handling administrivia and interfacing between our small team and the upper management.
He asked about my estimates. He said he had expected it to take about two months. I explained that it was two months, until we added a few more features to the end of the backlog. Those two months plus those features was about what the numbers came out to be. As I’d said before, we can still choose not to do those features. But these are the real numbers. I’m just trying to be up-front and realistic so that we don’t get caught off-guard.
A team leader wears two hats. Sometimes he wears his engineer’s hat, sometimes his manager’s hat. As a manager, he has corporate authority. As an engineer, he’s a peer. As a manager, he directs. As an engineer, he merely suggests options. Sometimes I think my manager switches hats without me realizing it.
He asked me about the code I had written. He wondered why I created a new design rather than putting conditionals inside the evil function to handle the new data. The evil function does essentially what I needed, and not much of it needed to change to work with the new data. It seemed like a lot of work to redesign something when a quick hack would’ve sufficed. Sometimes when working with a legacy system, quick hacks are the simplest thing to get us the features we need.
I knew there was some reason I couldn’t remember, some reason why this was not an option in this case. All I remembered was the conclusion I had reached, that refactoring and redesigning the evil function was at least as fast as hacking at it.
And the new design was much more reliable, too. I had discovered in the evil function three bugs hiding in its masses of rubbish.
I stood my ground, but I also said I’d consider what he said. I don’t know if I was convincing. I don’t remember much of what we said. I was too keyed up. I do remember asking what he wanted to accomplish with this meeting, and I don’t remember the answer. I asked because I was on the defensive, and I needed a productive direction to go in. Whatever he wanted, I don’t think we accomplished it.
I went to the caf’ and drank a gallon of water. One of the other engineer came through during that time, and I couldn’t help but tell him how I felt. “The project got me in trouble.” The company executive also happened upon me. He seemed as upset as I was about the project. I told him it wasn’t my intention to get anyone in trouble; I was just tying to be realistic and honest so that we wouldn’t be caught off guard. He told me no one was in trouble, but he did want to discuss what we could do about this project. He invited me to meet with him the next day, a meeting that turned out to be satisfying and productive.
Then I spent more than a few minutes in the bathroom. Then I went outside and sat next to the river and closed my eyes and took deep breaths. I felt tired. I wanted to go somewhere quiet to escape. I didn’t want to go home. The kids would be screaming in my ear, and everyone would badgering me for something. I didn’t think I could handle that. So I sat and thought.
Anger crept in. Was my manager actually expecting me to generate unreliable software? How professional is that? I call myself an engineer, and I think that implies that when I do something, I know what I’m doing. Just because maintaining this piece of crap has demoralized him, why does he have to spread it around? Hasn’t he ever read Peopleware?
And then something happened that’s never happened before to me in this kind of situation. I became energized, passionate about finishing this feature. I knew I could do it, and I knew I was right.
I went back inside, sat down at my desk, and processed my data through the evil function. This was not a solution to my problem, but it would tell me whether or not the corruption was caused by my refactoring. The evil function did not corrupt the data.
So I set to find out what was different between the way I was processing the data and the way the evil function was. Now that I had spent countless hours dissecting the evil function, cursing it, and refactoring it, I found it relatively easy (though still pretty painful) to review how the evil function handled each responsibility and how it differed from my design.
Ah! I found it. This bit of senseless logic here, completely undocumented, is a hack to get around the fact that this data structure here is thoroughly mismanaged. I need to add the same hack into my design, and document it as such. So I wrote a test to simulate the mismanaged situation. I ran my test and saw it fail. Good; the test is actually working. Now, I’ll insert the hack right here. The senseless logic is in a function called isFrameAtEndHack, so future programmers can tell what it tests and why it’s tested that way. I tried several times to add the hack. I was tired and my brain wasn’t working right, as my tests kept telling me. In order to use the hack, first I needed to clean up the rest of my logic. The code actually turned out better than it was before.
The tests finally passed. I integrated the new code with the rest of the system and passed my data through it. It worked. I smiled.
When I looked at the window, it was dark out. I don’t know when the last person before me left. When I looked at the clock, it was after 8:30.
Before I left, I moved the feature card over to the “done” column. Then I analyzed my time logs and calculated my velocity, my rate of progress. As it turns out, the doubled estimate was right-on.