What is it that causes developers to write bad code? The easy excuse is time–pressing deadlines can cause anyone to take shortcuts–but I don’t think that’s the most frequent problem. Generally, good code takes only a little longer to write than bad code (with the exception of architectural differences) and it pays for itself in the long run when you know exactly what it should do, you can read it easily, you don’t have to chase down sloppy bugs, etc.
Take a look at this method from some example code Google provides (DeviceDetailFragment.copyFile):
public static boolean copyFile(InputStream inputStream, OutputStream out) { byte buf[] = new byte[1024]; int len; try { while ((len = inputStream.read(buf)) != -1) { out.write(buf, 0, len); } out.close(); inputStream.close(); } catch (IOException e) { Log.d(WiFiDirectActivity.TAG, e.toString()); return false; } return true; }
This is an extremely common pattern in Java, copying from one stream to another. It will work correctly in most cases; however, it’s riddled with bad practices. Some of these are minor, but others are more significant. Starting from the top of the method and working down, here are some problems:
- No documentation – If this were a private method, I’d be more forgiving, but this is a public method, and the documentation would take 30 seconds to write. Most importantly, this method will attempt to close the streams that are passed to it! A user of this method has no way of knowing the streams will be closed without reading the source code of this method. That’s a booby trap waiting to catch someone.
- Bad method name – The method name is copyFile, but it’s copying streams, which may or may not be file in/out streams.
- Inconsistent parameter names – Why is one inputStream and one just out?
- Non-conventional bracket placement – The Android convention is to place brackets with the type like byte[].
- Magic number – Hopefully most developers will know it’s 1024 to be a 1kb buffer, but people reading your code should not have to figure out what the numbers are. Besides, 1024 bytes is a very small buffer and testing would likely show a larger size is more effective (maybe 8192). A constant would be far more clear and easier to change when needed.
- Missing finally block – If anything goes wrong interacting with either stream, the streams will not be closed.
- Bad log tag – The tag used when logging should tell the reader where this log message came from, but it’s explicitly lying and using the tag from another class.
- Bad log message – Without any more context, just logging the toString method of the exception can also make it difficult to track down where this is occurring, depending on the actual method implementation.
- Possible bad return value – The method could return false after copying from the InputStream to the OutputStream and closing the OutputStream successfully due to an exception closing the InputStream. It’s unclear if that’s correct behavior or not (partly because of the lack of documentation), but I’d suspect most developers seeing a method with this signature would assume that a false return value indicates the copying did not succeed
Given that this is such a common pattern, it wouldn’t be hard for a developer to find an example online. There are plenty of libraries that have utility methods for this, including Guava, Apache Commons, etc. And it’s also not a case of this being an aspiring developer, just beginning to learn code in his or her own time. That means this isn’t a hard problem and it isn’t a lack of ability, so why would a developer write sloppy code like this?
I don’t know the answer, but I have some guesses:
- Disconnect – The developer is disconnected from anyone that the sloppy code affects, whether that’s the end user, another developer, or himself/herself a few months down the road.
- No pride – Some developers just don’t care about the quality of their work. They work on it only until it’s good enough to pass a quick QA test or code review and then they move on. This is essentially the “throw it over the wall” approach.
- No fun – People like to solve problems, developers included, but this is just a utility method–a means of getting to a solution–so it’s not as interesting to work on. Maybe the whole project was uninteresting to the developer.
- The norm – If all the other developers are writing code like this, there is a lot less motivation to write code well.
Each of these possibilities is a different challenge, but all are important to address. One of the best things you can do for a developer is to get him or her to connect with the end user, to understand how every bit of code affects the user’s experience. If nothing else, connect with the other developers and realize that the quality of each developer’s code directly impacts the working speed and level of satisfaction or frustration of everyone else on the team.
You can sometimes increase the pride developers take in their code by complimenting the parts that are well done and asking a lot of questions about the parts that aren’t (“Why didn’t you document this?” or “Why did you choose to use two different naming patterns?”).
It’s not easy to make projects fun, but developers often find certain aspects more fun than others (one developer might love path-finding algorithms while another loves bitmap manipulation). Where possible, let developers take on the pieces that they’re most interested in. If they also take pride in their code and the norm is good code, all the less fun bits will be better off as well.
Finally, some companies or groups get into a culture of mediocre code. “Good enough” is the target and the entire code base gets full of undocumented methods with quirky behaviors that have changed over the years. Don’t accept it. A code review process should always be in place and it shouldn’t just be looking for bugs. Establish the level of quality the code has to have and reject anything that doesn’t cut it. It’s a painful process, and it can take months to get the culture to begin to shift, but eventually the developers will start to write better code to avoid dealing with the hassle of having it rejected. Farther down the road, they’ll start to see how much better the code base is and realize the benefits of writing good code in the first place.