97 Things Every Programmer Should Know: In Practice
In an industry obsessed with fleeting frameworks and hype-driven development, some wisdom is timeless. "97 Things Every Programmer Should Know" is a collection of such wisdom. It's not about a specific language or technology, but about the fundamental principles of crafting good software.
But principles are useless without practice. Here's how we're turning these lessons into tangible, enforceable rules in our monorepo.
The Complete List: 97 Things Every Programmer Should Know
This is the full table of contents from the book. It's a goldmine of experience from seasoned practitioners.
- Act with Prudence
- Apply Functional Programming Principles
- Ask, "What Would the User Do?" (You Are Not the User)
- Automate Your Coding Standard
- Beauty Is in Simplicity
- Before You Refactor
- Beware the Share
- The Boy Scout Rule
- Check Your Code First Before Looking to Blame Others
- Choose Your Tools with Care
- Code in the Language of the Domain
- Code Is Design
- Code Layout Matters
- Code Reviews
- Coding with Reason
- A Comment on Comments
- Comment Only What the Code Cannot Say
- Continuous Learning
- Convenience Is Not an -ility
- Deploy Early and Often
- Distinguish Business Exceptions from Technical
- Do Lots of Deliberate Practice
- Domain-Specific Languages
- Don't Be Afraid to Break Things
- Don't Be Cute with Your Test Data
- Don't Ignore That Error!
- Don't Just Learn the Language, Understand Its Culture
- Don't Nail Your Program into the Upright Position
- Don't Rely on "Magic Happens Here"
- Don't Repeat Yourself
- Don't Touch That Code!
- Encapsulate Behavior, Not Just State
- Floating-Point Numbers Aren't Real
- Fulfill Your Ambitions with Open Source
- The Golden Rule of API Design
- The Guru Myth
- Hard Work Does Not Pay Off
- How to Use a Bug Tracker
- Improve Code by Removing It
- Install Me
- Interprocess Communication Affects Application Response Time
- Keep the Build Clean
- Know How to Use Command-Line Tools
- Know Well More Than Two Programming Languages
- Know Your IDE
- Know Your Limits
- Know Your Next Commit
- Large Interconnected Data Belongs to a Database
- Learn Foreign Languages
- Learn to Estimate
- Learn to Say, "Hello, World"
- Let Your Project Speak for Itself
- The Linker Is Not a Magical Program
- The Longevity of Interim Solutions
- Make Interfaces Easy to Use Correctly and Hard to Use Incorrectly
- Make the Invisible More Visible
- Message Passing Leads to Better Scalability in Parallel Systems
- A Message to the Future
- Missing Opportunities for Polymorphism
- News of the Weird: Testers Are Your Friends
- One Binary
- Only the Code Tells the Truth
- Own (and Refactor) the Build
- Pair Program and Feel the Flow
- Prefer Domain-Specific Types to Primitive Types
- Prevent Errors
- The Professional Programmer
- Put Everything Under Version Control
- Put the Mouse Down and Step Away from the Keyboard
- Read Code
- Read the Humanities
- Reinvent the Wheel Often
- Resist the Temptation of the Singleton Pattern
- The Road to Performance Is Littered with Dirty Code Bombs
- Simplicity Comes from Reduction
- The Single Responsibility Principle
- Start from Yes
- Step Back and Automate, Automate, Automate
- Take Advantage of Code Analysis Tools
- Test for Required Behavior, Not Incidental Behavior
- Test Precisely and Concretely
- Test While You Sleep (and over Weekends)
- Testing Is the Engineering Rigor of Software Development
- Thinking in States
- Two Heads Are Often Better Than One
- Two Wrongs Can Make a Right (and Are Difficult to Fix)
- Ubuntu Coding for Your Friends
- The Unix Tools Are Your Friends
- Use the Right Algorithm and Data Structure
- Verbose Logging Will Disturb Your Sleep
- WET Dilutes Performance Bottlenecks
- When Programmers and Testers Collaborate
- Write Code As If You Had to Support It for the Rest of Your Life
- Write Small Functions Using Examples
- Write Tests for People
- You Gotta Care About the Code
- Your Customers Do Not Mean What They Say
Our Implementation: Enforcing Wisdom with Cursor Rules
Talk is cheap. Instead of just nodding along, we've codified the most critical principles as always-on Cursor rules. These aren't suggestions; they are part of our development environment.
Our Enforceable "97 Things" Ruleset:
- #8 The Boy Scout Rule:
boy-scout-rule- Use Case: Prevents the slow decay of code quality. Every commit must leave the code slightly better. This rule stops developers from saying "not my problem" and fosters collective ownership.
- #11 Code in the Language of the Domain:
code-in-domain-language- Use Case: The Mercury backend is filled with terms like
Tournament,Atlas, andDike. This rule forces us to use these terms consistently in our code, making it self-documenting and easier for new developers to understand the business logic without a translator.
- Use Case: The Mercury backend is filled with terms like
- #17 Comment Only What the Code Cannot Say:
comment-only-what-code-cannot-say- Use Case: We've all seen
// increment i by 1. This rule eliminates such noise and pushes for comments that explain the why, not the what. For example, a comment explaining why a specific delay is needed for a third-party API is valuable.
- Use Case: We've all seen
- #26 Don't Ignore That Error!:
dont-ignore-errors- Use Case: Prevents silent failures that cascade into 3 AM production fires. Every
catchblock must be meaningful. This rule would have flagged the empty error handling that allowed the Mercury tournament variant issue to go unnoticed until it hit production.
- Use Case: Prevents silent failures that cascade into 3 AM production fires. Every
- #30 Don't Repeat Yourself (DRY):
dont-repeat-yourself- Use Case: We had similar validation logic for user permissions in both
arcanaandanytracker. A change in one wasn't reflected in the other, causing a security gap. This rule forces us to centralize such logic into shared libraries.
- Use Case: We had similar validation logic for user permissions in both
- #55 Make Interfaces Easy to Use Correctly and Hard to Use Incorrectly:
make-interfaces-hard-to-misuse- Use Case: Instead of passing primitive strings and numbers, we're pushed to create domain-specific types like
TournamentIdorPortfolioId. This leverages the TypeScript compiler to prevent us from passing aPortfolioIdwhere aTournamentIdis expected.
- Use Case: Instead of passing primitive strings and numbers, we're pushed to create domain-specific types like
- #76 The Single Responsibility Principle:
single-responsibility- Use Case: A class or function should do one thing. The original
TournamentConfigFactoryin Mercury was responsible for creating configs, fetching latest versions, and applying experimental logic. This rule forces us to break such god objects into smaller, more maintainable pieces.
- Use Case: A class or function should do one thing. The original
Case Study: The Mercury Tournament Variant Incident
The recent production failure in Mercury's R instance is a perfect case study for why these rules are not academic.
Don't Ignore That Error!was violated: The system that createdTournamentEntityinstances didn't throw an error when thevariantwas missing, leading to a silent failure.Single Responsibility Principlewas violated: TheTournamentConfigFactoryhad too many responsibilities, hiding the fact thatgetLatestConfig()returned a base config without the necessaryexperimentalblock.Make Interfaces Easy to Use Correctly...was violated: Thevariantproperty onTournamentEntitywas optional in the code (TournamentVariant | undefined) butNOT NULLin the database. A stricter interface would have caught this at compile time.Code in the Language of the Domainwas violated: The inconsistent use of "config", "variant", and "experimental" created confusion about what was truly required to launch a tournament.
Had these rules been in place and enforced, this entire class of problem would have been impossible.
The Unenforceable (But Equally Important) Lessons
Some principles can't be linted. They are about mindset and team culture.
- #18 Continuous Learning: Our tech stack (NestJS, FastAPI, React, BullMQ, etc.) is constantly evolving. Staying current is not optional.
- #64 Pair Program and Feel the Flow: For complex domains like Mercury's risk engine (
Dike), two heads are essential to avoid mistakes. - #70 Read Code: Before starting any new feature in a domain, we expect developers to read the existing code and its
FDD.mdto understand the context.
Conclusion
The "97 Things" book is more than a collection of tips; it's a blueprint for professional software development. By embedding its core lessons directly into our workflow as enforceable Cursor rules, we are moving from "knowing" to "doing". It's a pragmatic approach to ensuring that the wisdom of the past directly prevents the failures of the future.
