The Pragmatic Programmer

From Journeyman to Master

David Thomas and Andrew Hunt

Summary: many useful tips for developers

Score: 75 / 100


Never run on auto-pilot.

“Kaizen” is a Japanese term that captures the concept of continuously making many small improvements.

Take responsibility for everything you do.

The greatest of all weaknesses is the fear of appearing weak. —J. B. Bossuet, Politics

You have the right not to take on a responsibility for an impossible situation, or one in which the risks are too great.

Don’t blame someone or something else, or make up an excuse.

Provide options, don’t make lame excuses.

Run through the conversation in your mind. What is the other person likely to say?

Neglect accelerates the rot faster than any other factor.

Don’t leave “broken windows” (bad designs, wrong decisions, or poor code) unrepaired.

People find it easier to join an ongoing success.

It’s easier to ask forgiveness than it is to get permission. —Rear Admiral Dr. Grace Hopper

Constantly review what’s happening around you, not just what you personally are doing.

Users be given an opportunity to participate in the process of deciding when what you’ve produced is good enough.

The scope and quality of the system you produce should be specified as part of that system’s requirements.

Great software today is often preferable to perfect software tomorrow.

If you give your users something to play with early, their feedback will often lead you to a better eventual solution.


An investment in knowledge always pays the best interest. —Benjamin Franklink

You must invest in your knowledge portfolio regularly.

The more different things you know, the more valuable you are.

Learn at least one new language every year.  

Design phase

If possible, involve your readers with early drafts of your document.

Software design

The evils of duplication and orthogonality, are closely related. The first warns you not to duplicate knowledge throughout your systems, the second not to split any one piece of knowledge across multiple system components.

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

Make it easy to reuse.

Eliminate effects between unrelated things.

When teams are organized with lots of overlap, members are confused about responsibilities. Every change needs a meeting of the entire team, because any one of them might be affected.

If you need to change an object’s state, get the object to do it for you.

Avoid global data.

In general, your code is easier to understand and maintain if you explicitly pass any required context into your modules.

Avoid similar functions.


Nothing is more dangerous than an idea if it’s the only one you have. —Emil-Auguste Chartier, Propos sur la religion, 1938

With every critical decision, the project team commits to a smaller target a narrower version of reality that has fewer options.

Requirements, users, and hardware change faster than we can get the software developed.

Prototyping generates disposable code. Tracer code is lean but complete, and forms part of the skeleton of the final system.

Computer languages influence how you think about a problem, and how you think about communicating.


The first question you have to ask yourself when someone asks you for an estimate is the context in which your answer will be taken. Do they need high accuracy, or are they looking for a ballpark figure?

Choose the units of your answer to reflect the accuracy you intend to convey.

Trick that always gives good answers: ask someone who’s already done it.

What to Say When Asked for an Estimate: “I’ll get back to you.”

Start keeping a log of your estimates.

Always be on the lookout for better ways of doing things.

Plain text, editors

With plain text, however, you can achieve a self-describing data stream that is independent of the application that created it.

Keep knowledge in plain text

All software becomes legacy as soon as it’s written.

Use plaintext for data so that it always outlive your software which is already a legacy code.

Plain text is that standard.

But if you do all your work using GUIs, you are missing out on the full capabilities of your environment.

As one of the new languages you are going to learn this year, learn the language your editor uses.

Source code control

Always use source code control


Debugging is just problem solving,

Fix the problem, not the blame.

The easiest person to deceive is one’s self.

Always try to discover the root cause of a problem, not just this particular appearance of it.

You must brutally test both boundary conditions and realistic end-user usage patterns. You need to do this systematically.

The best way to start fixing a bug is to make it reproducible.

Sometimes by forcing yourself to isolate the circumstances that display the bug, you’ll even gain an insight on how to fix it.

Explain it to someone else.

If it took a long time to fix this bug, ask yourself why. Is there anything you can do to make fixing this bug easier the next time around?

Interesting feature of passive code generators: they don’t have to be totally accurate. You get to choose how much effort you put into the generator, compared with the energy you spend fixing up its output. Producing lookup tables and other resources that are expensive to compute at runtime.

It is the caller’s responsibility to pass good data

Crash early.

A dead program normally does a lot less damage than a crippled one.

Never put code that must be executed into an assert.

Assertions check for things that should never happen.

An exception represents an immediate, nonlocal transfer of control it’s a kind of cascading goto.

Languages that do not support exceptions often have some other nonlocal transfer of control mechanism (C has longjmp/setjmp, for example).

The routine or object that allocates a resource should be responsible for deallocating it.

Organize your code into cells (modules) and limit the interaction between them.

Minimize coupling between modules

A more flexible approach is to write programs that can reload their configuration while they’re running.

Design for concurrency

A good definition of a module (or class) is that it has a single, well-defined responsibility.

Publish/subscribe protocol

Make code easy to test, and you’ll increase the likelihood that it will actually get tested.

For routines you call, rely only on documented behavior. If you can’t, for whatever reason, then document your assumption well.

Don’t assume it, prove it.

Attempting to build an application you don’t fully understand, or to use a technology you aren’t familiar with, is an invitation to be misled by coincidences.

Document your assumptions.

Don’t let existing code dictate future code.


Refactor early, refactor often.

Keep track of the things that need to be refactored. If you can’t refactor something immediately, make sure that it gets placed on the schedule.

Test your software, or your users will.

Don’t use wizard code you don’t understand


Perfection is achieved, not when there is nothing left to add, but when there is nothing left to take away.
—Antoine de St. Exupery, Wind, Sand, and Stars, 1939

There’s a simple technique for getting inside your users’ requirements that isn’t used often enough: become a user.

Work with a user to think like a user.

Good requirements documents remain abstract.

By tracking requirements you can get a clearer picture that “just one more feature” is really the fifteenth new feature added this month.

Create and maintain a project glossary.

It’s not whether you think inside the box or outside the box. The problem lies in finding the box identifying the real constraints.

Don’t be a slave to formal methods.

We see some benefit in capturing requirements this way, but we prefer, where possible, to show the user a prototype and let them play with it.


Quality can come only from the individual contributions of all team members.

The team as an entity needs to communicate clearly with the rest of the world.

Generate a brand. When you start a project, come up with a name for it, ideally something off-the-wall.

Organize around functionality, not job functions.


Why lay code out manually when your editor can do it automatically as you type? Why complete test forms when the overnight build can run tests automatically?

Civilization advances by extending the number of important operations we can perform without thinking. — Alfred North Whitehead

Don’t use manual procedures.

We want to check out, build, test, and ship with a single command.

Misleading information is worse than no information at all.

It seems that while code inspection is effective, conducting reviews in meetings is not.

Test Early. Test Often. Test Automatically.

Unit test is code that exercises a module.

Test state coverage, not code coverage

If a bug slips through the net of existing tests, you need to add a new test to trap it next time.


Gently exceed your users’ expectations.

Sign your work.

published: 2023-04-23
last modified: 2023-11-19