Software development is hard. Understanding requirements, keeping up with evolving scope, and getting everyone on the same page is difficult enough without developers adding self-inflicted wounds. Junior developers are going to make rookie mistakes now and again, but even veteran developers can fall into some bad habits and make some of the mistakes below, and being aware of them can help you avoid them.
When I was a less experienced developer, the company I worked for had a coding standard that prohibited methods that were more than a certain number of lines. I had thought about this and came to the conclusion that it wasn’t a very good standard and completely disregarded it. To me, a method needed to be exactly the number of lines it needed to be to get the job done, and putting some arbitrary limit to the number of lines of code was senseless.
The older, more experienced version of me now understands that the intent of this type of standard is to keep a method narrowly focused. Ideally, a method should do one thing. That allows it to be easily tested. It helps with readability. It makes it easier to refactor.
I still stand by the notion that a method needs to be exactly as long as it needs to be, but whenever you have methods that feel too long, you’re probably doing too much work in there, and you could probably break it down into more granular parts.
Reinventing the Wheel
Inexperienced developers may tend to get excited and want to get right to coding without realizing that there are already some common patterns and libraries in play. I know that I can get a bit eager even today and start slinging code before checking to see if there are already some established ways of doing things – either internally developed libraries or some that are freely available that could be leveraged.
Prematurely Optimizing for Performance
During interviews, I often ask developers to write some code on the whiteboard. I always mention prior to starting this that I understand that it’s unlikely that they will be able to write an optional implementation of something the first time they try, and that it’s even more unlikely that they are able to do that on a whiteboard without the advantage of Intellisense, google, and the other tools that they generally have at their disposal, and that I understand it. I don’t expect that. I expect a brute force solution that we can iterate on. My opinion is that any solution is better than no solution and it gives us a starting point to subsequently make better and faster.
No matter how often I mention this, a good number will write a line of code or two, erase, and then rewrite them in a way that they perceive to be slightly more performant.
That’s definitely not the time to try to optimize code. You want to know another time? How about before you’ve written a functional method and before you know how often and when that code will be called? Before you start worrying about whether to use the conditional / ternary operator instead of an if / else, or whether you move a variable outside a loop to eliminate object creation overhead, or any of the assorted micro-performance tips that you’ve come across before, get something that works and then decide where to spend your time performance tuning – if it’s even necessary!
Being Too Clever
A good mantra to follow when developing is to keep things simple and to avoid being too clever in code. We’ve all seen it (or done it ourselves!), where a line of code does so many things and makes you kind of smile at how it’s taking advantage of some syntax quirks or magic number to get the job done.
That doesn’t mean that you shouldn’t write efficient and elegant code, but you should avoid code that is so complex or obfuscated that you yourself wouldn’t want to revisit it. You or someone else like you is going to have to do it at some point. Remember, someone is going to have to maintain that code, and at least according to Code For the Maintainer, you should “always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.”
Writing Code that’s Too Generic
We developers love generic solutions. We’ve got patterns and patterns that help us write generically and allow for specific implementations to override our generic behavior, or we have templates that specific behavior can hook in. That’s all fine and good, but when taken too far, we can get into deeper and deeper levels of abstraction and have produced code that can handle just about anything. That’s great if you’re writing a tool that is intended to be used by the masses.
The problem? Our code only needed to solve one thing and now we’ve got loads of code to support and maintain that we may never use.
Probably the easiest habit for new developers to fall into is trying to force a solution into fitting the problem, rather than the other way around. With limited experience, it’s easy to get tunnel vision and try to apply the same patterns to solving any problem, and this can be a trap that even more experienced developers fall into. I recall working somewhere where a lead developer was infatuated with state machines. He saw every single application, feature, or change was an opportunity for him to implement a state machine.
Thinking back on it now, it doesn’t make a lot of sense to me. Maybe he was right and the problems he was focusing on were prime candidates to implement by way of a state machine, but I find it more likely that he had simply found his hammer, and if all you have is a hammer, everything looks like a nail.