http://home.earthlink.net/~huston2/books/programming_books.html
The Practice of Programming
Brian Kernighan, Rob Pike
Style
- Use descriptive names for globals, short names for locals.
- Be consistent.
- Use active names for functions.
- Be accurate.
- Indent to show structure.
- Use the natural form for expressions.
- Parenthesize to resolve ambiguity.
- Break up complex expressions.
- Be clear.
- Be careful with side effects.
- Use a consistent indentation and brace style.
- Use idioms for consistency.
- Use else-ifs for multi-way decisions.
- Avoid function macros.
- Parenthesize the macro body and arguments.
- Give names to magic numbers.
- Define numbers as constants, not macros.
- Use character constants, not integers.
- Use the language to calculate the size of an object.
- Don't belabor the obvious.
- Comment functions and global data.
- Don't comment bad code, rewrite it.
- Don't contradict the code. Clarify, don't confuse.
Interfaces
- Hide implementation details.
- Choose a small orthogonal set of primitives.
- Don't reach behind the user's back.
- Do the same thing the same way everywhere.
- Free a resource in the same layer that allocated it.
- Detect errors at a low level, handle them at a high level.
- Use exceptions only for exceptional situations.
Debugging
- Look for familiar patterns.
- Examine the most recent change.
- Don't make the same mistake twice.
- Debug it now, not later.
- Get a stack trace.
- Read before typing.
- Explain your code to someone else.
- Make the bug reproducible.
- Divide and conquer.
- Study the numerology of failures.
- Display output to localize your search.
- Write self-checking code.
- Write a log file.
- Draw a picture.
- Use tools.
- Keep records.
Testing
- Test code at its boundaries.
- Test pre- and post-conditions.
- Use assertions.
- Program defensively.
- Check error returns.
- Test incrementally.
- Test simple parts first.
- Know what output to expect.
- Verify conservation properties.
- Compare independent implementations.
- Measure test coverage.
- Automate regression testing.
- Create self-contained tests.
Performance
- Automate timing measurements.
- Use a profiler.
- Concentrate on the hot spots.
- Draw a picture.
- Use a better algorithm or data structure.
- Enable compiler optimizations.
- Tune the code.
- Don't optimize what doesn't matter.
- Collect common subexpressions.
- Replace expensive operations by cheap ones.
- Unroll or eliminate loops.
- Cache frequently-used values.
- Write a special-purpose allocator.
- Buffer input and output.
- Handle special cases separately.
- Precompute results.
- Use approximate values.
- Rewrite in a lower-level language.
- Save space by using the smallest possible data type.
- Don't store what you can easily recompute.
Portability
- Stick to the standard.
- Program in the mainstream.
- Beware of language trouble spots.
- Try several compilers.
- Use standard libraries.
- Use only features available everywhere.
- Avoid conditional compilation.
- Localize system dependencies in separate files.
- Hide system dependencies behind interfaces.
- Use text for data exchange.
- Use a fixed byte order for data exchange.
- Change the name if you change the specification.
- Maintain compatibility with existing programs and data.
- Don't assume ASCII.
- Don't assume English.
The Elements of Programming Style
Brian Kernighan, P. J. Plauger
Introduction
- Write clearly - don't be too clever.
Expression
- Say what you mean, simply and directly.
- Use library functions.
- Avoid temporary variables.
- Write clearly - don't sacrifice clarity for "efficiency."
- Let the machine do the dirty work.
- Replace repetitive expressions by calls to a common function.
- Parenthesize to avoid ambiguity.
- Choose variable names that won't be confused.
- Avoid the Fortran arithmetic IF.
- Avoid unnecessary branches.
- Use the good features of a language; avoid the bad ones.
- Don't use conditional branches as a substitute for a logical expression.
- Use the "telephone test" for readability.
Control Structure
- Use DO-END and indenting to delimit groups of statements.
- Use IF-ELSE to emphasize that only one of two actions is to be performed.
- Use DO and DO-WHILE to emphasize the presence of loops.
- Make your programs read from top to bottom.
- Use IF, ELSE IF, ELSE IF, ELSE to implement multi-way branches.
- Use the fundamental control flow constructs.
- Write first in an easy-to-understand pseudo-language; then translate into whatever language you have to use.
- Avoid THEN-IF and null ELSE.
- Avoid ELSE GOTO and ELSE RETURN.
- Follow each decision as closely as possible with its associated action.
- Use data arrays to avoid repetitive control sequences.
- Choose a data representation that makes the program simple.
- Don't stop with your first draft.
Program Structure
- Modularize. Use subroutines.
- Make the coupling between modules visible.
- Each module should do one thing well.
- Make sure every module hides something.
- Let the data structure the program.
- Don't patch bad code ?rewrite it.
- Write and test a big program in small pieces.
- Use recursive procedures for recursively-defined data structures.
Input and Output
- Test input for validity and plausibility.
- Make sure input cannot violate the limits of the program.
- Terminate input by end-of-file or marker, not by count.
- Identify bad input; recover if possible.
- Treat end of file conditions in a uniform manner.
- Make input easy to prepare and output self-explanatory.
- Use uniform input formats.
- Make input easy to proofread.
- Use free-form input when possible.
- Use self-identifying input. Allow defaults. Echo both on output.
- Localize input and output in subroutines.
Common Blunders
- Make sure all variables are initialized before use.
- Don't stop at one bug.
- Use debugging compilers.
- Initialize constants with DATA statements or INITIAL attributes; initialize variables with executable code.
- Watch out for off-by-one errors.
- Take care to branch the right way on equality.
- Avoid multiple exits from loops.
- Make sure your code "does nothing" gracefully.
- Test programs at their boundary values.
- Program defensively.
- 10.0 times 0.1 is hardly ever 1.0.
- Don't compare floating point numbers just for equality.
Efficiency and Instrumentation
- Make it right before you make it faster.
- Keep it right when you make it faster.
- Make it clear before you make it faster.
- Don't sacrifice clarity for small gains in "efficiency."
- Let your compiler do the simple optimizations.
- Don't strain to re-use code; reorganize instead.
- Make sure special cases are truly special.
- Keep it simple to make it faster.
- Don't diddle code to make it faster ?find a better algorithm.
- Instrument your programs. Measure before making "efficiency" changes.
Documentation
- Make sure comments and code agree.
- Don't just echo the code with comments ?make every comment count.
- Don't comment bad code ?rewrite it.
- Use variable names that mean something.
- Use statement labels that mean something.
- Format a program to help the reader understand it.
- Indent to show the logical structure of a program.
- Document your data layouts.
- Don't over-comment.
Practical Java
Peter Haggar
General Techniques
- Understand that parameters are passed by value, not by reference.
- Use final for constant data and constant object references.
- Understand that all non-static methods can be overridden by default.
- Choose carefully between arrays and Vectors.
- Prefer polymorphism to instanceof.
- Use instanceof only when you must.
- Set object references to null when they are no longer needed.
Objects and Equality
- Differentiate between reference and primitive types.
- Differentiate between == and equals.
- Do not rely on the default implementation of equals.
- Implement the equals method judiciously.
- Prefer getClass in equals method implementations.
- Call super.equals of base classes.
- Consider carefully instance of in equals method implementations.
- Follow these rules when implementing an equals method.
Exception handling
- Know the mechanics of exception control flow.
- Never ignore an exception.
- Never hide an exception.
- Consider the drawback to the throws clause.
- Be specific and comprehensive with the throws clause.
- Use finally to avoid resource leaks.
- Do not return from a try block.
- Place try/catch blocks outside of loops.
- Do not use exceptions for control flow.
- Do not use exceptions for every error condition.
- Throw exceptions from constructors.
- Return objects to a valid state before throwing an exception.
Performance
- Focus initially on design, data structures, and algorithms.
- Do not rely on compile-time code optimization.
- Understand runtime code optimization.
- Use StringBuffer, rather than String, for concatenation.
- Minimize the cost of object creation.
- Guard against unused objects.
- Minimize synchronization.
- Use stack variables whenever possible.
- Use static, final, and private methods to allow in lining.
- Initialize instance variables only once.
- Use primitive types for faster and smaller code.
- Do not use an Enumeration or an Iterator to traverse a Vector.
- Use System array copy for copying arrays.
- Prefer an array to a Vector or ArrayList.
- Reuse objects whenever possible.
- Use lazy evaluation.
- Optimize source code by hand.
- Compile to native code.
Multithreading
- Understand that for instance methods, synchronized locks objects, not methods or code.
- Distinguish between synchronized statics and synchronized instance methods.
- Use private data with an accessor method instead of public or protected data.
- Avoid unnecessary synchronization.
- Use synchronized or volatile when accessing shared variables.
- Lock all objects involved in a single operation.
- Acquire multiple locks in a fixed, global order to avoid deadlock.
- Prefer notifyAll to notify.
- Use spin locks for wait and notifyAll.
- Use wait and notifyAll instead of polling loops.
- Do not reassign the object reference of a locked object.
- Do not invoke the stop or suspend methods.
- Terminate threads through thread cooperation.
Classes and Interfaces
- Use interfaces to support multiple inheritance.
- Avoid method clashes in interfaces.
- Use abstract classes when it makes sense to provide a partial implementation.
- Differentiate between an interface, abstract class, and concrete class.
- Define and implement immutable classes judiciously.
- Use clone for immutable objects when passing or receiving object references to mutable objects.
- Use inheritance or delegation to define immutable classes.
- Call super.clone when implementing a clone method.
- Do not rely on finalize methods for non-memory resource cleanup.
- Use care when calling non-final methods from constructors.
Effective Java
Joshua Bloch
Creating and Destroying Objects
- Consider providing static factory methods instead of constructors
- Enforce the singleton property with a private constructor
- Enforce noninstantiability with a private constructor
- Avoid creating duplicate objects
- Eliminate obsolete object references
- Avoid finalizers
Methods Common to All Objects
- Obey the general contract when overriding equals
- Always override hashCode when you override equals
- Always override toString
- Override clone judiciously
- Consider implementing Comparable
Classes and Interfaces
- Minimize the accessibility of classes and members
- Favor immutability
- Favor composition over inheritance
- Design and document for inheritance or else prohibit it
- Prefer interfaces to abstract classes
- Use interfaces only to define types
- Favor static member classes over nonstatic
Substitutes for C Constructs
- Replace structures with classes
- Replace unions with class hierarchies
- Replace enum constructs with classes
- Replace function pointers with classes and interfaces
Methods
- Check parameters for validity
- Make defensive copies when needed
- Design method signatures carefully
- Use overloading judiciously
- Return zero-length arrays, not nulls
- Write doc comments for all exposed API elements
General Programming
- Minimize the scope of local variables
- Know and use the libraries
- Avoid float and double if exact answers are required
- Avoid strings where other types are more appropriate
- Beware the performance of string concatenation
- Refer to objects by their interfaces
- Prefer interfaces to reflection
- Use native methods judiciously
- Optimize judiciously
- Adhere to generally accepted naming conventions
Exceptions
- Use exceptions only for exceptional conditions
- Use checked exceptions for recoverable conditions and run-time exceptions for programming errors
- Avoid unnecessary use of checked exceptions
- Favor the use of standard exceptions
- Throw exceptions appropriate to the abstraction
- Document all exceptions thrown by each method
- Include failure-capture information in detail messages
- Strive for failure atomicity
- Don't ignore exceptions
Threads
- Synchronize access to shared mutable data
- Avoid excessive synchronization
- Never invoke wait outside a loop
- Don't depend on the thread scheduler
- Document thread safety
- Avoid thread groups
Serialization
- Implement Serializable judiciously
- Consider using a custom serialized form
- Write readObject methods defensively
- Provide a readResolve method when necessary