My takeaways from a TDD debate between Cope & Uncle Bob

As a developer, there is a high chance that you had a debate on the value of TDD in building software, especially if you apply it!

I had a lot of those debates!

A couple of months ago, I came across such a debate between Jim Coplien and Robert Martin (Uncle Bob). I found this discussion kind of interesting especially that it involves two leaders in software engineering.

You can watch the debate here:

Takeaways

Here are my takeaways from the discussion:

Three Rules of TDD

Uncle Bob defines the following three rules for applying TDD:

  1. Don’t write a line of production code without having a corresponding failing test
  2. Don’t write too many failing tests without writing production code
  3. Don’t write more production code than is sufficient to pass the currently failing test

Architecture is Important

Jim points out that he has no problem with those rules, his concerns are more architecture related. Jim and Uncle Bob would argue for more than ten minutes to finally reach an agreement on the importance of architecture. The below five points summarizes what they agreed on:

  1. Architecture is very important
  2. It is entirely wrong to assume that the code will assemble itself magically just by writing a lot of tests and doing quick iterations
  3. Design evolves with time and should be assembled one bit at a time
  4. An Object should have properties to give it meaning. At the beginning, those properties should be minimal.
  5. Architecture shouldn’t be created based on speculation

TDD and Professionalism

Probably the only disagreement you can sense from this debate is what defines a professional software engineer. For Uncle Bob, it is irresponsible for a software engineer to deliver a single line of code without writing a unit test for it. Jim, on the other hand, considers ‘Design by Contract’ to be more powerful than TDD.

My Point of View!

Personally, I have been applying TDD since I joined my team three years ago. After experiencing the benefits of this practice, we got to a point where we don’t write or refactor any line of code without having a corresponding unit test!

In addition to that, I had the chance to coach other developers by running coding dojo sessions at work.

All that makes me say that I agree more with Uncle Bob on the topic of professionalism!

References
  1. Coplien and Martin Debate TDD, CDD and Professionalism

How to handle Java exceptions in clean code? – Part 2

In my previous post, I explained the different types of exceptions in Java and left two questions for this post!

Which exception to use?

Java documentation and Uncle Bob’s book (Clean Code) were my references when preparing this tutorial. I noticed a slight difference between what is recommended in each!

Java Documentation

While reading the Java documentation, you sense a preference for Checked Exceptions. The documentation states that the usage of UnChecked Exceptions should be restricted to the case where crashing the system is intentional if an exception occurs. In other cases where recovery is still possible, Checked Exceptions should be used.

Robert Martin’s opinion

Uncle Bob has a different opinion!  He argues that although Checked Exceptions might have some benefits and can be useful in some special cases like writing critical libraries, they are not a necessity to have a robust software.

Breaking the ‘Open/Closed Principle‘ and ‘Encapsulation‘ are the main two reasons that make using Checked Exceptions a bad idea!

Let’s see how!

In the below example, the MainBookReader.main method is calling the method MainBookReader.readJsonObject to get the book’s description from a JSON file. And since readJsonObject throws an exception we had to add throws FileNotFoundException clause to the signature of the main method and the interface JsonLoader!

import java.io.FileNotFoundException;

public class MainBookReader {
    public static void main(String[] args) throws FileNotFoundException {
        BookJsonLoader jsonLoader = new BookJsonLoader();
        Book bookFromJson = jsonLoader.readJsonObject("books.json");
        System.out.println(bookFromJson.name());
    }
}
import com.google.gson.Gson;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class BookJsonLoader implements JsonLoader {
    public Book readJsonObject(String fileName) throws FileNotFoundException {
        FileReader jsonFile = new FileReader(fileName);
        Gson gson = new Gson();
        return transformToBook(gson.fromJson(jsonFile, JsonBook.class));
    }

    private Book transformToBook(JsonBook jsonBook) {
        return new Book(jsonBook.getName());
    }
}
import java.io.FileNotFoundException;

public interface JsonLoader {
    Book readJsonObject(String fileName) throws FileNotFoundException;
}

Adding the throws clause to most of our methods means two things!

  1. Our top methods & classes had to know a lot about the lower methods & classes!
  2. If any change is to be applied to the lower methods, this change has to be replicated to all above methods!

This is a violation of the ‘Open/Closed Principle‘ and ‘Encapsulation‘!

Is there a better way?

No matter how careful we are, things can still go wrong. Thus we still need to deal the exceptions when they occur! The alternative to the code above is writing clean code!

In this section, I will refactor the above code to make the code look better, although it might involve more code!

Wrap the Exception

The first step is to wrap the exception in one place! In our case, we can replace the throws clause with a try-catch!

In the catch clause, I’m throwing a JsonFileNotFoundException (UncheckedException) that I have created based on the needs of this system (i.e. providing an informative message to the user).

public class BookJsonLoader implements JsonLoader {
    public Book readJsonObject(String fileName){
        try {
            FileReader jsonFile = new FileReader(fileName);
            Gson gson = new Gson();
            return transformToBook(gson.fromJson(jsonFile, JsonBook.class));
        } catch (FileNotFoundException e) {
            throw new JsonFileNotFoundException("Can't find the file " + fileName + ". Please make sure it exists!", e);
        }
    }

    private Book transformToBook(JsonBook jsonBook) {
        return new Book(jsonBook.getName());
    }
}

The benefits of this refactoring are:

  • Remove all ‘throws clauses.’
  • Hide the logic and information of our class from the outer classes
  • Send an informative message to the user in case the file was not found
  • Minimize the dependency (remove imports from other classes)

Don’t return null

In some cases, developers don’t want to throw an UnCheckedException, so they tend to return a ‘null’ instead! That is a bad practice because it will eventually result in a NullPointerException later.

So, what to do? The answer is simple, return a SPECIAL CASE!

The below class ‘MissingBook‘ is our special case!

public class MissingBook extends Book {

    private MissingBook(String name) {
        super(name);
    }

    public static MissingBook aMissingBook() {
        return new MissingBook("MISSING BOOK");
    }
}

This allows us to return an instance of the MissingBook in the catch instead of throwing an exception!

public class BookJsonLoader implements JsonLoader {
    public Book readJsonObject(String fileName){
        try {
            FileReader jsonFile = new FileReader(fileName);
            Gson gson = new Gson();
            return transformToBook(gson.fromJson(jsonFile, JsonBook.class));
        } catch (FileNotFoundException e) {
            return MissingBook.aMissingBook();
        }
    }

    private Book transformToBook(JsonBook jsonBook) {
        return new Book(jsonBook.getName());
    }
}

Don’t pass null

In your code, you shouldn’t pass null as parameters and instead use the Special Cases as before. But, it gets harder to prevent your clients from passing nulls thus you might need to assert some function parameters before proceeding with your code execution!

Finally

Doing the refactoring for the simple example above might look like over-engineering! But, it becomes beneficial when writing more complex code!

I think that the excessive usage of CheckedExceptions will pollute the code and thus should be avoided! For me, the benefits of CheckedExceptions can still be achieved through:

  1. Following the rules of Clean Code
  2. Increase test coverage of the code by following the TDD practice! With the use of TDD, all the edge cases should be covered and thus eliminating the possibility of having unexpected exceptions during execution!

To understand better the importance and how to write clean code, it is highly recommended that you read the book Clean Code by Robert Martin!

References:

  1. Clean Code: A Handbook of Agile Software Craftsmanship
  2. Unchecked Exceptions — The Controversy
  3. Java Exceptions
  4. The Clean Code Blog

How to handle Java exceptions in clean code? – Part 1

Reading the chapter ‘Error Handling‘ from the book ‘Clean Code‘ made me think whether developers follow the clean code rules when writing production code. It also appeared to me that the difference between the Java exception types might not clear be for some!

The purpose of this short tutorial is to clarify those two points. And to make it clearer, I will be dividing it into two posts. In the first one, I will explain the different types of Java exceptions. Whereas in the second one, I will show how to write clean exception code!

Java Exceptions

In Java, the class Throwable is at the top of the class hierarchy of all exceptions. The classes Error and Exception directly extend Throwable. All the subclasses of Error and Exception are grouped into two types ‘Checked Exceptions‘ and ‘UnChecked Exceptions‘as displayed in the image below.

javaexceptiondiagram

What is the difference between Checked and UnChecked Exceptions?

UnChecked Exceptions

UnChecked Exceptions extend the classes ‘Error‘ or ‘RuntimeException‘. In this case, developers don’t have to worry about catching or handling the exceptions at compile time as the compiler won’t report any error. But, as they are not caught, those exceptions may result in complete failure of the application when thrown during execution.

Below are two examples of UnChecked Exceptions!

1. Runtime Exception

The below method simply divides two numbers:

private static int divide(int numerator, int denominator) {
    return numerator / denominator;
}

The code is fine and will compile with no errors! So what is the problem?

The problem might occur at runtime if we try to call the method while passing zero as the denominator. Since this exception is not caught, our system will crash throwing the below arithmetic exception:

Exception in thread "main" java.lang.ArithmeticException: / by zero
 at MainUnCheckedException.divide(MainUnCheckedException.java:7) ...
2. Error

The below method compiles, but since I missed writing the base cases calling the method will crash the system and throw a StackOverflowError (as shown below) will throw an Error!

private static int fibonacci(int number) {
    return fibonacci(number - 1) + fibonacci(number - 2);
}
Exception in thread "main" java.lang.StackOverflowError
 at MainUnCheckedException.fibonacci(MainUnCheckedException.java:10)...

Checked Exceptions

On the other hand, all other classes extending the ‘Exception‘ class are considered to be Checked Exceptions. In this case, the code will not compile if the developer doesn’t explicitly handle the exception.

This adds a level of security to your code, as you are forced to specify how your code should behave when an exception occurs and thus decreases the chance of having an unrecoverable failure in the system.

Let’s see an example!

public class MainCheckedException {
    public static void main(String[] args) {
        FileInputStream fileInputStream = new FileInputStream("foo.txt");
    }
}

Error:(6, 43) java: unreported exception java.io.FileNotFoundException;
must be caught or declared to be thrown

In the above code, we are trying to read a file ‘foo.txt’, but our compiler complains that we need to handle the FileNotFoundException.

Solving this compilation error can be done in two ways:

  1. Try Catch: The first solution requires surrounding the code with a try-catch.
    private static void readFileTryCatch() {
        try {
            FileInputStream fooFile = new FileInputStream("foo.txt");
        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
        }
    }

    Even if the file was not there, this code would not break thanks to the try-catch. As shown in the below code and output, the system will print out the exception and continue execution!

    public static void main(String[] args) {
        readFileTryCatch();
        System.out.println("Done");
    }
    ------------Output------------
    foo.txt (No such file or directory)
    Done
  2. Throw an exception: The other solution is adding a ‘throws FileNotFoundException‘ to the method signature.
    private static void readFileThrow() throws FileNotFoundException {
        new FileInputStream("foo.txt");
    }

    Using this solution will propagate handling the exception to the calling methods as illustrated in the two options below:
    Option 1: Adding throws to all methods signatures 

    public static void main(String[] args) throws FileNotFoundException {
        readFileThrow();
        System.out.println("Done");
    }
    ------------Output------------
    Exception in thread "main" java.io.FileNotFoundException: foo.txt (No such file or directory)
    at java.io.FileInputStream.open0(Native Method) ...

    Option 2: Catching the exception in one of the methods in the call stack

    public static void main(String[] args)  {
        try {
            readFileThrow();
        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
        }
        System.out.println("Done");
    }
    ------------Output------------
    foo.txt (No such file or directory)
    Done

 

I hope this post gave you a better understanding of Java exceptions! In the 2nd part, I will be sharing more details on how to properly write exceptions in clean code and which of the two exception types to use!