Null check OR isEmpty Check

The straight SOQL accounts = [select id, .. from Account ...]

always returns a list

You can usually avoid even having to test for list empty by coding your methods to accept lists as arguments and otherwise use for loop processing as in

for (Account a: accounts) { // if accounts is empty, loop does nothing
    // do work
}

someReturnType myMethod(Account[] accounts) {
   for (Account a: account) {
      // do work
   }
}

Otherwise use accounts.isEmpty() or accounts.size() == 0

testing for null is unnecessary in a well-designed application that uses lists and maps as the core internal data structures. Personally, if I ever get a null value for a list, I prefer the null exception to occur during unit testing as it means I didn't design my code correctly to operate with lists throughout (or I forgot to init a list to empty).

N.B.

For DML, you never need to test for list empty as SFDC will do nothing if passed an empty list

Use:

update accounts; // does not burn a DML limit if list is empty

Don't use (wastes a line of code):

if (!accounts.isEmpty())
   update accounts;

Generally speaking, null values should be rare, and you should treat them as such. The results of a query always results in a list, even though it may be an empty list. Querying a checkbox field from the database will always be true or false, never null. Calling XmlNode.getChildElements() will never return null, but may return empty lists.

There are a few rare exceptions when you do need to check for null values. For example, when you make a variable transient, its not stored in the view state, so you usually need to implement lazy loading by checking for null first, and when you bind to a multi-select picklist, the list will always be either null or non-empty.

These are the exceptions to the rule, and you should learn them. Whenever you declare a list of your own, you should always assign a non-null value to it, which may be either a new list, or a list generated from a query. You should prefer to initialize those lists in a constructor, so that all your initialization code is in one place.

Generally speaking, when I use a method I'm not familiar with, I try calling it with a few different parameters to see if I can get a null value back instead of a list. I test it independently to verify its behavior. If it doesn't return a null, then I presume it never will. Just remember that most of the API avoids returning null values, or has a way to check beforehand. Most of the best design patterns for code avoid nulls using various techniques. As you gain experience, you'll learn those rare times when you must check for null, and find ways to code structures to reduce the null checks you need to make.