More on this book
Community
Kindle Notes & Highlights
Look for every boundary condition and write a test for it.
Every time you see duplication in the code, it represents a missed opportunity for abstraction.
Variables and function should be defined close to where they are used.
The methods of a class should be interested in the variables and functions of the class they belong to, and not the variables and functions of other classes.
Sometimes, however, Feature Envy is a necessary evil.
Selector arguments are just a lazy way to avoid splitting a large function into several smaller functions.
In general it is better to have many functions than to pass some code into a function to select the behavior.
One of the most important decisions a software developer can make is where to put code.
Code should be placed where a reader would naturally expect it to be.
In general you should prefer nonstatic methods to static methods.
More explanatory variables are generally better than fewer.
Being unsure what your code does is just laziness.
In general it is a bad idea to have raw numbers in your code. You should hide them behind well-named constants.
There are some formulae in which constants are simply better written as raw numbers.
Extract functions that explain the intent of the conditional.
Temporal couplings are often necessary, but you should not hide the coupling.
Public classes that are not utilities of some other class should not be scoped inside another class.
Separating levels of abstraction is one of the most important functions of refactoring, and it’s one of the hardest to do well.
Names are too important to treat carelessly.
The power of carefully chosen names is that they overload the structure of the code with description.
The length of a name should be related to the length of the scope.
The tests are insufficient so long as there are conditions that have not been explored by the tests or calculations that have not been validated.
Sometimes you can diagnose a problem by finding patterns in the way the test cases fail.
We can define an atomic operation as any operation that is uninterruptable.
A Callable looks like a Runnable, but it can return a result, which is a common need in multithreaded solutions.
The synchronized keyword always acquires a lock, even when a second thread is not trying to update the same value.
It is always better to synchronize as little as possible as opposed to synchronizing as much as possible.
To really solve the problem of deadlock, we need to understand what causes it.
One thread cannot take resources away from another thread.
The most insidious problems are the ones that have such a small cross section that they only occur once in a billion opportunities.