Hindsight is supposedly 20/20, yet when it comes to appraising existing code and solutions we often run our mouths with the blinders on.
Existing code nearly always seems poor because when people look at code they look with their current perspective and not with the perspective of the time it was written. As the writer, we’re likely to have learned new coding techniques or learned more about the domain, and had we known those things we’d have chosen different approaches. As the reader, we seldom understand the constraints that were in place when that code was created i.e considerations like time-to-market, customers demanding fixes, and the time and resources available to developers for research.
It is important that we evaluate past decisions – we can’t learn without analysis – and very often it is necessary in order to allow changes to the solution. Trouble arises because we’re human and have feelings, and don’t particularly enjoy being criticized, even in the pursuit of learning.
One solution to the human problem, egoless programming (summarized here), seems like a pretty healthy concept. However, it essentially asks people to be detached from what they’ve achieved, which to me seems like a recipe for mediocrity. I’m from the software craftsman camp, where people take pride in the work they’ve done (while still accepting that nobody’s perfect); and people take better care of the things they have pride in.
Feelings exist, so how do we manage that? Egoless programming highlights a key message that is surprisingly often forgotten: “Critique code instead of people”; or to use the sporting metaphor I prefer “play the ball not the player”. At times it seems like people forget that other people are in the room when evaluating old code – rather than “this doesn’t make sense to me”, it’s “this is spaghetti-tangled rubbish” or “what idiot put this together”. At some point our frustration at trying to understand something turns into an utter lack of tact, and as peers we shouldn’t tolerate that – we should be quick to point out the potential for hurting feelings and our own limitations.
As mentioned in the introduction, anyone reading code or analyzing solutions brings with them their perspectives based on their knowledge and experience. When that experience doesn’t include the relevant business constraints, their evaluation can sound horrible – like the coding equivalent of saying “you’d be much better off if your parents had paid for a better school” to someone from a poor background – circumstances matter!
Our experience can also bias us towards solutions that we understand, and make us uncertain of different approaches. It seems anachronistic to have to say this in the era of equality, but just because something is different doesn’t mean it’s wrong! And we don’t learn anything by sticking to what we know!
Instead, solutions should be considered and appraised for what they are, while being considerate of circumstances. For instance, I find the stack depth that results from the design patterns heavy approach used in Java quite frustrating to debug. But I also appreciate that Java was envisaged as a pure object-oriented language which places some significant constraints on what the language can do, and in particular excludes first-class functions. Rather than saying anything too negative about Java, the lesson I’d take from this is that it’s good to be pragmatic rather than ideological when designing a language.
Don’t get me wrong – I haven’t written this from the perspective of a saint. I’ve been on both sides of the scenarios I’ve mentioned. The key is that I’ve learned from those experiences and hope by sharing that others can learn too.
Finally, don’t forget the lesson of self-deprecating humor: criticizing your own code in negative terms is fine; criticizing someone else’s code in those terms is not.