Writing Readable and Maintainable Code — Java

Chamith Madusanka
The Startup
Published in
6 min readOct 21, 2020

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).
  • 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

  • Don’t put redundant comments if a method or a variable says what it does.
  • Don’t keep commented out code.
  • Use JavaDoc comments as necessary.
  • Don’t put comments on getters and setters.

Always code with static code analyzer in your IDE. It will give you a better code review on the code before submitting it. (SonarLint, FindBugs)

Thank you for reading this post. If you like this post, give a Cheer!!!

Happy CLEAN Coding ❤

--

--

Chamith Madusanka
The Startup

Full Stack Enthusiast | JavaScript | TypeScript | NodeJs | NestJs | ReactJs | NextJs | Java | https://www.linkedin.com/in/chamith24/