18 abril 2011

Java coding best practices

For a software project in which I worked as a external code/design reviewer, a contractor asked me to do a document with Java coding best practices.

Instead of giving them a big document that nobody is going to read, I preferred to make a small list of good practices - and try to fit them in a A4 page.

I'm not a big fan of "best practices" documents. I know that they are good to standardize the work and share knowledge. But there is a "dark side": a "best practice" depends on a particular context. So is good to understand why a practice was good and distinguish when there are better options; in the end you can't get blood out of a stone: the best way to produce good quality systems is to work with good professionals.

So this is the list of best practices that I made (in no particular order):

Choose Meaningful Names
- Classes:
     If a class represents a concept of the problem domain, use a well known name for that domain (the technical analyst/product owner is our friend here).
     Forget the name, look at the class responsibilities and them choose a name based on that (CRC helps).
     Using suffixes like "Data", "Info", "Object"; doesn't add information and can be confusing ie. What is the difference between Customer and CustomerInfo? 
- If you still don't known how to name something and you invent a new name: communicate that to your team, and be sure that everybody agrees to name "the thing" in the same way. 
- When you start to feel uncomfortable with a method or class name and you have a better name, don't heistate: rename it.
Write short methods
- Remember the "Extract method" refactoring shortcut keys, you are going to use them a lot. 
- Methods from comments: usualy in a long method programmers start to use comments to explain parts of the code, create a new method instead of using comments in that way. 
- If you can't do an extract method because you end with lots of  parameters or you have some obscure dependency; is a sign that something is wrong with the design. Sometimes using a "Method Object" to fix that helps to find what is missing.
Avoid undesired side-effects (use immutable objects as much as possible)
- Avoid mutable global state, ie: non-final static variables or "singleton" like globals (see "Avoid Singletons"). 
- Try to use constructors with arguments instead of a combination of "default constructor/setters" (this is related with "Avoid object instances in invalid state" and "Avoid nulls")- 
- Be careful when returning collections: most of the Java collections are mutable, returning those could expose your object internal state.
Avoid object instances in an "invalid state"
- Declare required instance variables as final, and initialize them in the constructor. 
Use good defaults. 
- If you end with a constructor with lots of parameters: refactor the code, maybe you can group of those parameters in an object.
- If you need to build a complex instance, you can create a builder (but never put the validations in the builder, put them in the constructor).
Avoid nulls
- You can use the Null Object pattern 
- Use good defaults instead of null
Use singletons ONLY to represent unique objects
- A lot of programmers use the singleton pattern to point to globally well known objects, this combined with mutable objects is a time bomb: lots of undesired side effects and code that could have concurrency problems. 
- If you need a "setInstance" method for testing purposes: is not a singleton! 
- Usually "Null Objects" can be singletons, but most of the time singletons are not needed. 
- Singletons should be immutable.
Classes should have one responsibility - one reason to change
- Using the CRC method sometimes helps to think in single responsibility classes. 
- Classes with many responsibilities tend to be difficult to test with unit tests; so a hard to write unit test could be a sign that you are dealing with too many responsibilities.
Write unit tests like specifications
- In TDD the most confusing part of unit test is the word "test". If you think in tests like in small specifications of that your code supposes to do, then is very easy to write tests first.
- Don't put to many assertions in one test method, is better to have only one or two assertions per unit test method. Why? You'll start working faster with the TDD cycle and if something fails is easy to see what's happening.
Use exceptions instead of return codes
- If you "Avoid Nulls" you will find this straight forward.
- Think in exceptions in a layered way, and handle them in the place that you could do something about it.
- Don't hide exceptions with empty catch blocks: if you have a bug it will be very difficult to find.
- Never catch Throwable: because you can catch sub-classes of Error too.
When creating polymorphic class hierarchies: remember the substitution principle
- If you have a method that is overridden by different subclasses: avoid coupling between the type of the method argument and specific subclasses.
- If you need to cast an argument, maybe you are not following the substitution principle, take account that even if the "subtypes" are ok to the compiler you will not be able to change implementations easily.  
Optimize only if it's necessary and with the help of a profiler
- Applying optimization tips blindly... tend to create wrong assumptions, and sometimes code that is difficult to understand.
- A linear search in a collection with a bounded number of elements is O(1) (even when the max number of elements is 100, 1000 or 10000)
- I/O is orders of magnitude more expensive than CPU/Memory tasks.
- Generational Garbage Collectors like the ones in Java are optimized for short lived objects, it means that if you need to create objects to write more understandable code: create them. Early optimizations in object instance creation could cause other problems in concurrency and impact in the GC time; optimize object creation ONLY if you conclude that is necessary after using a profiler.

6 comentarios:

  1. Dear Sir,

    I wish to read your blog articles in english, so tried subscribing using yahoo pipes but all my attempts failed. Please update your rss content with proper values.

    may be if you try to setup pipes you will know.
    here is a pipes demo

    http://www.youtube.com/watch?v=OeybemOxoPk

    ResponderBorrar
  2. Hi ganesh, you have to use the Atom feed to parse the blog feed. Try with this URL: http://diegacho.blogspot.com/feeds/posts/default

    ResponderBorrar
  3. Code review is single best development practice I recommend since code spent 90% time in maintenance and only 10% in initial development , a code which has comments and readable is easy to maintain and understand and saves a lot of time to understand and less error prone while making any changes but at the same time there should be guidelines for maintainers also because I have seen code quality getting degraded with every version.

    Thanks
    How to use ArrayList in Java 1.5 with generics

    ResponderBorrar
  4. Este comentario ha sido eliminado por el autor.

    ResponderBorrar
  5. >Its really helpful for me to understand where we i lost in my previous interview. Thanks.
    If anyone wants to Learn Java in Chennai go to the Besant Technologies which is No.1 Training Institute in Chennai.

    http://www.besanttechnologies.com/training-courses/java-training

    ResponderBorrar