One of the many things that it took me a while to get a really solid handle on after college is what good code actually is. As a beginner, my idea of good versus bad code was solely based on whether it worked or not, it didn’t make sense to me that working code could be ‘bad.’ I was reading this blog post about writing good code (it’s really good, you should read it) the other day and thought I should talk about what good code actually is and why it matters.
First of all, a piece of code that works is always better than one that doesn’t. Credit where it’s due, I believe it was Deryk Barker who told us that back in college. Having an elegant, clear design for your application is great and all, but an application that actually does things is better than a perfectly designed application that doesn’t exist yet. Your design is never going to be perfect anyway, and if you want to accomplish anything eventually you need to write some code.
But once you have a working application, whether your code is bad or good matters a whole lot. Development is maintenance. That is, if anyone ever uses your code, it will eventually need to change, and if your code is bad making those changes will be much harder than it has to be.
Understanding what makes code difficult to change is something that just comes with experience, none of my projects in college were long term enough for me to really understand what makes code hard to change. In other words, you shouldn’t feel bad if you don’t understand what makes code good or bad right away, it’s a tough concept. It’s also a matter of taste. There are certainly some things that are near universally loathed, but there are many more things that are matters of personal preference.
Let’s start simple and work up to the more complicated stuff.
One of the simplest parts of good code is just formatting it consistently. One of the reasons I think python is a great first language is because it forces you to indent your code properly. It sounds pointlessly picky, but indenting is really, really important. Especially in HTML, I can’t tell you how many times I’ve failed to close a div because I didn’t indent my code nicely and didn’t see the problem. Consistent formatting not only helps you spot errors, it makes your code a lot easier to read. You know how your English teacher hounded you about proof-reading your essays until your spelling and grammar were perfect? That’s because it’s jarring to be happily reading along and suddenly hit something that looks wrong. Code is very similar, the more different each piece is the more time you spend trying to figure out what’s going on. Every fiddly little detail takes up more space in your brain, the more of them you can take for granted the more room you have left for what the code is actually doing.
Another formatting issue is taking advantage of language quirks like not always requiring curly braces for if statements. Java, the language I’ve used most, allows you to leave out the braces if the body of your if is only one line. I hate that a whole lot because it’s a trap: if you add another line to that if but you forget to add braces, that other line won’t actually be part of the if block and your code won’t do what you meant. You should always code to make it as difficult as possible to screw up, and leaving out braces makes it easy to screw up later.
Another big deal is having good variable and method/function names. I’ve talked about this before, but it’s worth repeating – variable names matter! Sure, you know what you mean right now, but will you remember why you named that variable that way in six months? You will not, so give your variables nice clear names. If that means your variable name has to be a bit longer, that’s fine. You’re going to read that code many more times than you write it, it’s really not worth worrying about making variable names easy to type.
Besides being descriptive, names need to be consistent too. If you name three methods handleNewOrder, handleCompletedOrder, and handleShippedOrder, your cancel method needs to be named handleCancelOrder. If you have an extremely small application it doesn’t matter so much, but in my experience most professional programming is done on applications much too large for anyone to just remember all the method names. If your method names are consistent, you can pretty much guess what they’re called and use another piece of code without having to dig through it to figure out what on earth the order cancelling method you’re sure exists somewhere is actually called. That’s a big waste of time
Methods/functions also need to do one thing and only one thing. This is one of those things that will make much more sense once you’ve worked on a larger project. Methods that do too much are a pain to update because you have to sort out which part of it does which thing and where you can safely make changes to one part without breaking the other one. They’re also a hassle if new code only needs part of the functionality of the megamethod, now you’ve got to either refactor it into more methods that only do one thing each or duplicate part of the functionality. Both of those are risky, especially if you don’t have automated tests.
Speaking of tests, opinions vary but I think they’re a really important part of good code. It’s really helpful to be able to make a change, run the test suite, and be reasonably sure (no test suite is perfect) that you didn’t break anything that used to work. Sometimes parts of your application will interact in ways you just didn’t think of, comprehensive tests can catch that before you migrate something to production that will ruin your support team’s day.
At a level up from methods, your objects should just be in charge of one thing and have reasonable names too. For example, if you have a simple todo list application, you probably want a user object and a list object, and you really don’t want the user object to mess with your list or your list object to update your user. The easier it is to guess where a particular function actually happens in your code (updating a user, for example), the less time you waste digging around trying to figure out where on earth users get updated.
Application architecture is also an important part of good code, but this post is already quite long enough :) The TL;DR version of architecture is that your objects should be grouped in predictable ways just like your methods are grouped into objects in predictable ways. More on that later!