Pragmatic Programmer : Chapter 4

PhaseSmith·2022년 3월 26일
0

Topic 24. Dead Programs Tells No Lies

If there is an error, there is something very wrong in the code, so don’t ignore it, under any circumstances.

Tip 38. Crash Early

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)

Topic 25. Assertive Programming

Tip 39. Use Assertions to Prevent the Impossible

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:

  1. In reality, for any complex program, you are unlikely to test all the possible space of error, so you never know
  2. the program will be deployed in a world very different from the testing environment

Topic 26. How to Balance Resources

Tip 40. Finish what you start

... 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.

Tip 41. Act locally

Nest Alloctions:

  • Finish what you started
  • Deallocate resources in the opposite order to that in which you
    allocate them. That way you won’t orphan resources if one resource contains references to another
  • When allocating the same set of resources in different places in
    your code, always allocate them in the same order. This will reduce the possibility of deadlock.

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:

  1. Use variable scope (for example, stack variables in C++ or Rust)
  2. Use a finally clause in a try…catch block

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 (\because dynamic data structures):

Establish a semantic invariant for memory allocation (ex: in the case of deallocating the top-structure):

  • The top-level structure is also responsible for freeing any
    substructures that it contains. These structures then recursively delete data they contain, and so on.
  • The top-level structure is simply deallocated. Any structures that it pointed to (that are not referenced elsewhere) are orphaned
  • The top-level structure refuses to deallocate itself if it contains any substructures.

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)

Topic 27. Don’t Outrun Your Headlights

Tip 42. Take Small Steps - Always

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.

Tip 43. Avoid Fortune-Telling

profile
우리는 데이터와 하나다

0개의 댓글