Wednesday, October 7, 2009

Code Consequences

I think we're all aware that our actions have consequences for ourselves and, often, for others. I'm going to talk about such a situation that is relevant to iPhone developers.

I've had to turn down some development work while writing More iPhone 3 Development. This isn't a big deal, as I expected that to happen when I agreed to do the book, and I like to see it, because it tells me that our platform is still doing well. However, a surprising number of the projects that I've turned down have been to fix iPhone applications written by other contract developers, something I don't consider to be such a good sign.

Even if I were available to take the work, there's something inherently uncomfortable about these situations. Some developer whose identity is unknown to me wrote this code. That developer has no chance to respond to my criticisms and I, in turn, have absolutely no idea of the situation under which the code was written. I feel like there's no right way to point out flaws in code like this. Yet, to fix the problem or even give an accurate estimate, those problems have to be identified.

In one such recent instance, the potential (and very trusting) client actually sent me their code along with the description of the problem. I knew that I couldn't take on a hefty project until the book is finished, but the description of the problem sounded like something that I might be able to be fix quickly and easily, so I looked at the code that evening.

The problem was exactly what I thought it was going to be based on the description, and I thought I'd be able to fix it with a few dozen lines of code over not more than a couple of hours, which would be a win for the prospective client and the client's users who were experiencing significant performance problems with relatively low volumes of data. In a well-designed application, fixing this specific problem wouldn't have impacted anything outside of a single class or, at worst, a handful of classes. It was a bottleneck in pure "Model" code in the MVC sense. In theory, as long as I didn't change the way the object's data was accessed and updated by other classes, I could change the implementation details, such as how the data was persisted, without impacting anything.

You know what they say about theories, right? They work great, in theory. But in practice…

So, yeah, that theory didn't pan out. I started looking at the rest of the project looking for potential cross-depenencies and I found that my assumption was totally and completely wrong. The underlying data store wasn't only vended through the data class, it was accessed directly by literally dozens of classes. In fact, all the actual persistence code - both loading and saving - (with the exception of encodeWithCoder: and decodeWithCoder:) was contained in controller classes. And there were dozens of these controller classes, many of which were nearly identical except for a handful of lines, which seemed to indicate a general lack of design or forethought. The core problem was thus made significantly harder to fix by a desperate and unrequited need for refactoring. There were, literally, several dozen classes that could have been written as a single-class or, at worst, a couple of subclasses with a common parent. The entire project looked like one thrown together by a tragically inexperienced developer, or one who didn't have any use for "this new fangled OO shit". Seriously, there should be a link to this project on the Wikipedia's spaghetti code entry, in the section on "Spaghetti with meatballs". Reading the project made me feel like I was in some weird programming equivalent of Poe's Law; I don't think I could intentionally create code this convoluted.

Now, I wish I could say that I'd never seen nor written bad code, but that wouldn't be true. I've seen lots of bad code over the years and have written more than I'd care to admit. Despite that, though, this code was worse than most of what I've seen, and that's saying something, given that my job for several years was fixing problems in other people's code.

After a couple of hours, I came to the inescapable conclusion that this job was beyond my ability to take on right now. I'm almost positive that it would be more work to try and fix the multitude of problems in this code than it would be to just rewrite the application from scratch. Even if not, the end result would certainly be better. Out of fairness to the prospective client, I had to explain why I had to turn down the project, something that seems likely to cause problems for the previous developer. I couldn't, in good conscious, not tell him, however. I did put it in far more diplomatic terms than I am doing here in my blog, at least, where I sometimes personify the fact that 'tact' is a four letter word.

The high demand for iPhone developers has made it a profitable livelihood and, I think, has caused many people to offer their services as iPhone developers without truly adequate experience. Hell, I've been working with the SDK as long as anybody outside of Apple, and I sometimes feel like I don't have "truly adequate experience" with it - that's the peril of a new technology I guess. Regardless, when you're developing for a client or employer rather than yourself, you really need to be aware that there will be downstream developers affected by the decision you make or fail to realize are there in the first place. When you cut a corner to save time or fail to use good design because you don't know any better or don't care, that decision may very well have significant consequences for your client and for other developers who inherit your code.

Good application architecture and writing good code takes a little longer in the short run, but in the long run, it requires much less of a time investment to maintain. Yes, I know most of us bill by the hour and writing bad code is far more profitable than writing good code, but don't do that. Seriously. That's worse than just being ignorant of how to write good code. Despite the economy, there's no shortage of iPhone development work right now, and even if that weren't the case, you owe it to your clients to give them the best code you are capable of writing.

Addendum: Based on some of the comments, I fear that this post has come across as rather more judgmental than intended. Contract software development, especially for clients who are not familiar with the process, is extraordinarily hard. No contract developer has ever, ever created a perfectly designed, bug-free application. In the real world, you have to deal with deadlines, unreasonable demands, and tons of other factors that work against you delivering a perfect application. I am not unsympathetic to this at all, as I have experienced it myself on countless occasions.

I intended this only as a cautionary tale to give you something to think about when deciding whether to cut a corner or to skip doing that rewrite you know you need to do, not as an indictment of anybody. Believe me, looking at some of my early contract work, I've got some pretty harsh words for myself.

One of the most important traits in a developer is the willingness to accept that no matter how good you are, you do make mistakes. You will never stop making mistakes and you will never stop being able to learn from those mistakes. Sometimes, you can even learn from others' mistakes and save yourself some pain.

No comments:

Post a Comment