
The integration is almost finished when someone opens an actual supplier file. Product identifiers lose leading zeros, some rows repeat, and a negative quantity means a return in one system but an error in the other. The team built a screen, selected storage and wrote a scheduler before checking what the data supporting its plan actually meant.
This fictional failure helps explain why I recommend The Pragmatic Programmer. Its value lies in improving decisions during development: investigating assumptions, obtaining feedback and reducing the cost of correcting direction. The benefit depends on turning memorable advice into specific experiments. Calling an approach pragmatic does not explain which risk was investigated or why the solution fits its circumstances.
This review covers The Pragmatic Programmer: Your Journey to Mastery, by David Thomas and Andrew Hunt, the twentieth-anniversary second edition, published in 2019, ISBN 9780135957059. The Addison-Wesley/InformIT catalog identifies it as a revision of the 1999 book, with new topics and examples. This is not an assessment of the first edition.
Its arrangement into relatively independent topics suits reading alongside real work. You can study an idea, try it and return with a more precise question. I recommend resisting the urge to turn the advice into a mandatory checklist for every project. The value of its range is that it supplies different questions for different problems, including tools, testing, requirements and coordination.
Among the ideas discussed here, I find its attention to duplicated knowledge, independence between parts and the distinction between learning from a prototype and building a version that will remain particularly useful. They connect through one concern: how much work will a mistaken assumption force us to repeat?
The official DRY extract explains duplication in terms of knowledge and intent, including across code and documentation. The authors acknowledge that their earlier explanation allowed an interpretation limited to copying and pasting code. That clarification is a specific reason to pay attention to the revised edition.
Suppose an application accepts batches containing at most a thousand rows. If its screen, importer and manual independently record that limit, a change requires remembering three places. A shared contract or generated documentation can reduce inconsistency. The question is which representation has authority and how the others are derived or checked.
Two validators accepting the same character sequence today might have different reasons. One handles product codes and another handles customer identifiers. Merging them because their text looks similar could force unrelated areas to evolve together. I would keep the repetition until the knowledge really was shown to be the same. The cost of a mistaken abstraction will surface in subsequent changes too.
This reasoning extends to tests. A test obtaining its expected result by calling the very function under examination can repeat an error consistently. Some acceptance examples need independently chosen values. Centralizing a rule should not remove every opportunity to notice that its implementation is wrong. A single source of authority still needs meaningful verification.
The authorized tip collection for the anniversary edition includes independence between parts, openness to change and verification of assumptions. My application of orthogonality would be to ask whether changing an input format requires changing decisions unrelated to that format. A new inventory-file delimiter should not alter import authorization.
That may justify separating file reading, business validation and presentation. Separation carries costs, so I would check whether each part actually hides a decision. Placing the same sequence in five classes that share global state is unlikely to create independence. The useful criterion is how a change propagates, not how many boxes appear in the diagram.
Reversibility must include data and users. Replacing a parser before importing any batches differs from correcting thousands of published records. A database choice can be reconsidered, but migration costs work, compatibility decisions and risk. I would translate the advice into preparing samples, versioning contracts and delaying effects that are difficult to undo until behavior is better understood. Keeping options open becomes cheaper when the team recognizes where those options become expensive.
The authors explain prototypes and a minimal path through a system in The Art in Computer Programming, a 2001 essay predating the reviewed edition. I use it to clarify a distinction also present in the second edition's organization: an experiment can be discarded after producing knowledge, while a tracer bullet establishes a path that will be extended.
For the supplier example, an isolated script can check whether the parser preserves identifiers in test files. A synthetic sample provides controlled inputs without authentication or scheduling. Investigating the supplier's actual encoding and format requires a representative sample from that supplier or a confirmed contract. The useful result records what each experiment supports. That does not automatically make the script a candidate for production service code.
A first slice intended to remain requires different care. It crosses receipt, parsing, validation and presentation, with narrow scope and observable behavior. It may accept only one format and one item type, but should not depend on shortcuts the team intends to forget. Confusing small scope with temporarily unsafe behavior is one way a prototype quietly becomes production software.
I would record which kind of work we are doing, what we expect to learn and who decides what happens next before starting. That helps prevent a visually convincing demonstration from becoming a promise of a complete delivery. Both techniques reduce uncertainty, but they leave different artifacts and maintenance obligations.
Free and paid products to turn ideas into a foundation you can actually ship.
13 products availableContinue exploring similar topics

A critical review of The Clean Coder: estimates, negotiation, testing and responsibility, with a concrete scenario and limits to individual accountability.

A critical review of Cal Newport on concentration, engineering work and availability limits in real teams.

Apply Michael Nygard's approach to timeouts, isolation and recovery through a controlled failure exercise.
A 47-point checklist to find bugs, security risks, and performance issues before launch.
Production-tested templates trusted by developers. Save weeks of setup on your next project.
The following scenario is original and intended for teaching. A supplier sends CSV files to update quantities. The team must establish whether quantity means current balance or a change in balance, whether identifiers are case-sensitive, and whether repeated identifiers represent corrections or errors. These questions affect the meaning of import and require answers from the data-contract owner.
The first disposable experiment checks only how to read a sample while preserving identifiers as text. Then, assuming the contract confirms current balances and exact codes, the team builds a retained slice: receive a small file, validate it and display a preview in staging. It does not publish changes to live inventory.
For this exercise, the rules are explicit: negative quantities are invalid, a repeated code in the same file invalidates the batch, and repeating the same batch does not create another preview. These are scenario decisions, not universal importer requirements.
| Exercise input | Expected observable result |
|---|---|
One row with code 00017 and quantity 5 | Preview shows exactly 00017, including its leading zeros, with proposed balance 5 |
One row with quantity -1 | Batch rejected with a reason; nothing published |
Two rows with code A-17 | Batch rejected for a repeated code |
| Resubmission of an already staged batch | No additional preview; previous result identified |
Working through these cases exposes issues a disconnected screen would not establish: batch identity, error messages, code preservation and the location of state. An operator can check whether the preview expresses the decision they need to make. If the meaning of balance was mistaken, the team discovers that before automating a real update.
This slice is still far from a complete importer. Size limits, concurrency, retention, publication authorization and recovery remain, along with representative-volume validation. Initial acceptance establishes only the agreed scope. A decision to extend it should identify the next uncertainty to address, rather than adding features because they appeared in the original plan.
It is equally useful to discover that a proposed slice cannot answer the question. A local parser test cannot establish the supplier's delivery reliability, for example. The experiment needs to reach the dependency whose behavior matters, or state that its evidence does not cover it.
I would automate those cases after understanding the contract. A verification command can load controlled files, inspect results and fail explicitly when an expectation is unmet. Another developer should be able to run it with documented dependencies. A command dependent on private paths and forgotten local data offers little dependable feedback.
A framework can help through conventions, test facilities and code generation. Using those facilities may be simpler than building an independent structure around every detail. I would check what gets generated, which files should be edited and how to reproduce generation. Manually editing derived outputs often creates two versions of the same knowledge.
Permanent effects require their own automation boundaries. Repeating a sample read differs from repeating an inventory update. The workflow needs to separate staging from publication, identify operations and leave enough evidence to investigate failures. Fast feedback is useful when the team understands what ran and can connect its result to the assumption under examination.
Short statements make ideas easier to remember, but also make it easier to lose their conditions. DRY can turn into a demand to merge independent validators. Reversibility can sound like a promise of cheap replacement. Automation can justify spending days on a rare task. Those would be poor applications, but avoiding them requires additional work from the reader.
My principal reservation is the distance between general advice and a situated decision. Which volume makes an experiment representative? Which integration can be replaced without losing semantics? Who accepts the cost of migration? The book's repertoire improves these questions but cannot answer them for a particular application. Teams still need domain knowledge and an understanding of environmental constraints.
Individual responsibility has limits too. One developer can propose a thin slice, but permissions, budgets, test environments and priorities depend on other decisions. I would not use the book to judge the dedication of someone unable to change that working system alone. Recommendations about habits need conditions that make those habits possible.
I recommend this edition to readers who can write small programs, use version control and follow basic tests. A task already underway supplies material for experiments. Experienced developers can use the book to reconsider habits that have become automatic. Someone still wrestling with syntax may benefit more from building that foundation before attempting several principles at once.
Within the series, Clean Code concentrates on understanding code; this book also asks how we discover that we are building the wrong thing. Refactoring is a useful continuation after identifying a needed change and wanting controlled steps to make it. Accelerate broadens the conversation toward delivery capability and organizational conditions, helping avoid treating every delay as a failure of personal habit.
Choose two topics connected to current work and record a question before reading. For the import, those might be "are we repeating knowledge or only syntax?" and "do we need to learn about a format or establish a retained workflow?" Then define a small experiment, its expected evidence and the decision it should inform. Return to the text with the costs and unanswered questions that emerge.
My verdict is to recommend the book for the range of criteria it provides for examining your own work. The revision deserves particular attention for clarifying DRY and offering a chance to test familiar advice against present problems. Its benefit becomes visible when an assumption is exposed before an expensive effect, an unnecessary abstraction is avoided or a result becomes easier to reproduce. Those are outcomes a team can observe.