Coding practices to prevent incidents and get through code reviews quickly

  1. Small try/catch blocks
  2. Precise Exception Catching
  3. Null Pointer Exceptions and Optional
  4. Return Early
  5. Lombok
  6. Avoid wildcard imports
  7. Log the exception
  8. AssertJ
  9. Verify complete and real objects

For inspiration, read more about Offensive Programming and Clean Code.

Small try/catch blocks

Small try blocks are easier to read and make sure the expected exceptions are coming from where they are expected.

Good

try {
    something();
} catch () {
    // exception came from something()
}

Bad

try {
    something_a();
    something_b();
    something_c();
    something_d();
} catch () {
    // where did the exception come from? 
}

Precise Exception Catching

Good

try {
    something();
} catch (ABCDException abcde) {
    // handle abcde
}

Bad

try {
    something();
} catch (Exception e) {
    // handle unexpected exceptions that will never be found, leading to bigger problems 
}

Null Pointer Exceptions and Optional

Don’t catch Null Pointer Exceptions. These should not happen and developers should use Optional and null checks if the object can be null. Catching them is awkward.

Return Early

Returning Early reduces complexity of the code, making reviewing easier. It removes else statements and brings the edge cases to the front of the method.

Good

// edge cases
if (!a) {}
if (!b) {}
if (!c) {}

// happy path
do_d();

Bad

if (a) {}
else {
    if (b) {}
    else {
        if (c) {}
        else {
            do_d();
        }
    }
}

Lombok

Project Lombok removes so much boilerplate code in Java, making the code readable, requiring less test coverage of generated code, and automatically updating methods when new variables are added.

  • Logger: @Slf4j
  • Builder: @Builder
  • Getters/Setters/ToString: @Data (read/write) and @Value (read).

Avoid wildcard imports

IDEs like to squash multiple imports into a single line: import com.abc.*. This saves minimal space and makes reading code without an IDE difficult. It also make the code vague, so turn off the setting!

Log the exception

Pass the exception to the logger so the entire stacktrace, message, and other fields can be logged. Plus, there is no String building required.

Good

log.error("Something bad", e);

Bad

log.error("Something bad: {}", e.getMessage());

AssertJ

Use a deep library like AssertJ that has more methods for comparison and reals more naturally.

Verify complete and real objects

Avoid being lazy and using any() or any(Object.class). These satisfy code coverage but miss the goal of unit testing: preventing regressions. Pass the entire object that you expect to receive.

Similarly, when asserting for correctness of the returned value, use isEqualTo() and check the entire object. When new fields are added, that unit tests will fail, requiring an update. For imprecise fields like time, AssertJ has isCloseTo().

This entry was posted in Software Engineering and tagged , , , , . Bookmark the permalink.

Leave a comment