Universal Best Practices
This skill provides comprehensive guidance on software engineering best practices that apply across all programming languages and project types. Follow these principles to ensure clean, maintainable, production-ready code.
Core Workflow
When writing or reviewing code:
- Start with KISS and YAGNI - implement the simplest thing that solves the current need
- Apply SOLID principles to structure your classes and modules
- Ensure DRY by eliminating duplication
- Use SoC to separate concerns into distinct responsibilities
- Follow LoD to minimize coupling between components
- Apply remaining principles as relevant to your specific context
Fundamental Principles
SOLID
Five core object-oriented design principles that make code maintainable and flexible:
S - Single Responsibility Principle (SRP)
- Each class/module should have only ONE reason to change
- One class = one job or responsibility
- Example: Separate
UserRepository(data access) fromUserValidator(validation logic) - Benefit: Changes to one responsibility don't affect unrelated code
O - Open/Closed Principle (OCP)
- Open for extension, closed for modification
- Add new functionality without changing existing code
- Use interfaces, abstract classes, or inheritance to extend behavior
- Example: Plugin systems, strategy patterns
- Benefit: Add features without risking existing functionality
L - Liskov Substitution Principle (LSP)
- Subtypes must be substitutable for their base types
- Child classes shouldn't break parent class contracts
- Example: If
Birdhasfly(), don't createPenguin extends Bird- penguins can't fly - Benefit: Polymorphism works correctly, no surprises
I - Interface Segregation Principle (ISP)
- No class should implement methods it doesn't use
- Many specific interfaces > one general interface
- Example: Split
IWorkerintoIWorkableandIEatableinstead of forcing robots to implementeat() - Benefit: Lean interfaces, no unnecessary dependencies
D - Dependency Inversion Principle (DIP)
- Depend on abstractions, not concrete implementations
- High-level modules shouldn't depend on low-level modules
- Both should depend on abstractions
- Example:
PaymentProcessordepends onIPaymentGatewayinterface, not concreteStripeGateway - Benefit: Loose coupling, easy to swap implementations
DRY (Don't Repeat Yourself)
- Every piece of knowledge has ONE authoritative representation
- Avoid duplicating logic, even if written differently
- Example: Extract common tax calculation logic into single function instead of repeating across products
- Benefit: Changes happen in one place, consistency guaranteed
KISS (Keep It Simple, Stupid)
- Prefer the simplest solution that works
- Avoid unnecessary complexity and cleverness
- Simple code is easier to understand, maintain, and debug
- Example: Use straightforward conditionals over complex nested ternaries
- Benefit: Code is readable and less error-prone
YAGNI (You Aren't Gonna Need It)
- Don't build features you don't need right now
- No speculative functionality for hypothetical future needs
- Focus on current requirements
- Example: Don't build a recommendation engine if you only need product listing
- Benefit: Less code to maintain, faster delivery
Architectural Principles
SoC (Separation of Concerns)
- Divide code into distinct sections, each addressing a separate concern
- Each module focuses on ONE aspect of functionality
- Example: Separate data layer, business logic, and presentation
- Benefit: Changes to one concern don't affect others
SSOT (Single Source of Truth)
- Every data element is mastered in only ONE place
- All other references point to or derive from this source
- Example: User profile data lives in one database table, not duplicated across systems
- Benefit: No data inconsistency, one place to update
LoD (Law of Demeter) - Principle of Least Knowledge
- Object should only talk to immediate friends
- Don't access nested objects:
a.getB().getC().doSomething()violates LoD - Instead:
a.doSomethingWithC()where A delegates internally - Rule: Method can only call methods on:
- Itself (this) - Its parameters - Objects it creates - Its direct properties
- Benefit: Reduced coupling, easier refactoring
CQS (Command Query Separation)
- Methods should either:
- Command: Change state (return void) - Query: Return data (don't change state)
- Never both in same method
- Example:
getBalance()reads only,withdraw()changes only - Exception: Sometimes
pop()orincrementAndGet()violates this for practical reasons - Benefit: Predictable behavior, easier reasoning
DbC (Design by Contract)
- Define explicit preconditions, postconditions, and invariants
- Preconditions: What must be true before method runs
- Postconditions: What must be true after method completes
- Invariants: What must always be true for the object
- Example:
withdraw(amount)requiresamount > 0(precondition) andbalance >= amount(precondition), ensuresnew_balance = old_balance - amount(postcondition) - Benefit: Clear contracts, fail-fast validation
Operational Principles
ETC (Easier to Change)
- Optimize for change cost, not cleverness
- Ask: "Will this be easy to modify later?"
- Prefer composition over inheritance
- Keep coupling loose, cohesion high
- Benefit: Code adapts to evolving requirements
PoLP (Principle of Least Privilege)
- Grant minimum permissions needed for the task
- Processes run with minimal privileges
- Functions access only what they need
- Example: Read-only database connection for queries, no write access
- Benefit: Reduced attack surface, limited blast radius
CoC (Convention over Configuration)
- Use sensible defaults over explicit configuration
- Follow standard conventions to reduce decisions
- Only configure when deviating from convention
- Example: Rails assumes
Userclass maps touserstable - no config needed - Benefit: Less boilerplate, faster development, easier onboarding
Idempotency
- Operation produces same result whether executed once or multiple times
- Safe to retry without side effects
- Critical for distributed systems and APIs
- Example:
PUT /users/123with same data always results in same user state - Implementation: Use idempotency keys, request IDs, or natural idempotency
- Benefit: Safe retries, fault tolerance, consistency
POLA (Principle of Least Astonishment)
- System behavior should match user expectations
- Code does what it looks like it does
- No surprises or unexpected behavior
- Example:
delete()should delete, not archive - Benefit: Intuitive code, fewer bugs from misunderstanding
Principle Application Priority
When multiple principles conflict:
- Safety first: PoLP, DbC preconditions
- Simplicity: KISS, YAGNI
- Maintainability: DRY, SoC, SRP
- Flexibility: OCP, DIP, ETC
- Consistency: POLA, CoC
Common Anti-Patterns to Avoid
- Premature optimization: Violates YAGNI and KISS
- God classes: Violates SRP and SoC
- Deep nesting: Violates LoD
- Copy-paste code: Violates DRY
- Magic numbers/strings: Violates SSOT and maintainability
- Mixing commands and queries: Violates CQS
- Over-engineering: Violates KISS and YAGNI
Quick Reference
For detailed examples and language-specific implementations, see:
references/solid-examples.md- SOLID principle examplesreferences/patterns-reference.md- Design pattern applications of principles
When Principles Conflict
DRY vs KISS: If abstraction becomes complex, duplicate similar code YAGNI vs Future-proofing: Build for today, refactor when tomorrow arrives LoD vs Performance: Sometimes direct access is needed - document why CQS vs Pragmatism: Database pop() operations can violate CQS when needed
Remember: Principles are guidelines, not laws. Apply with judgment based on context.