Skip to content

Essential Coding Philosophies & Principles

SOLID Principles

The foundation of object-oriented design, ensuring maintainable and scalable code.

Single Responsibility Principle (SRP)

Each class or module should have one, and only one, reason to change. A class should do one thing well.

Example: A User class should handle user data, not also handle email sending or database operations.

Benefits: Easier testing, reduced coupling, clearer purpose

Open/Closed Principle (OCP)

Software entities should be open for extension but closed for modification. Add new functionality without changing existing code.

Implementation: Use inheritance, interfaces, or composition patterns

Benefits: Reduces risk of breaking existing functionality, promotes code reuse

Liskov Substitution Principle (LSP)

Objects of a superclass should be replaceable with objects of its subclasses without breaking the application. Subtypes must honor the contract of their base types.

Example: If Bird has a fly() method, Penguin shouldn't inherit from Bird since penguins can't fly.

Benefits: Ensures proper inheritance hierarchies, prevents unexpected behaviors

Interface Segregation Principle (ISP)

Clients should not be forced to depend on interfaces they don't use. Many small, specific interfaces are better than one large, general-purpose interface.

Example: Instead of one IWorker interface with work(), eat(), sleep(), create separate IWorkable, IFeedable, IRestable interfaces.

Benefits: Reduces dependencies, increases flexibility, clearer contracts

Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details; details should depend on abstractions.

Implementation: Use dependency injection, depend on interfaces rather than concrete implementations

Benefits: Loose coupling, easier testing with mocks, more flexible architecture


Core Design Principles

DRY (Don't Repeat Yourself)

Every piece of knowledge should have a single, unambiguous representation in the system. Avoid code duplication by abstracting common functionality.

How to apply: - Extract repeated code into functions or classes - Use inheritance or composition for shared behavior - Create utility libraries for common operations - Use configuration files for repeated values

Warning: Don't over-DRY. Sometimes a little duplication is better than the wrong abstraction.

KISS (Keep It Simple, Stupid)

Simplicity should be a key design goal. Most systems work best if kept simple rather than made complex.

Guidelines: - Write code that's easy to understand, not just clever - Avoid premature optimization - Use straightforward algorithms unless complexity is justified - Prefer clear naming over comments - Break complex problems into simpler sub-problems

Remember: Simple doesn't mean simplistic or feature-poor; it means elegant and understandable.

YAGNI (You Aren't Gonna Need It)

Don't implement functionality until it's actually needed. Avoid building features based on speculation about future requirements.

Why it matters: - Reduces code bloat and maintenance burden - Saves development time - Prevents over-engineering - Requirements often change anyway

When to ignore: Infrastructure decisions that would be expensive to change later (e.g., database choice, major architectural patterns).


Development Methodologies

Test-Driven Development (TDD)

Write tests before writing the actual code. Follow the Red-Green-Refactor cycle.

Process: 1. Red: Write a failing test for new functionality 2. Green: Write minimal code to make the test pass 3. Refactor: Clean up code while keeping tests green

Benefits: - Better test coverage - Forces you to think about design upfront - Tests serve as documentation - Easier refactoring with confidence - Fewer bugs in production

Behavior-Driven Development (BDD)

Extension of TDD that focuses on the behavior of the application from the end user's perspective.

Key aspects: - Uses natural language descriptions (Given-When-Then) - Bridges communication between developers, testers, and business stakeholders - Tests describe what the system does, not how it does it

Code Review Culture

Systematic examination of code by peers before merging to the main branch.

Best practices: - Keep reviews small (under 400 lines) - Review promptly (within 24 hours) - Focus on logic, not style (use linters for style) - Be respectful and constructive - Ask questions rather than making demands - Explain the "why" behind suggestions

Benefits: Knowledge sharing, bug detection, consistent code quality, team learning


Software Development Approaches

Agile Development

Iterative approach that emphasizes flexibility, collaboration, and customer feedback.

Core values: - Individuals and interactions over processes and tools - Working software over comprehensive documentation - Customer collaboration over contract negotiation - Responding to change over following a plan

Scrum Framework

Agile framework with defined roles, events, and artifacts.

Key components: - Sprints: Fixed time-boxes (1-4 weeks) for development - Roles: Product Owner, Scrum Master, Development Team - Ceremonies: Sprint Planning, Daily Standups, Sprint Review, Retrospective - Artifacts: Product Backlog, Sprint Backlog, Increment

Benefits: Faster delivery, adaptability, transparency, continuous improvement

Kanban

Visual workflow management method focusing on continuous delivery.

Principles: - Visualize work on a board - Limit work in progress (WIP) - Manage flow - Make policies explicit - Continuous improvement


Clean Code Principles

Guidelines for writing readable, maintainable code (from Robert C. Martin's "Clean Code").

Meaningful Names

  • Use intention-revealing names
  • Avoid disinformation
  • Make meaningful distinctions
  • Use pronounceable and searchable names
  • Avoid mental mapping

Example:

Bad:  int d; // elapsed time in days
Good: int elapsedTimeInDays;

Functions

  • Should be small (typically under 20 lines)
  • Do one thing well
  • Use descriptive names
  • Prefer fewer arguments (0-2 ideal, 3+ questionable)
  • No side effects
  • Command-Query Separation

Comments

  • Code should be self-explanatory
  • Use comments for "why," not "what"
  • Avoid redundant comments
  • Explain intent and rationale
  • TODO comments for temporary solutions

Error Handling

  • Use exceptions rather than return codes
  • Provide context with exceptions
  • Don't return null (use Optional/Maybe patterns)
  • Don't pass null

Formatting

  • Consistent indentation and spacing
  • Vertical openness (separate concepts with blank lines)
  • Related code should be vertically close
  • Keep files and classes small

Additional Important Principles

Separation of Concerns (SoC)

Divide a program into distinct sections, each addressing a separate concern. Related to Single Responsibility but applied at a higher level (modules, layers, systems).

Examples: MVC pattern, microservices architecture, layered architecture

Law of Demeter (Principle of Least Knowledge)

A module should not know about the internal workings of the objects it manipulates. Only talk to immediate friends.

Rule: An object's method should only call methods on: - The object itself - Objects passed as parameters - Objects it creates - Its direct properties

Composition Over Inheritance

Favor object composition over class inheritance to achieve code reuse and flexibility.

Why: Inheritance creates tight coupling and can lead to fragile base class problems. Composition is more flexible.

Fail Fast

Detect and report errors as early as possible. Validate inputs immediately, throw exceptions early, use assertions.

Benefits: Easier debugging, prevents error propagation, clearer error messages

Convention Over Configuration

Provide sensible defaults and follow common conventions to reduce the number of decisions developers need to make.

Examples: Ruby on Rails, Spring Boot

Principle of Least Astonishment (POLA) / Principle of Least Surprise (POLS)

A component should behave in a way that most users expect it to behave, minimizing surprise and confusion.

Core idea: Design should match the user's experience, expectations, and mental models. Leverage existing knowledge to minimize the learning curve.

Guidelines: - Function/method names should intuitively match their behavior - Follow platform conventions (keyboard shortcuts, UI patterns) - Apply sensible defaults - Borrow from functionally similar programs users are familiar with - When behavior is ambiguous, choose what will least surprise the user

Examples: - F1 key opens help in most applications - API methods named getUserById() should return a user, not delete one - JavaScript's parseInt("08") originally parsed as octal (base 8), violating POLA—fixed in later versions - Keyboard shortcuts like ? for help on websites (Gmail, YouTube)

Benefits: Reduced learning curve, fewer bugs from misunderstanding, better user experience, intuitive interfaces

Premature Optimization is the Root of All Evil

Don't optimize code before you have evidence it's a bottleneck. Write clear code first, then profile and optimize only what matters.

Process: Make it work → Make it right → Make it fast


Practical Application Tips

  1. Start small: Don't try to apply all principles at once
  2. Context matters: Not every principle applies to every situation
  3. Balance: Sometimes principles conflict; use judgment
  4. Refactor regularly: Apply principles when touching existing code
  5. Team alignment: Discuss and agree on which principles to emphasize
  6. Continuous learning: These principles evolve with experience
  7. Pragmatism over dogmatism: Break rules when you have good reasons

Remember: These principles are guidelines, not laws. The goal is writing maintainable, understandable, and robust software that delivers value.