I often contemplate what makes great developers stand out from good ones. After years of seeing teams repeatedly face the same problems, I keep returning to one book: The Pragmatic Programmer by Andrew Hunt and David Thomas.
Some developers know of this book, but few have internalized its principles. They still make the same mistakes Hunt and Thomas warned about decades ago.
I recommend reading A Pragmatic Programmer Book Review and then purchasing the book to support the authors.
You don’t need to read the entire book to improve your code today. This list offers essential principles as practical advice, but it’s no substitute for the impactful stories in the book.
Care About Your Craft
Why spend your life developing software unless you care about doing it well?
This isn’t about perfectionism but taking pride in your work. Caring about your craft drives you to improve, ask questions, experiment, and learn.
Many developers treat coding as a paycheck factory, writing functional code without considering maintainability, readability, or elegance. That’s not artisanship, but assembly line work.
Think About Your Work
Turn off autopilot, take control, and constantly critique your work.
When writing functions, ask if it’s the best solution. During debugging, identify the root cause instead of just fixing the symptom.
The standout developers aren’t always the smartest; they’re the ones who measure twice, then commit.
Provide Options, Don’t Make Excuses
Instead of excuses, offer options and explain how to salvage the situation.
I’ve lost track of how often I’ve heard “That’s not possible” when what the developer truly means is “I don’t know how to do that.” There’s a difference.
When you hit a wall, figure out what’s blocking you and suggest alternatives. Your team will respect you, and you will be able to solve more problems.
The Broken Windows Principle
A broken window left unrepaired fosters abandonment, which in turn leads to increased window breakage, littering, graffiti, and structural damage.
Don’t Live with Broken Windows
This principle can change how you handle codebases. Fix long-standing TODO comments, functions like doStuff(), or copied code immediately.
Not because it’s broken, but because it’s a broken window. Left alone, it signals to other developers that quality doesn’t matter here.
The DRY Principle: Don’t Repeat Yourself
Every knowledge piece must have a single, clear, authoritative form within a system.
This isn’t just about copying code but duplicating knowledge. Repeating business rules or scattered configurations violates the DRY principle.
The issue isn’t duplication but the risk of forgetting to update it in three places when changes are needed.
Orthogonality: Eliminate Effects Between Unrelated Things
Two or more things are orthogonal if changes in one don’t affect others.
Write “shy” code: code that doesn’t reach out to everything, does one thing well, and ignores the rest of the system.
Orthogonal code makes changes easier and more isolated. You can modify, test, and reuse components without affecting others.
Design by Contract
A correct program only does what it claims.
Use preconditions, postconditions, and invariants. Be strict about acceptances upfront and promise as little as possible.
This isn’t about writing more code. It’s about being explicit about your code’s expectations and guarantees. Being explicit makes bugs obvious; vagueness hides bugs.
Crash Early
A dead program does less damage than a crippled one.
When your code discovers that something that was supposed to be impossible just happened, your program is no longer viable. Don’t try to limp along. Crash immediately and crash loudly.
I’ve seen many systems that log an error and continue handling impossible situations. These systems are nightmares to debug because the error occurs elsewhere, but symptoms appear everywhere.
Refactor Early, Refactor Often
Code needs to evolve. It’s not a static thing.
When should you refactor?
- You’ve discovered a violation of the DRY principle.
- You’ve found code that could be more orthogonal.
- Your knowledge of the problem has improved.
- The requirements have evolved.
- You need to improve performance.
Avoid refactoring and adding features at the same time. Ensure thorough tests are in place first. Take small, deliberate steps.
Test Early, Test Often, Test Automatically
Tests that run with every build are the most effective.
Finding a bug early reduces the costs of fixing it. “Code a little, test a little.”
The 70 Tips Quick Reference
Here are 70 tips from The Pragmatic Programmer, organized for quick reference:
Philosophy and Approach
1. Care About Your Craft
Why spend your life developing software unless you care about doing it well?
2. Think! About Your Work
Turn off the autopilot and take control.
3. Provide Options, Don’t Make Lame Excuses
Instead of excuses, provide options.
4. Don’t Live with Broken Windows
Fix bad designs, wrong decisions, and poor code when you see them.
5. Be a Catalyst for Change
Show people how the future might be and help them participate in creating it.
6. Remember the Big Picture
Don’t get so engrossed in the details that you forget to check what’s happening around you.
7. Make Quality a Requirements Issue
Involve your users in determining the project’s real quality requirements.
Knowledge and Learning
8. Invest Regularly in Your Knowledge Portfolio
Make learning a habit.
9. Critically Analyze What You Read and Hear
Don’t be swayed by vendors, media hype, or dogma.
10. It’s Both What You Say and the Way You Say It
There’s no point in having great ideas if you don’t communicate them effectively.
Code Organization
11. DRY – Don’t Repeat Yourself
Every piece of knowledge must have a single, unambiguous, authoritative representation.
12. Make It Easy to Reuse
If it’s easy to reuse, people will.
13. Eliminate Effects Between Unrelated Things
Design components that are self-contained and independent of each other.
14. There Are No Final Decisions
No decision is cast in stone. Always plan for change.
15. Use Tracer Bullets to Find the Target
Tracer bullets help you target precisely by focusing on the end goal and advancing cautiously with small, inexpensive steps.
Prototyping and Learning
16. Prototype to Learn
Prototyping is a learning experience. Its value lies in the lessons you learn.
17. Program Close to the Problem Domain
Design and code in your user’s language.
18. Estimate to Avoid Surprises
Estimate before you start. You’ll spot potential problems up front.
19. Iterate the Schedule with the Code
Use the experience you gain as you implement to refine the project time scales.
Tools and Environment
20. Keep Knowledge in Plain Text
Plain text won’t become obsolete.
21. Use the Power of Command Shells
Use the shell when graphical user interfaces don’t cut it.
22. Use a Single Editor Well
The editor should be an extension of your hand.
23. Always Use Source Code Control
Source code control is a time machine for your work.
24. Fix the Problem, Not the Blame
It doesn’t really matter whether the bug is your fault or someone else’s.
25. Don’t Panic When Debugging
Take a deep breath and consider what might be causing the issue.
Debugging and Problem Solving
26. “select” Isn’t Broken
The bug is most likely in the application, not the OS or compiler.
27. Don’t Assume It – Prove It
Prove your assumptions in the actual environment.
28. Learn a Text Manipulation Language
You spend a large part of each day working with text.
29. Write Code That Writes Code
Code generators increase your productivity and help avoid duplication.
Error Handling and Contracts
30. You Can’t Write Perfect Software
Software can’t be perfect. Protect your code and users from errors.
31. Design with Contracts
Use contracts to document and verify that code does what it claims.
32. Crash Early
A dead program usually does a lot less damage than a crippled one.
33. Use Assertions to Prevent the Impossible
Assertions validate your assumptions.
34. Use Exceptions for Exceptional Problems
Reserve exceptions for exceptional things.
35. Finish What You Start
The routine that allocates a resource should be responsible for deallocating it.
Design and Architecture
36. Minimize Coupling Between Modules
Avoid coupling by writing “shy” code.
37. Configure, Don’t Integrate
Implement technology choices as configuration options.
38. Put Abstractions in Code, Details in Metadata
Program for the general case.
39. Analyze Workflow to Improve Concurrency
Exploit concurrency in your user’s workflow.
40. Design Using Services
Design in terms of services behind well-defined interfaces.
41. Always Design for Concurrency
Allow for concurrency, and you’ll design cleaner interfaces.
42. Separate Views from Models
Gain flexibility at low cost by designing models and views.
43. Use Diagrams to Coordinate Workflow
Use diagrams to coordinate disparate facts and agents.
Programming Practices
44. Don’t Program by Coincidence
Rely only on reliable things. Beware of accidental complexity.
45. Estimate the Order of Your Algorithms
Get a feel for how long things are likely to take.
46. Test Your Estimates
Mathematical analysis doesn’t tell you everything.
47. Refactor Early, Refactor Often
Just as you might weed and rearrange a garden.
48. Design to Test
Start thinking about testing before you write a line of code.
49. Test Your Software, or Your Users Will
Test ruthlessly. Don’t make your users find bugs.
50. Don’t Use Wizard Code You Don’t Understand
Make sure you understand all the generated code.
Requirements and Planning
51. Don’t Gather Requirements – Dig for Them
Requirements rarely lie on the surface.
52. Work With a User to Think Like a User
It’s the best way to gain insight into how the system will really be used.
53. Abstractions Live Longer than Details
Invest in the abstraction, not the implementation.
54. Use a Project Glossary
Create and maintain a single source of all specific terms.
55. Don’t Think Outside the Box – Find the Box
When faced with an impossible problem, identify the real constraints.
56. Start When You’re Ready
You’ve been building experience all your life. Don’t ignore doubts.
57. Some Things Are Better Done than Described
Don’t fall into the specification spiral.
58. Don’t Mindlessly Adopt Formal Methods
Don’t unquestioningly adopt any technique.
59. Costly Tools Don’t Produce Better Designs
Judge tools on their merits.
Team and Project Management
60. Organize Teams Around Functionality
Don’t separate designers from coders. Build teams like you build code.
61. Don’t Use Manual Procedures
A script will execute the exact instructions every time.
62. Test Early. Test Often. Test Automatically
Tests that run with every build are much more effective.
63. Coding Ain’t Done ‘Til All the Tests Run
‘Nuff said.
64. Use Saboteurs to Test Your Testing
Introduce bugs intentionally to verify that testing catches them.
65. Test State Coverage, Not Code Coverage
Identify and test significant program states.
66. Find Bugs Once
Once a human tester identifies a bug, automated tests should verify it’s fixed from then on.
Documentation and Communication
67. English is Just a Programming Language
Write documents as you would write code.
68. Build Documentation In, Don’t Bolt It On
Documentation created separately from code is less likely to be correct.
69. Gently Exceed Your Users’ Expectations
Come to understand your users’ expectations, then deliver just that little bit more.
70. Sign Your Work
Artisans of an earlier age took pride in signing their work.
How to Use This List
Don’t try to implement all 70 tips at once. Focus on one principle for a week until it becomes second nature, then choose another.
Begin with core principles: care about your craft, consider your work, and avoid neglect. These will transform your approach.
The goal isn’t to be a perfect programmer but a pragmatic one—writing code that works, is maintainable, and solves real problems.
Comments #