Writing Readable and Maintainable Code — Java
By: Chamith Madusanka
Tags: Best Practices, Clean Code, Code Review, Maintainable Code, Java

Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live — John Woods

Writing clean code is not easy. It must be readable and maintainable. Writing clean code is not a process that we can achieve very quickly. It’s a continuous process, practically a daily effort. So let’s start…

1. Proper Naming

Naming is on of the most important aspect of coding. But somehow most of developers underestimate the importance of proper naming in the code. Developers spend most of their time reading code instead of writing. Thinking what is the purpose of this class, what kind of data containing in this field and what this method really does.

Classes

Class name should be a noun. When you create a instance from a class it should be a thing that physically exists. It can also be abstract thing. Always try to make it as noun.

Class name should be specific. If not every time we have to go inside the class to see what’s inside. E.g. Suppose you have CommonUtils.java class containing random number generating method, String split method. Better to make both methods to two separate classes and name as StringUtils.java, NumberUtils.java.

Variables

Never a single letter.

Always specific.

Ideally 1–2 words.

Boolean should prefix with is, has, can, should ( isValid, isActive, hasLicense, canEvaluate, shouldAbort).

( isValid, isActive, hasLicense, canEvaluate, shouldAbort). Use camelCase.

Use ALL_CAPS with underscores for constants.

Methods

Functionality fully understandable from the name.

Should name like Verb (Do what?) + Noun (To what?). But better be specific. e.g : setCustomerName, calculatePrice, validateUserDetails

Do not use and, or, if in method names.

2. Implementing Methods

When implementing method we should carefully take a look on what not to return, method parameters, fail fast & return early, conditionals, apply Single responsibility principle (SRP) etc.

Method can return primitives, objects or null. Avoid returning null as much as possible. when we return null if caller doesn’t know about it will hit a NullPointerException. If caller does know about null have to add extra checks. This extra condition here adds cyclomatic complexity and every time the method calls caller has to add null checks.

Method should only do one thing. That’s how we apply SRP(Single Responsibility Principle) to methods

Avoid returning special codes (-1, 0, 1 and other) from a method. It will force client code to check for magic numbers.

Always try to have 0–3 arguments in a method. More than that will indicate your method needs refactoring. If method takes too many primitive types, pass a single object.

Put magic numbers as parameters only if a method accepts one argument and method name suggests what method does. e.g. nowPlusWeeks(int weeks), nowPlusDays(int days). If not it’s very difficult to identify what method does without looking inside. e.g. nowPlusTime(int months, int weeks, int days)

nowPlusTime() with magic numbers as parameters (Not good)

Understandable methods even with magic number parameters

Avoid flag (Boolean) arguments as method parameters. Flag arguments complicates the signature of the method, loudly proclaiming that method does more than one thing. If we have a method with sendNotification(content, isSms) we can split it to two methods as sendSms(content), sendEmail(content)

Method with flag argument

Flag argument method split into two methods

Fail fast and return early. Immediately report any failure and let the program fail. If you have arithmetic calculations inside a method, check at top of the method for certain conditions to avoid arithmetic exceptions and throw IllegalArgumentException. Therefore we can throw the exception early before it reach the logic.

Throw exception early

Multiple if statements lead to high cyclomatic complexity. It’s very difficult to keep all those conditions in head while reading the code. To avoid this we can check simple conditions first and return immediately. Now you can see conditions and the return statements are in the same line. If you have argument values that are valid but they are logically allow you to stop execution early, then evaluate these first and exit the function right away.

With multiple if blocks

Refactored with return early

DRY — Don’t Repeat Yourself. If you have repeating code in methods, put those into a single method.

Avoid complex conditionals. Just extract complex logical conditions to a separate method that sounds like Boolean question and return a Boolean value.

Complex if condition, difficult to under

Condition moved to a separate method with understandable name

Avoid nested ternary expression. Instead put those in separate if statements for readable code.

3. Exception Handling

Don’t catch the top level throwable because you might catch errors , such as out of memory error or internal error.

Try to avoid catching the general exception because you might catch runtime exceptions that shouldn’t be caught such as NullPointer. You should use multiple catch blocks to cater this issue. Then you can show specific exception messages as well. But after Java 7 we can handle this by one line and it’s easy. But the downside is we can’t show specific exception messages.

Catching general exception is not good

Better way to catch multiple exception

Easier and better way to catch multiple exception

Don’t handle NullPointerException. NullPointerException is a result of programing error. If we handle NullPointerException then we cover up our own programming errors.

Don’t put empty catch blocks.

Don’t put only logs in catch blocks.

In catch block we should log the error using any logger framework and throw the exception with a informative message.

Avoid methods that throw exceptions in finally block.

4. Class Organization

We should only put methods that are specific to the particular class. That’s how we apply SRP(Single Responsibility Principle) to classes. Should put functionally and logically working things together.

Few unrelated methods in Order class

Unrelated methods are moved to separate classes

Always try to program to an interface. If not for a single change you have to go through each and every class to change.

Put inter-related methods one after the other for better readability.

Proximity rule

5. Writing Comments