The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition
Rate it:
Open Preview
18%
Flag icon
While many people try to keep their code flexible, you also need to think about maintaining flexibility in the areas of architecture, deployment, and vendor integration.
19%
Flag icon
Look for the important requirements, the ones that define the system. Look for the areas where you have doubts, and where you see the biggest risks. Then prioritize your development so that these are the first areas you code.
19%
Flag icon
Tracer code is not disposable: you write it for keeps. It contains all the error checking, structuring, documentation, and self-checking that any piece of production code has. It simply is not fully functional.
19%
Flag icon
In a tracer code development, developers tackle use cases one by one. When one is done, they move to the next. It is far easier to measure performance and to demonstrate progress to your user. Because each individual development is smaller, you avoid creating those monolithic blocks of code that are reported as 95% complete week after week.
19%
Flag icon
Prototyping generates disposable code. Tracer code is lean but complete, and forms part of the skeleton of the final system. Think of prototyping as the reconnaissance and intelligence gathering that takes place before a single tracer bullet is fired.
20%
Flag icon
if you find yourself in an environment where you cannot give up the details, then you need to ask yourself if you are really building a prototype at all.
20%
Flag icon
What sorts of things might you choose to investigate with a prototype? Anything that carries risk. Anything that hasn’t been tried before, or that is absolutely critical to the final system. Anything unproven, experimental, or doubtful. Anything you aren’t comfortable with.
20%
Flag icon
Prototyping is a learning experience. Its value lies not in the code produced, but in the lessons learned. That’s really the point of prototyping.
20%
Flag icon
Before you embark on any code-based prototyping, make sure that everyone understands that you are writing disposable code. Prototypes can be deceptively attractive to people who don’t know that they are just prototypes. You must make it very clear that this code is disposable, incomplete, and unable to be completed.
20%
Flag icon
Properly used prototypes can save you huge amounts of time, money, and pain by identifying and correcting potential problem spots early in the development cycle—the time when fixing mistakes is both cheap and easy.
21%
Flag icon
One of the reasons that the classic gather requirements, design, code, ship approach doesn’t work is that it is anchored by the concept that we know what the requirements are. But we rarely do. Your business users will have a vague idea of what they want to achieve, but they neither know nor care about the details. That’s part of our value: we intuit intent and convert it to code. So when you force a business person to sign off on a requirements document, or get them to agree to a set of Cucumber features, you’re doing the equivalent of getting them to check the spelling in an essay written in ...more
22%
Flag icon
The downside of internal domain languages is that you’re bound by the syntax and semantics of that language. Although some languages are remarkably flexible in this regards, you’re still forced to compromise between the language you want and the language you can implement.
22%
Flag icon
Writing a domain language adds some cost to your project, and you’ll need to be convinced that there are offsetting savings (potentially in the long term).
23%
Flag icon
Before you get too committed to model building, cast around for someone who’s been in a similar situation in the past. See how their problem got solved. It’s unlikely you’ll ever find an exact match, but you’d be surprised how many times you can successfully draw on others’ experiences.
23%
Flag icon
Often, the scope you choose will form part of the answer you give: “Assuming there are no traffic accidents and there’s gas in the car, I should be there in 20 minutes.”
23%
Flag icon
Building the model introduces inaccuracies into the estimating process. This is inevitable, and also beneficial. You are trading off model simplicity for accuracy. Doubling the effort on the model may give you only a slight increase in accuracy. Your experience will tell you when to stop refining.
Mauricio Chirino
We need to learn when and where to stop refining things for the sake of cost/benefits.
24%
Flag icon
Program Evaluation Review Technique, or PERT. Every PERT task has an optimistic, a most likely, and a pessimistic estimate. The tasks are arranged into a dependency network, and then you use some simple statistics to identify likely best and worst times for the overall project.
24%
Flag icon
What to Say When Asked for an Estimate You say “I’ll get back to you.”
25%
Flag icon
The better your tools, and the better you know how to use them, the more productive you can be.
25%
Flag icon
Human-readable forms of data, and self-describing data, will outlive all other forms of data and the applications that created them. Period. As long as the data survives, you will have a chance to be able to use it—potentially long after the original application that wrote it is defunct.
26%
Flag icon
If you can’t remember which configuration file manages your system backups, a quick grep -r backup /etc should tell you.
27%
Flag icon
The distance between thinking something and having it appear in an editor buffer drop way down. Your thoughts will flow, and your programming will benefit. (If you’ve ever taught someone to drive, then you’ll understand the difference between someone who has to think about every action they take and a more experienced driver who controls the car instinctively.)
29%
Flag icon
It is a painful thing To look at your own trouble and know That you yourself and no one else has made it Sophocles, Ajax
29%
Flag icon
Unfortunately, modern computer systems are still limited to doing what you tell them to do, not necessarily what you want them to do.
29%
Flag icon
No one writes perfect software, so it’s a given that debugging will take up a major portion of your day.
Mauricio Chirino
High contrast point of view with craftsmanship where they say debugging should be limited to a minimum due to rigorous TDD
29%
Flag icon
It doesn’t really matter whether the bug is your fault or someone else’s. It is still your problem.
29%
Flag icon
If your first reaction on witnessing a bug or seeing a bug report is “that’s impossible,” you are plainly wrong. Don’t waste a single neuron on the train of thought that begins “but that can’t happen” because quite clearly it can, and has.
29%
Flag icon
Resist the urge to fix just the symptoms you see: it is more likely that the actual fault may be several steps removed from what you are observing, and may involve a number of other related things. Always try to discover the root cause of a problem, not just this particular appearance of it.
29%
Flag icon
We routinely set compiler warning levels as high as possible. It doesn’t make sense to waste time trying to find a problem that the computer could find for you!
29%
Flag icon
The best way to start fixing a bug is to make it reproducible. After all, if you can’t reproduce it, how will you know if it is ever fixed?
29%
Flag icon
we want a bug that can be reproduced with a single command. It’s a lot harder to fix a bug if you have to go through 15 steps to get to the point where the bug shows up.
30%
Flag icon
Tracing is invaluable in any system where time itself is a factor: concurrent processes, real-time systems, and event-based applications.
30%
Flag icon
It is generally more profitable to assume that the application code is incorrectly calling into a library than to assume that the library itself is broken. Even if the problem does lie with a third party, you’ll still have to eliminate your code before submitting the bug report.
30%
Flag icon
when faced with a “surprising’’ failure, you must accept that one or more of your assumptions is wrong. Don’t gloss over a routine or piece of code involved in the bug because you “know” it works. Prove it. Prove it in this context, with this data, with these boundary conditions.
31%
Flag icon
When you come across a surprise bug, beyond merely fixing it, you need to determine why this failure wasn’t caught earlier. Consider whether you need to amend the unit or other tests so that they would have caught it.
32%
Flag icon
In a world of imperfect systems, ridiculous time scales, laughable tools, and impossible requirements, let’s play it safe. As Woody Allen said, “When everybody actually is out to get you, paranoia is just good thinking.”
32%
Flag icon
Dealing with computer systems is hard. Dealing with people is even harder.
32%
Flag icon
What is a correct program? One that does no more and no less than it claims to do. Documenting and verifying that claim is the heart of Design by Contract
32%
Flag icon
A routine should never get called when its preconditions would be violated. It is the caller’s responsibility to pass good data
32%
Flag icon
A class ensures that this condition is always true from the perspective of a caller. During internal processing of a routine, the invariant may not hold, but by the time the routine exits and control returns to the caller, the invariant must be true. (Note that a class cannot give unrestricted write-access to any data member that participates in the invariant.)
33%
Flag icon
If all the routine’s preconditions are met by the caller, the routine shall guarantee that all postconditions and invariants will be true when it completes.
33%
Flag icon
preconditions should not be used to perform things such as user-input validation.
33%
Flag icon
Remember, if your contract indicates that you’ll accept anything and promise the world in return, then you’ve got a lot of code to write!
34%
Flag icon
TDD and other testing happens only at “test time” within the build cycle. But DBC and assertions are forever: during design, development, deployment, and maintenance
34%
Flag icon
DBC is more efficient (and DRY-er) than defensive programming, where everyone has to validate data in case no one else does.
34%
Flag icon
TDD is a great technique, but as with many techniques, it might invite you to concentrate on the “happy path,” and not the real world full of bad data, bad actors, bad versions, and bad specifications.
34%
Flag icon
Simply enumerating what the input domain range is, what the boundary conditions are, and what the routine promises to deliver—or, more importantly, what it doesn’t promise to deliver—before you write the code is a huge leap forward in writing better software.
34%
Flag icon
conventional runtime systems and libraries are not designed to support contracts, so these calls are not checked. This is a big loss, because it is often at the boundary between your code and the libraries it uses that the most problems are detected
34%
Flag icon
Who is responsible for checking the precondition, the caller or the routine being called? When implemented as part of the language, the answer is neither: the precondition is tested behind the scenes after the caller invokes the routine but before the routine itself is entered. Thus if there is any explicit checking of parameters to be done, it must be performed by the caller, because the routine itself will never see parameters that violate its precondition.
34%
Flag icon
It’s much easier to find and diagnose the problem by crashing early, at the site of the problem.