Apply meta-principles of software craftsmanship: DRY, orthogonality, tracer bullets, and design by contract. Use when the user mentions "best practices", "pragmatic approach", "broken windows", "tracer bullet", or "software craftsmanship". Covers estimation, domain languages, and reversibility. For code-level quality, see clean-code. For refactoring techniques, see refactoring-patterns.
From programming-skillsnpx claudepluginhub wesleyegberto/software-engineering-skills --plugin programming-skillsThis skill uses the workspace's default tool permissions.
references/broken-windows.mdreferences/contracts-assertions.mdreferences/dry-orthogonality.mdreferences/estimation-portfolio.mdreferences/reversibility.mdreferences/tracer-bullets.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides implementation of event-driven hooks in Claude Code plugins using prompt-based validation and bash commands for PreToolUse, Stop, and session events.
A systems-level approach to software craftsmanship from Hunt & Thomas' "The Pragmatic Programmer" (20th Anniversary Edition). Apply these principles when designing systems, reviewing architecture, writing code, or advising on engineering culture. This framework addresses the meta-level: how to think about software, not just how to write it.
Care about your craft. Software development is a craft that demands continuous learning, disciplined practice, and personal responsibility. Pragmatic programmers think beyond the immediate problem -- they consider context, trade-offs, and long-term consequences of every technical decision.
The foundation: Great software comes from great habits. A pragmatic programmer maintains a broad knowledge portfolio, communicates clearly, avoids duplication ruthlessly, keeps components orthogonal, and treats every line of code as a living asset that must earn its place. The goal is not perfection -- it is building systems that are easy to change, easy to understand, and easy to trust.
Goal: 10/10. When reviewing or creating software designs, architecture, or code, rate it 0-10 based on adherence to the principles below. A 10/10 means full alignment with all guidelines; lower scores indicate gaps to address. Always provide the current score and specific improvements needed to reach 10/10.
Seven meta-principles for building software that lasts:
Core concept: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. DRY is about knowledge, not code -- duplicated logic, business rules, or configuration are far more dangerous than duplicated syntax.
Why it works: When knowledge is duplicated, changes must be made in multiple places. Eventually one gets missed, introducing inconsistency. DRY reduces the surface area for bugs and makes systems easier to change.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Config values | Single source of truth | Define DB connection in one env file, reference everywhere |
| Validation rules | Shared schema | Use JSON Schema or Zod schema for both client and server validation |
| API contracts | Generate from spec | OpenAPI spec generates types, docs, and client code |
| Business logic | Domain module | Tax calculation in one module, not scattered across controllers |
| Database schema | Migration-driven | Schema defined in migrations, ORM models generated from DB |
See: references/dry-orthogonality.md
Core concept: Two components are orthogonal if changes in one do not affect the other. Design systems where components are self-contained, independent, and have a single, well-defined purpose.
Why it works: Orthogonal systems are easier to test, easier to change, and produce fewer side effects. When you change the database layer, the UI should not break. When you change the auth provider, the business logic should not care.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Architecture | Layered separation | Controller -> Service -> Repository, each replaceable |
| Dependencies | Dependency injection | Pass a Notifier interface, not a SlackClient concrete class |
| Testing | Isolated unit tests | Test business logic without database, network, or filesystem |
| Configuration | Environment-driven | Feature flags in config, not if branches in business logic |
| Deployment | Independent services | Deploy auth service without redeploying payment service |
See: references/dry-orthogonality.md
Core concept: Tracer bullets are end-to-end implementations that connect all layers of the system with minimal functionality. Unlike prototypes (which are throwaway), tracer bullet code is production code -- thin but real.
Why it works: Tracer bullets give immediate feedback. You see what the system looks like end-to-end before investing in filling out every feature. Users can see something real, developers have a framework to build on, and integration issues surface early.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| New project | Vertical slice | Build one feature end-to-end: button -> API -> DB -> response |
| Uncertain tech | Spike prototype | Test if WebSocket performance is sufficient before committing |
| Framework eval | Tracer through stack | Build login flow through the full framework before choosing it |
| Microservice | Walking skeleton | Deploy a hello-world service through the full CI/CD pipeline |
| Data pipeline | End-to-end flow | One record from ingestion through transformation to output |
See: references/tracer-bullets.md
Core concept: Define and enforce the rights and responsibilities of software modules through preconditions (what must be true before), postconditions (what is guaranteed after), and class invariants (what is always true). When a contract is violated, fail immediately and loudly.
Why it works: Contracts make assumptions explicit. Instead of silently corrupting data or limping along in an invalid state, the system crashes at the point of the problem -- making bugs visible and traceable. Dead programs tell no lies.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Function entry | Precondition guard | assert age >= 0, "Age cannot be negative" at function start |
| Function exit | Postcondition check | Verify returned list is sorted before returning |
| Class state | Invariant validation | validate! method called after every state mutation |
| API boundary | Schema validation | Validate request body against schema before processing |
| Data pipeline | Stage assertions | Assert row count after ETL transform matches expectation |
See: references/contracts-assertions.md
Core concept: One broken window -- a badly designed piece of code, a poor management decision, a hack that "we'll fix later" -- starts the rot. Once a system shows neglect, entropy accelerates and discipline collapses.
Why it works: Psychology. When code is clean and well-maintained, developers feel social pressure to keep it that way. When code is already messy, the threshold for adding more mess drops to zero. Quality is a team habit, not an individual heroic effort.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Legacy code | Board up windows | Wrap bad code in a clean interface before adding features |
| Code review | Zero-tolerance for new debt | Reject PRs that add // TODO: fix later without a ticket |
| Tech debt | Debt budget | Allocate 20% of each sprint to fixing broken windows |
| New team member | Clean onboarding path | First task: fix a broken window to learn the codebase |
| Monitoring | Entropy metrics | Track linting violations, test coverage trends over time |
See: references/broken-windows.md
Core concept: There are no final decisions. Build systems that make it easy to change your mind about databases, frameworks, vendors, architecture, and deployment targets. The cost of change should be proportional to the scope of change.
Why it works: Requirements change. Vendors get acquired. Technologies fall out of favor. If your architecture has hard-coded assumptions about any of these, every change becomes a rewrite. Flexible architecture treats decisions as configuration, not structure.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Database | Repository pattern | Business logic calls repo.save(user), not pg.query(...) |
| External API | Adapter/wrapper | PaymentGateway interface wraps Stripe; swap to Braintree later |
| Feature flags | Runtime toggles | New checkout flow behind a flag, rollback in seconds |
| Architecture | Event-driven decoupling | Services communicate via events, not direct HTTP calls |
| Deployment | Container abstraction | Dockerized app runs on AWS, GCP, or bare metal unchanged |
See: references/reversibility.md
Core concept: Learn to estimate reliably by understanding scope, building models, decomposing into components, and assigning ranges. Manage your learning like a financial portfolio: invest regularly, diversify, and rebalance.
Why it works: Estimation builds trust with stakeholders when done honestly ("1-3 weeks" is better than "2 weeks exactly"). A knowledge portfolio ensures you stay relevant as technologies shift -- the programmer who stops learning stops being effective.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Sprint planning | Range estimates | "3-5 days" with confidence level, not a single number |
| New technology | Time-boxed spike | "I'll spend 2 days evaluating; then I can estimate properly" |
| Large project | Bottom-up decomposition | Break into tasks < 1 day, sum with buffer for integration |
| Learning | Weekly investment | 1 hour/week on a new language, tool, or domain |
| Career growth | Portfolio diversification | Mix of depth (expertise) and breadth (adjacent skills) |
See: references/estimation-portfolio.md
| Mistake | Why It Fails | Fix |
|---|---|---|
| DRY-ing similar-looking code that serves different purposes | Creates coupling between unrelated concepts; changes to one break the other | Only DRY knowledge, not coincidental code similarity |
| Skipping tracer bullets and building layer-by-layer | Integration issues surface late; no end-to-end feedback until the end | Build one thin vertical slice first |
| Ignoring broken windows "because we'll refactor later" | Entropy accelerates; later never comes; team morale drops | Fix immediately or board up with a tracked ticket |
| Estimates as single-point commitments | Creates false precision; erodes trust when missed | Always give ranges with confidence levels |
| Making everything "flexible" upfront | Over-engineering; YAGNI; abstraction without evidence of need | Add flexibility when you have concrete evidence you'll need it |
| Assertions in production removed "for performance" | Bugs that assertions would catch now silently corrupt data | Keep critical assertions; benchmark before removing any |
| Global state "for convenience" | Destroys orthogonality; every module coupled to everything | Use dependency injection and explicit parameters |
| Question | If No | Action |
|---|---|---|
| Can I change the database without touching business logic? | Orthogonality violation | Introduce repository/adapter pattern |
| Do I have an end-to-end slice working? | Missing tracer bullet | Build one vertical slice before expanding |
| Is every business rule defined in exactly one place? | DRY violation | Identify the authoritative source and remove duplicates |
| Would a new developer call this codebase "clean"? | Broken windows present | Schedule a dedicated cleanup sprint |
| Do my estimates include ranges and confidence levels? | Estimation problem | Switch to PERT or range-based estimates |
| Can I roll back this deployment in under 5 minutes? | Reversibility gap | Add feature flags and blue-green deploys |
| Am I learning something new every week? | Knowledge portfolio stagnant | Schedule weekly learning time and track it |
Andrew Hunt is a programmer, author, and publisher. He co-founded the Pragmatic Bookshelf and was one of the 17 original authors of the Agile Manifesto. His work focuses on the human side of software development -- how teams learn, communicate, and maintain quality over time.
David Thomas is a programmer and author who co-founded the Pragmatic Bookshelf. He coined the term "DRY" (Don't Repeat Yourself) and "Code Kata." A pioneer in Ruby adoption outside Japan, he co-authored "Programming Ruby" (the Pickaxe book) and has spent decades advocating for developer pragmatism over dogma.