There’s a lot of styles and conventions that we follow as Java developers just because that’s how things are done and we don’t take the time to stop and question them. Over time I’ve developed some code styles and patterns that go against the grain. I was outraged about many of them when someone first showed them to me, but after spending some time using them, I realised they result in a much cleaner, manageable codebase and a happier developer.
Don’t just write these ideas off because they’re different. I suggest taking one or two of the below and try them out for a couple of weeks. If you still don’t like them then it won’t take long to back them out.
Zero Comments (Except on Public APIs)
I honestly thought that as an industry we’d already agreed on this one, but having paired with a lot of interviewees I’ve discovered this really isn’t the case. So let’s go into it once more: no comments. Comments become stale very quickly. If you write a comment above a code block there’s no guarantee that the next person that edits that block will update the comments, and from experience I know they won’t. Code blocks get deleted. Requirements change. Your comment is going to cause more harm than good.
The solution is simply to write self documenting code. Just by naming stuff well it should be patently clear what’s going on. If it isn’t then you need to refactor and break it down into simpler forms. Don’t worry about long method names if it makes what’s happening explicit. We have autocomplete, no one is going to have to type the full name out.
The obvious exception is public APIs. If you’re building a library to hand out, still use clear method names, but Javadoc can be really helpful in this, and only this situation.
Don’t Start Test Methods With the Word “Test”
There’s really no need. Your method is annotated as a test, and the class is in the test package. We know it’s a test. Your test method should specify what the requirement is that it’s testing, for example: “reversesTheWordRandomToModnar()” or “adds70ToBalanceOf100ToMakeBalanceOf170(). This makes it totally clear exactly what is under test and what is expected.
If you’re using IntelliJ there’s a particularly awesome plugin called Enso which will turn your test name into a sentence, and is viewable from the thing you’re testing. This means when you’re looking at any class, Enso will show you what the spec is for it (assuming you’re using TDD, which you are, right?)
Don’t Use @Override
This is something that is quite contentious, but hear me out. If you don’t use @Override, the worst case is that you’re overriding another implementation and the original version will get called instead of your new version. Fortunately, you’re using TDD and you’re testing the amalgamated blocks of your codebase, so this would get caught, which renders the @Override as redundant code. Redundant code is bad and distracting. Stop using @Override, rely on TDD instead.
Don’t Use getX()/setY()
This really winds people up for no apparent reason. the use of getXXX and setXXX is a remnant of JavaBeans, an era long past and no longer needed. It makes code horrible with no added benefit. Drop the get/set in favour of the field name. car.engine() will give you an engine, and car.engine(new v8()) set the engine to something new. This is still really clear and makes for much cleaner code if you end up accessing something over a couple of levels, e.g. car.lights().frontLeft() vs. car.getLights().getFrontLeft(). This is the one that at first I was most against, then came around to, and now I’m extremely passionate about.
Working Code > Performant Code
Ok, so this is less stylistic and more general, but the amount of time I’ve seen people take a problem and overengineer a solution just grates me. Solve the problem at it’s most basic level and check the performance. 99 time out of 100, it’ll be fast/scalable/buzzword enough. Instead I normally see people creating complicated caching solutions that do nothing to improve the performance but make the code a rats nest. Do the most basic thing you can to solve the problem, then optimise. At least this way you can prove there’s a problem and have empirical data to prove it’s fixed.
Use Your Own Exception Type
Again, I thought we’d all agreed as an industry but I was wrong. Checked exceptions are terrible. Almost all other languages (hi C#) realised this and didn’t even include them as a type. In any app I write, I will create my own Exception type. Any Exception thrown in my app will get wrapped in my created one and thrown as a runtime exception. This makes my code much cleaner (I don’t have throws XXXException spreading throughout my application) and it also means that if I see something in the logs I can trace that it has come from within my code (and where) or if it’s some completely unexpected exception.