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
- •Itself (
- •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 examples - •
references/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.