Chapter 1: A Pragmatic Philosophy
Tip 1: Care About Your Craft
Take pride in your work; if you are going to spend time building software, commit to doing a high-quality job rather than just churning out code.
Tip 2: Think! About Your Work
Avoid working on autopilot. continuously critique your own code and decisions to ensure you are solving the right problems effectively.
Tip 3: You Have Agency
Remember that you are in control of your career and environment. If something is wrong, you have the power to change it or leave; you are not a helpless victim.
Tip 4: Provide Options, Don’t Make Lame Excuses
When things go wrong, focus on solutions and alternatives rather than explaining why you failed. Stakeholders need a path forward, not an apology.
Tip 5: Don’t Live with Broken Windows
Fix bad code, messy designs, and poor decisions immediately. Leaving them creates a culture of neglect that encourages further degradation of the system.
Tip 6: Be a Catalyst for Change
If you want to improve a process, show people it works by doing a small part of it well. People are more likely to join an ongoing success than to agree to a theoretical overhaul.
Tip 7: Remember the Big Picture
Do not get so lost in the technical details that you miss the broader context of the project. Regularly check if what you are doing still aligns with the business goals.
Tip 8: Make Quality a Requirements Issue
Perfection is expensive and often unnecessary. Define what “good enough” means for your specific users and context to avoid over-engineering.
Tip 9: Invest Regularly in Your Knowledge Portfolio
Treat your skills like an investment portfolio. Learn constantly, diversify your knowledge, and balance high-risk technologies with stable, foundational ones.
Tip 10: Critically Analyze What You Read and Hear
Do not blindly accept hype or dogma from vendors and influencers. Always verify claims and consider if they actually apply to your specific situation.
Chapter 2: A Pragmatic Approach
Tip 11: English is Just Another Programming Language
Treat requirements and documentation with the same rigor as code. Ambiguity in language leads to bugs just as surely as syntax errors do.
Tip 12: It’s Both What You Say and the Way You Say It
Technical skills are useless if you cannot communicate your ideas. Tailor your communication style to your audience’s needs and level of understanding.
Tip 13: Build Documentation In, Don’t Bolt It On
Keep documentation close to the code it describes so it evolves with the system. Separate documentation inevitably becomes outdated and misleading.
Tip 14: Good Design Is Easier to Change Than Bad Design
The primary measure of a good design is how easily it adapts to future requirements. If it is hard to change, it is bad design.
Tip 15: DRY - Don’t Repeat Yourself
Ensure every piece of knowledge has a single, authoritative representation in your system. Duplicating logic means you have to fix bugs in multiple places.
Tip 16: Make It Easy to Reuse
If you want people to reuse code, you must make reuse easier than rewriting. Create an environment where finding and using existing components is the path of least resistance.
Tip 17: Eliminate Effects Between Unrelated Things
Design components that are self-contained. Changes in one part of the system should not break unrelated parts; this is the principle of orthogonality.
Tip 18: There Are No Final Decisions
Stay flexible because requirements and technologies will change. Design your architecture so that decisions can be reversed or swapped out later.
Tip 19: Forgo Following Fads
Choose technologies based on their utility for your specific problem, not because they are currently popular or trendy.
Tip 20: Use Tracer Bullets to Find the Target
Build a thin, end-to-end slice of the system to verify that all architectural layers work together before filling in the details.
Tip 21: Prototype to Learn
Build quick, disposable models to answer specific questions or test risky ideas. Discard the code once you have learned the lesson.
Tip 22: Program Close to the Problem Domain
Use the vocabulary of the business in your code. This reduces the translation gap between what the user needs and what the software does.
Tip 23: Estimate to Avoid Surprises
Perform estimation to discover what you don’t know. The process of estimating forces you to understand the problem’s complexity.
Tip 24: Iterate the Schedule with the Code
Update your schedule as you progress. Use the feedback from the actual work to refine your timeline rather than sticking to an initial guess.
Chapter 3: The Basic Tools
Tip 25: Keep Knowledge in Plain Text
Store data and configuration in human-readable formats. They are durable, easy to parse, and work with virtually every tool.
Tip 26: Use the Power of Command Shells
Master the command line. It allows you to compose tools and automate tasks in ways that graphical interfaces cannot match.
Tip 27: Achieve Editor Fluency
Learn your text editor inside and out. You should be able to manipulate text as fast as you can think, without the tool getting in your way.
Tip 28: Always Use Version Control
Use version control for everything, not just code. It provides a safety net, a history of changes, and a way to collaborate without fear.
Tip 29: Fix the Problem, Not the Blame
When a bug occurs, focus on fixing the system ensuring it doesn’t happen again, rather than finding someone to blame.
Tip 30: Don’t Panic
When things go wrong, stay calm. Panic leads to rash decisions and tunnel vision, which usually make the problem worse.
Tip 31: Failing Test Before Fixing Code
Never fix a bug without first writing a test that reproduces it. This proves the bug exists and ensures it won’t come back later.
Tip 32: Read the Damn Error Message
Do not guess at the problem. The system usually tells you exactly what is wrong if you take the time to read the error output carefully.
Tip 33: “select” Isn’t Broken
Assume the bug is in your application code, not in the compiler or the operating system. Rare system bugs happen, but your logic errors are much more common.
Tip 34: Don’t Assume It—Prove It
Do not rely on assumptions about how things work. Verify your theories with tests and data in the actual environment.
Tip 35: Learn a Text Manipulation Language
Master a scripting language that handles text well. This allows you to quickly automate repetitive tasks and transform data.
Chapter 4: Pragmatic Paranoia
Tip 36: You Can’t Write Perfect Software
Accept that software will fail. Design your systems to handle errors gracefully and protect user data when things go wrong.
Tip 37: Design with Contracts
Define exactly what your functions expect and what they promise to return. This makes it clear who is responsible when something breaks.
Tip 38: Crash Early
It is better for a program to crash immediately than to continue running with corrupted data. Fail fast so the problem is obvious and contained.
Tip 39: Use Assertions to Prevent the Impossible
Use assertions to check for conditions that should never happen. If an “impossible” state is reached, the program should halt and alert you.
Tip 40: Finish What You Start
If you allocate a resource, you are responsible for deallocating it. Balance every setup with a teardown to prevent leaks.
Tip 41: Act Locally
Keep variables and resources in the smallest possible scope. Global state is hard to reason about and causes spooky side effects.
Tip 42: Take Small Steps—Always
Make small, incremental changes. This minimizes the risk of breaking the system and makes it easier to undo mistakes.
Chapter 5: Bend, or Break
Tip 43: Avoid Fortune-Telling
Do not write code for future requirements that do not exist yet. You will likely guess wrong and burden the code with unnecessary complexity.
Tip 44: Decoupled Code Is Easier to Change
Reduce the dependencies between modules. If components are loosely coupled, you can change one without breaking the others.
Tip 45: Tell, Don’t Ask
Command objects to perform actions rather than querying their state to make decisions for them. This keeps logic encapsulated where it belongs.
Tip 46: Don’t Chain Method Calls
Avoid traversing through multiple objects to get a value. It creates tight coupling to the internal structure of dependencies.
Tip 47: Avoid Global Data
Global data makes code unpredictable and hard to test because any part of the system can change it at any time.
Tip 48: If It’s Important Enough To Be Global, Wrap It in an API
If you must use global state, hide it behind an interface so you can control and monitor how it is accessed.
Tip 49: Programming Is About Code, But Programs Are About Data
Focus on how data flows and transforms through your system. Thinking in data pipelines often leads to cleaner designs than thinking in objects.
Tip 50: Don’t Hoard State; Pass It Around
Prefer stateless functions that take input and return output. Passing data explicitly is safer than holding onto it in internal variables.
Tip 51: Don’t Pay Inheritance Tax
Avoid deep inheritance hierarchies. They are brittle and create tight coupling. Use composition or interfaces instead.
Tip 52: Prefer Interfaces to Express Polymorphism
Use interfaces to define what an object can do, rather than inheritance to define what it is. This allows for more flexible code.
Tip 53: Delegate to Services: Has-A Trumps Is-A
Use composition to share behavior. An object should “have” a logger, not “be” a logger.
Tip 54: Use Mixins to Share Functionality
Inject specific capabilities into classes without forcing them into a rigid family tree.
Tip 55: Parameterize Your App Using External Configuration
Store settings outside the application code. This allows you to change behavior for different environments without recompiling.
Chapter 6: Concurrency
Tip 56: Analyze Workflow to Improve Concurrency
Look for tasks in the real world that happen at the same time and model your software to match. This reveals natural opportunities for parallelism.
Tip 57: Shared State Is Incorrect State
Eliminate shared mutable state to avoid race conditions. If resources are not shared, they cannot be corrupted by concurrent access.
Tip 58: Random Failures Are Often Concurrency Issues
If a bug is hard to reproduce or happens intermittently, assume it is a concurrency problem. Timing variations often expose race conditions.
Tip 59: Use Actors For Concurrency Without Shared State
Use the Actor model or similar patterns to manage concurrency. By sending messages instead of sharing memory, you avoid locks and synchronization headaches.
Chapter 7: While You Are Coding
Tip 60: Use Blackboards to Coordinate Workflow
Decouple complex workflows by having agents post data to a shared space. This allows components to react to data without knowing about each other.
Tip 61: Listen to Your Inner Lizard
Trust your intuition. If the code feels wrong or hard to write, pause and re-evaluate the design; your subconscious often spots problems before you do.
Tip 62: Don’t Program by Coincidence
Understand exactly why your code works. Relying on luck or undefined behavior is a recipe for disaster.
Tip 63: Estimate the Order of Your Algorithms
Know the complexity of your code. Understanding Big-O notation helps you predict how your system will perform as data grows.
Tip 64: Test Your Estimates
Mathematical theory is a guide, but real-world performance depends on hardware and context. Measure your code to verify your estimates.
Tip 65: Refactor Early, Refactor Often
Keep code clean as you work. Refactoring is a continuous activity, not a phase at the end of the project.
Tip 66: Testing Is Not About Finding Bugs
Tests are primarily a design tool. They provide feedback on how usable and decoupled your code is.
Tip 67: A Test Is the First User of Your Code
Writing a test forces you to experience your own API. If it is painful to test, it will be painful to use.
Tip 68: Build End-To-End, Not Top-Down or Bottom Up
Build a small vertical slice of functionality that connects all layers. This proves the architecture works before you invest too much time.
Tip 69: Design to Test
Write code with testing in mind from the very beginning. Testability usually enforces good design principles like decoupling.
Tip 70: Test Your Software, or Your Users Will
Be ruthless in your testing. It is unprofessional to let your paying customers find your bugs for you.
Tip 71: Use Property-Based Tests to Validate Your Assumptions
Use tools that generate random data to test general rules about your code. This uncovers edge cases you would never think to write manually.
Tip 72: Keep It Simple and Minimize Attack Surfaces
Complexity hides security vulnerabilities. Simple code has fewer places for attackers to hide.
Tip 73: Apply Security Patches Quickly
Security is a race. When a vulnerability is found in your dependencies, patch it immediately.
Tip 74: Name Well; Rename When Needed
Names should express intent. If the code changes, update the name to reflect the new reality. Misleading names are dangerous.
Chapter 8: Before the Project
Tip 75: No One Knows Exactly What They Want
Requirements are rarely static. Treat them as a starting point for discovery, not a fixed target.
Tip 76: Programmers Help People Understand What They Want
Your job is to help the client explore the problem space. You co-create the requirements by showing them what is possible.
Tip 77: Requirements Are Learned in a Feedback Loop
Use iteration to refine requirements. Show the user a draft, get feedback, and adjust.
Tip 78: Work with a User to Think Like a User
Observe your users in their actual environment. You will learn more from watching them work than from reading any specification.
Tip 79: Policy Is Metadata
Extract business rules and policies into configuration files. This allows the business to change rules without requiring you to rewrite code.
Tip 80: Use a Project Glossary
Establish a common vocabulary. Everyone on the team should use the same terms for the same concepts to avoid confusion.
Tip 81: Don’t Think Outside the Box—Find the Box
Identify the true constraints of the problem. Often, the “box” is just a set of assumptions that can be challenged.
Tip 82: Don’t Go into the Code Alone
Collaborate with others. Pair programming and code reviews improve quality and spread knowledge.
Tip 83: Agile Is Not a Noun; Agile Is How You Do Things
Agile is a mindset of adaptation and feedback, not a set of ceremonies or tools.
Chapter 9: Pragmatic Projects
Tip 84: Maintain Small Stable Teams
Small teams communicate better and build trust faster. Large teams often suffer from bureaucracy and friction.
Tip 85: Schedule It to Make It Happen
If you don’t schedule time for maintenance, learning, and improvement, they will never happen.
Tip 86: Organize Fully Functional Teams
Build teams around features, not job titles. A team should have all the skills needed to deliver a feature from start to finish.
Tip 87: Do What Works, Not What’s Fashionable
Adopt practices that actually help your team in your context, rather than just following the latest industry trends.
Tip 88: Deliver When Users Need It
Release software when it provides value, not just when a schedule says so. Continuous delivery reduces risk.
Tip 89: Use Version Control to Drive Builds, Tests, and Releases
Automate your pipeline. Your version control system should be the trigger for building and deploying your software.
Tip 90: Test Early, Test Often, Test Automatically
Shorten the feedback loop. Automated tests running constantly ensure that you know about breaks immediately.
Tip 91: Coding Ain’t Done ‘Til All the Tests Run
Code that hasn’t passed tests is incomplete. Do not commit or ship code that hasn’t been verified.
Tip 92: Use Saboteurs to Test Your Testing
Intentionally break your code to ensure your tests actually catch the failure. If the tests pass when the code is broken, they are useless.
Tip 93: Test State Coverage, Not Code Coverage
Focus on testing the logic and states of your application, not just hitting every line of code.
Tip 94: Find Bugs Once
When you find a bug, write a test to ensure it never comes back. This builds a regression suite that protects you over time.
Tip 95: Don’t Use Manual Procedures
Humans make mistakes; scripts do not. Automate every repetitive task to ensure consistency and reliability.
Tip 96: Delight Users, Don’t Just Deliver Code
Your goal is to solve the user’s problem and make their life better, not just to write lines of code.
Tip 97: Sign Your Work
Take ownership of what you produce. Be proud enough of your work to put your name on it.
Tip 98: First, Do No Harm
Ensure your software does not damage data, security, or the user’s business. Reliability is an ethical obligation.
Tip 99: Don’t Enable Scumbags
Refuse to build unethical software. You are responsible for the consequences of the code you write.
Tip 100: It’s Your Life. Share it. Celebrate it. Build it. AND HAVE FUN!
Software development is a creative and social endeavor. Enjoy the process and use your skills to build great things.