The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition
Rate it:
Open Preview
34%
Flag icon
Be sure not to confuse requirements that are fixed, inviolate laws with those that are merely policies that might change with a new management regime. That’s why we use the term semantic invariants—it must be central to the very meaning of a thing, and not subject to the whims of policy (which is what more dynamic business rules are for).
35%
Flag icon
with enough components and agents that can negotiate their own contracts among themselves to achieve a goal, we might just solve the software productivity crisis by letting software solve it for us.
35%
Flag icon
If DBC is so powerful, why isn’t it used more widely? Is it hard to come up with the contract? Does it make you think about issues you’d rather ignore for now? Does it force you to THINK!? Clearly, this is a dangerous tool!
35%
Flag icon
All errors give you information. You could convince yourself that the error can’t happen, and choose to ignore it. Instead, Pragmatic Programmers tell themselves that if there is an error, something very, very bad has happened. Don’t forget to Read the Damn Error Message
35%
Flag icon
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.
36%
Flag icon
“Defensive programming is a waste of time. Let it crash!”
36%
Flag icon
when your code discovers that something that was supposed to be impossible just happened, your program is no longer viable. Anything it does from this point forward becomes suspect, so terminate it as soon as possible.
36%
Flag icon
A dead program normally does a lot less damage than a crippled one.
36%
Flag icon
There is a luxury in self-reproach. When we blame ourselves we feel no one else has a right to blame us. Oscar Wilde,
36%
Flag icon
If you need to free resources, catch the assertion’s exception or trap the exit, and run your own error handler. Just make sure the code you execute in those dying milliseconds doesn’t rely on the information that triggered the assertion failure in the first place.
36%
Flag icon
It’s embarrassing when the code we add to detect errors actually ends up creating new errors. This can happen with assertions if evaluating the condition has side effects.
37%
Flag icon
In reality, for any complex program you are unlikely to test even a minuscule percentage of the permutations your code will be put through.
Mauricio Chirino
Keeping this in mind at all times will make you humble enough to change your mindset from an arrogant state to a helpful one whenever a bug is reported (in spite of having a test suite in place)
37%
Flag icon
Turning off assertions when you deliver a program to production is like crossing a high wire without a net because you once made it across in practice.
39%
Flag icon
When in doubt, it always pays to reduce scope.
Mauricio Chirino
Lean philosophy
39%
Flag icon
If you’re adding logging records in a database, is there a similar process in place to expire them? For anything that you create that takes up a finite resource, consider how to balance it.
39%
Flag icon
The basic pattern for resource allocation can be extended for routines that need more than one resource at a time. There are just two more suggestions: 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.
39%
Flag icon
If an exception is thrown, how do you guarantee that everything allocated prior to the exception is tidied up? The answer depends to some extent on the language support. You generally have two choices: Use variable scope (for example, stack variables in C++ or Rust) Use a finally clause in a try…catch block
40%
Flag icon
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.
40%
Flag icon
What’s a task that’s too big? Any task that requires “fortune telling.”
40%
Flag icon
we hear you cry, aren’t we supposed to design for future maintenance? Yes, but only to a point: only as far ahead as you can see. The more you have to predict what the future will look like, the more risk you incur that you’ll be wrong. Instead of wasting effort designing for an uncertain future, you can always fall back on designing your code to be replaceable.
41%
Flag icon
we’ll tell you how to make reversible decisions, so your code can stay flexible and adaptable in the face of an uncertain world.
41%
Flag icon
Coupling is the enemy of change, because it links together things that must change in parallel.
41%
Flag icon
to make matters worse, coupling is transitive: if A is coupled to B and C, and B is coupled to M and N, and C to X and Y, then A is actually coupled to B, C, M, N, X, and Y.
41%
Flag icon
Developers who are afraid to change code because they aren’t sure what might be affected.
41%
Flag icon
Meetings where everyone has to attend because no one is sure who will be affected by a change.
42%
Flag icon
Try not to have more than one “.” when you access something. And access something also covers cases where you use intermediate variables,
42%
Flag icon
There’s a big exception to the one-dot rule: the rule doesn’t apply if the things you’re chaining are really, really unlikely to change. In practice, anything in your application should be considered likely to change. Anything in a third-party library should be considered volatile, particularly if the maintainers of that library are known to change APIs between releases. Libraries that come with the language, however, are probably pretty stable,
43%
Flag icon
Transforming Programming​ we talk about composing functions into pipelines. These pipelines transform data, passing it from one function to the next. This is not the same as a train wreck of method calls, as we are not relying on hidden implementation details.
43%
Flag icon
Each piece of global data acts as if every method in your application suddenly gained an additional parameter: after all, that global data is available inside every method.
43%
Flag icon
Our experience has been that reuse should probably not be a primary concern when creating code, but the thinking that goes into making code reusable should be part of your coding routine. When you make code reusable, you give it clean interfaces, decoupling it from the rest of your code. This allows you to extract a method or module without dragging everything else along with it.
43%
Flag icon
If all you have is a singleton with a bunch of exported instance variables, then it’s still just global data. It just has a longer name.
43%
Flag icon
Any mutable external resource is global data. If your application uses a database, datastore, file system, service API, and so on, it risks falling into the globalization trap. Again, the solution is to make sure you always wrap these resources behind code that you control.
43%
Flag icon
Coupled code is hard to change: alterations in one place can have secondary effects elsewhere in the code, and often in hard-to-find places that only come to light a month later in production.
43%
Flag icon
Things don’t just happen; they are made to happen. John F. Kennedy
43%
Flag icon
computers have to integrate into our world, not the other way around. And our world is messy: things are constantly happening, stuff gets moved around, we change our minds, …. And the applications we write somehow have to work out what to do.
43%
Flag icon
An event represents the availability of information. It might come from the outside world: a user clicking a button, or a stock quote update. It might be internal: the result of a calculation is ready, a search finishes.
43%
Flag icon
if we write applications that respond to events, and adjust what they do based on those events, those applications will work better in the real world. Their users will find them to be more interactive, and the applications themselves will make better use of resources.
44%
Flag icon
A state machine is basically just a specification of how to handle events. It consists of a set of states, one of which is the current state. For each state, we list the events that are significant to that state. For each of those events, we define the new current state of the system.
46%
Flag icon
Pubsub is a good technology for decoupling the handling of asynchronous events. It allows code to be added and replaced, potentially while the application is running, without altering existing code. The downside is that it can be hard to see what is going on in a system that uses pubsub heavily: you can’t look at a publisher and immediately see which subscribers are involved with a particular message.
46%
Flag icon
Streams let us treat events as if they were a collection of data.
46%
Flag icon
The current de facto baseline for reactive event handling is defined on the site http://reactivex.io, which defines a language-agnostic set of principles and documents some common implementations.
47%
Flag icon
we need to get back to thinking of programs as being something that transforms inputs into outputs. When we do, many of the details we previously worried about just evaporate. The structure becomes clearer, the error handling more consistent, and the coupling drops way down.
49%
Flag icon
You might think that this is just syntactic sugar. But in a very real way the pipeline operator is a revolutionary opportunity to think differently. Using a pipeline means that you’re automatically thinking in terms of transforming data; each time you see |> you’re actually seeing a place where data is flowing between one transformation and the next.
50%
Flag icon
If your background is object-oriented programming, then your reflexes demand that you hide data, encapsulating it inside objects.
50%
Flag icon
Data becomes a peer to functionality: a pipeline is a sequence of code → data → code → data…. The data is no longer tied to a particular group of functions, as it is in a class definition. Instead it is free to represent the unfolding progress of our application as it transforms its inputs into its outputs. This means that we can greatly reduce coupling: a function can be used (and reused) anywhere its parameters match the output of some other function.
53%
Flag icon
Do you program in an object-oriented language? Do you use inheritance? If so, stop! It probably isn’t what you want to do.
55%
Flag icon
If a parent class has 20 methods, and the subclass wants to make use of just two of them, its objects will still have the other 18 just lying around and callable.
57%
Flag icon
When code relies on values that may change after the application has gone live, keep those values external to the app.
57%
Flag icon
look for anything that you know will have to change that you can express outside your main body of code, and slap it into some configuration bucket.
58%
Flag icon
Concurrency is when the execution of two or more pieces of code act as if they run at the same time. Parallelism is when they do run at the same time. To have concurrency, you need to run code in an environment that can switch execution between different parts of your code when it is running. This is often implemented using things such as fibers, threads, and processes. To have parallelism, you need hardware that can do two things at once. This might be multiple cores in a CPU, multiple CPUs in a computer, or multiple computers connected together.