If there is an error, there is something very wrong in the code, so don’t ignore it, under any circumstances.
One of the benefits of detecting problems as soon as you can is that you can crash earlier, and crashing is often the best thing you can do. The alternative may be to continue, writing corrupted data to some vital database or commanding the washing machine into its twentieth consecutive spin cycle. (p.205)
→ Try to crash the code during it’s development instead of assumming it’s sanity.
A dead program normally does a lot less damage than a crippled one. (p.206)
Whenever you find yourself thinking “but of course that could never happen,” add code to check it. (p. 207)
The purpose of assertions: to exclude the impossible from happening
Careful:
Don’t use assertions in place of real error handling. Assertions
check for things that should never happen. (p. 208)
Heisenbug: debugging that changes the behavior of the system being debugged
Always keep the assertions on because:
... the routine that allocates a resource should also free it. (p.214)
ex) Let the function that uses the file to close it before returning.
Nest Alloctions:
It doesn’t matter what kind of resources we’re using —
transactions, network connections, memory, files, threads, windows — the basic pattern applies: whoever allocates a resource should be responsible for deallocating it. (p. 216)
Objects and Exceptions:
Objects → constructor and destructor
The class represents a resource, the constructor gives you a particular object of that resource type, and the destructor removes it from your scope. (p. 217)
Balancing and Exceptions:
To guarantee that everthing allocated prior to the exception is tidied up in a language that supports exceptions:
Exception anti-pattern:
Don’t do:
Instead, do:
This pattern prevents deallocating things that we’re even allocated in the first place (maybe due to an error that was raised before the finally
clause)
When you can’t balance resources ( dynamic data structures):
Establish a semantic invariant for memory allocation (ex: in the case of deallocating the top-structure):
Because Pragmatic Programmers trust no one, including
ourselves, we feel that it is always a good idea to build code that
actually checks that resources are indeed freed appropriately.
For most applications, this normally means producing wrappers
for each type of resource, and using these wrappers to keep
track of all allocations and deallocations. At certain points in
your code, the program logic will dictate that the resources will
be in a certain state: use the wrappers to check this. (p. 220)
Always take small, deliberate steps, checking for feedback and
adjusting before proceeding. Consider that the rate of feedback
is your speed limit. You never take on a step or a task that’s “too
big.” (p. 223)
Feedback: anything that independently confirms or disproves your action.
ex1) Unit tests provide feedback on your last code change
ex2) User demo and conversation provide feedback on features and
usability
“too big”: anything that requires wild speculations
ex1) Estimate completion dates months in the future
ex2) Plan a design for future maintenance or extendability
ex3) Guess user’s future needs
ex4) Guess future tech availability
→ only guess upto the point as you can see
Instead of wasting effort designing for an uncertain future, you can always fall back on designing your code to be replaceable. Make it easy to throw out your code and replace it with something better suited. Making code replaceable will also help with cohesion, coupling, and DRY, leading to a better design overall. (p.224)
Black Swans:
Significant events “from high-profile, hard-to-predict, and rare events that are beyond the realm of normal expectations” that cause disproportionate effects.