Introduction
This is a general list of guidelines and best practices collected from a variety of sources (books, interwebs, personal experience). All of these are guidelines should be taken with a grain of salt
some very helpful sources:
General
Try to minimize the number of different languages (natural and artificial) in a source file
Always follow the “Principle of the least surprise”. Make things intuitive.
Every class/method should implement the behaviour that another programmer could reasonably expect from the object's name (and nothing more).
generally try to make the code as expressive as possible
place code where a reader would expect it
Do not suppress compiler warnings unless you have a very good reason
Code Duplication is bad. DRY (Dont repeat yourself).
Be Consistent
do all similar things in the same way
choose conventions and continue to follow them
have only one name for one concept
increase the expressiveness of calculations by using explanatory variables
prefer polymorphism to if/else or switch/case
No magic numbers
Using float or double to represent currency is criminal
Ecapsulate Conditionals
Avoid negative conditionals
Keep configurable data at high levels
Prefer enums to constants
Never write any code you dont need right now
Naming
Names should
be descriptive
be unambiguos
have appropriate length
follow conventions (eg. of the domain)
use standard nomenclature where possible
be at the appropriate level of abstraction
avoid encodings
dont encode type information or visibility in variable names ( m_Name , s_Url)
it is redundant, distracting and useless (the IDE does it better)
dont't call an interface ISomething (look at core java. List,Set,Map not IList,ISet,IMap)
Every time you write a comment you should feel guilty. This may sound radical but keep reading. If you write a comment you are basically admitting that you need to document a hidden concept or intention that you did not manage to express within your code. When you start writing a comment stop for a second and think hard if there exists a better way to express yourself within the programming language. This will not always be possible but here are a few points to consider:
Comments need to be maintained separately. They grow stale or get separated from the code they were intended to document. They become inappropriate.
Inappropriate comments can be distracting or even harmful
They lead the programmer along the wrong way of thought
Many inappropriate comments started out as being well intended, then the code evolved and the comment did not evolve with it
Redundant comments are bad. Reiterating what your code already says is clutter at best.
If a part of your program is so complicated that you feel the need to write a long comment then it is time to STOP. Refactor and simplify your code.
Commented out code needs to be deleted. Always. (SVN will remember)
Write comments only for things your code can not say for itself
First try improving the naming of your variables, create adequately named helper methods or introduce clarifying variables
Dont explain what code does (this MUST be obvious from the code, otherwise refactor until it is). Explain WHY you do it.
If you write a comment make sure it is the best comment you can write
API Documentation (Javadoc)
Public APIs should be well documented. But the above guidelines should be kept in mind. A comment like
public interface Person {
/**
* Get the birthday of the associated person.
*
* @return the birthday of the Person as Date.
*/
public Date getBirthday();
....
}
adds NO INFORMATION to the API. It does however add unnecessary clutter.
What should be documented in a Javadoc API:
things that are
not obvious to the user of the
API
requirements for the input variables
the conditions under which checked and unchecked Exceptions are thrown
side effects of a method (if any)
runtime complexity
thread safety
if a class is intended for inheritance (most classes really aren't)
Example
A trivial example: Instead of doing
...
if (record.getDate().before(MIN_DATE) ) { // remove if too old
remove(record);
}
do
...
removeIfTooOld(record);
....
private void removeIfTooOld(Record r) {
if (r.getDate().before(MIN_DATE) {
remove(r);
}
}
your intention is now encoded in the language itself, a comment is no longer required.
Classes / Interfaces
Helpers/utility classes are candidates for inner classes
Classes that are not utilities of some other class should not be scoped inside another class
Keep all Interfaces as small as possible
Base classes should know nothing about their derivatives
Avoid artificial coupling
-
Avoid transitive navigation
-
Inheritance
Design classes for inheritance or explicitly forbid inheritance (private constructor, final class)
If you override a method ALWAYS declare @Override
Classes designed for inhertiance need to document their implementation (to a certain extent)
Protected methods are effectively part of the public interface
Classes designed for inheritance should never call their own public/protected methods
Composition is often preferable to inheritance
Methods
methods should never contain anything that comes as a surprise to the reader
a method should do exactly one thing
each method should be simple and as short as possible
Pick clear, descriptive names
the method name should give the reader a good idea of what the method does
if you have to look at the implementation to find out what the method does, then pick a better name
if you are tempted to put an AND in a method name, it may be a good idea to split the method
Never return null when you could return an empty List (Set, Map, etc.)
class methods that are used together should be close together
try to stay at the same level of abstraction
separate higher-level concepts from detailed concepts
within a method don't mix levels of abstraction. It is deeply confusing.
This also applies to Exceptions: a high-level method should NOT need to deal with low-level Exceptions
prefer nonstatic methods
Arguments
Functions should have a small number of arguments (No argument is best, 3 should be max)
Output arguments are counterintuitive. Readers expect arguments to be INPUTS. Dont confuse them.
Boolean arguments are bad
Methods that are never called are dead code and should be discarded
unless they are part of a public
API
Testing
Only test things that are part of the class contract
Test everything that could possibly break
Tests also have documentary value
Tests should be precise
Always test boundary conditions
Test exhaustively near bugs
Tests should be fast
Don't write several tests for the same thing
Don't test more than one thing per @Test
Make tests self-documenting
Don't accept failing unittests. ever.
if a test fails fix it
if a test is obsolete delete it
if a test is not ready @Ignore it until it is ready
If you ignore a unittest, document why you ignore it: @Ignore(“Test case not ready”)
Keep the unittests healthy
Exception Handling
Here are a few guidelines
Use Exceptions only for exceptional cases
Before throwing an Exception think how the user of the
API can/will react to it
Use checked Exceptions when
the user of your
API can realistically be expected to recover (or at least do something useful after the exception has happened)
you want the user of the
API to be aware of an exceptional situation (a checked Exception forces him to handle it)
Use unchecked Exceptions
to indicate programming errors (IllegalArgumentException, IllegalStateException, NullPointerException)
to abort in unrecoverable situations
Provide meaningful error messages with your Exceptions
Use Exceptions at the right level of abstraction
Don't declare to throw multiple Exceptions unless they actually make a difference to the caller
Try to re-use existing Exceptions
Document the conditions under which an Exception is thrown
Do not “swallow Exceptions”
If there is a really good reason for an empty catch block