There are many software design & development principles that act as “mental guardrails” for writing clean, maintainable code. Here’s a list, grouped by topic.
1. Code Quality & Simplicity Principles
Principle | Definition | Java Example |
KISS – Keep It Simple, Stupid | Keep code straightforward and avoid unnecessary complexity. | Bad Practice: if(user != null && user.isActive() && user.hasPermission("ADMIN")) {} Good Practice: if(isAdmin(user)) {} |
DRY – Don’t Repeat Yourself | Avoid duplicating logic. Extract common logic to methods or utilities. | Bad Practice: double d1 = price - (price * 0.1); double d2 = price - (price * 0.1); Good Practice: double applyDiscount(double price) { return price - (price * 0.1); } |
YAGNI – You Aren’t Gonna Need It | Don’t build features you don’t need yet. | ❌ Adding caching logic on day 1 without confirmed performance need. |
SLAP – Single Level of Abstraction | A method should operate at one abstraction level. | Bad Practice: void processOrder() { fetchFromDB(); calculateDiscount(); sendEmail(); } Good Practice: void processOrder() { getOrder(); applyDiscount(); notifyCustomer(); } |
Boy Scout Rule | Leave the code better than you found it. | Refactor a messy method when fixing a bug. |
2. Object-Oriented Design Principles (SOLID)
Principle | Definition | Java Example |
S – Single Responsibility | A class should have one reason to change. | Bad Practice: class InvoiceService { void createInvoice(){} void sendEmail(){} } Good Practice: class InvoiceService { void createInvoice(){} } class EmailService { void sendEmail(){} } |
O – Open/Closed | Open for extension, closed for modification. | interface Payment { void pay(double amount); } class CreditCardPayment implements Payment { public void pay(double a) {} } class UpiPayment implements Payment { public void pay(double a) {} }
// Add PayPalPayment without changing old code |
L – Liskov Substitution | Subclasses should be replaceable by base class. | interface Bird {} interface FlyingBird extends Bird { void fly(); } class Sparrow implements FlyingBird { public void fly() {} } class Ostrich implements Bird {} |
I – Interface Segregation | Many small interfaces over one large one. | Bad Practice: interface Machine { print(); scan(); fax(); } Good Practice: interface Printer { void print(); } interface Scanner { void scan(); } |
D – Dependency Inversion | Depend on abstractions, not concretions. | interface MessageService { void send(String msg); } class EmailService implements MessageService { public void send(String msg) {} } |
3. Architecture & Maintainability Principles
Definition | Java Example | |
SoC – Separation of Concerns | Different layers handle different concerns. | @Controller class UserController { @Autowired UserService service; } @Service class UserService { @Autowired UserRepository repo; } |
High Cohesion, Low Coupling | Modules should be focused and independent. | Logging module works without depending on authentication. |
Law of Demeter | Only talk to direct friends. | Bad Practice: order.getCustomer().getAddress().getCity(); Good Practice: order.getCustomerCity(); |
Inversion of Control (IoC) | Framework controls object creation. | Spring creates beans & injects dependencies automatically. |
CQRS | Separate read and write models. | OrderQueryService for reads, OrderCommandService for writes. |
4. Testing & Reliability Principles
Principle | Definition | Java Example |
First-class Tests | Tests are as important as production code. | Keep JUnit tests in /src/test/java. |
Test Pyramid | More unit tests than UI tests. | 70% unit, 20% integration, 10% Selenium tests. |
Fail Fast | Detect errors early. | if(user == null) throw new IllegalArgumentException("User required"); |
AAA – Arrange, Act, Assert | Clear test structure. | // Arrange User u = new User("John"); // Act u.activate();
// Assert assertTrue(u.isActive()); |
5. Agile/Lean Development Principles
Principle | Definition | Java Example |
Iterate Quickly | Deliver small increments. | Ship a basic Spring Boot REST API before adding caching/security. |
Build-Measure-Learn | Release → Collect metrics → Improve. | Deploy MVP, check user API usage, optimize endpoints. |
CI/CD | Auto-build & deploy after merges. | GitHub Actions running mvn test & deploy to AWS. |
Limit WIP | Reduce active tasks. | Developer works on only 1–2 Jira issues at a time. |
6. Performance & Resource Management Principles
Principle | Definition | Java Example |
Optimize Last | Make it work before making it fast. | Write a working algorithm before parallelizing it. |
Caching | Store expensive results. | Use Spring Cache with Redis for repeated DB queries. |
Bulkhead Pattern | Isolate failures. | Separate DB connections for high-priority vs. low-priority tasks. |
7. Security & Reliability Principles
Principle | Definition | Java Example |
Least Privilege | Minimal permissions. | DB user with read/write but no DROP privilege. |
Defense in Depth | Multiple security layers. | Input validation + Spring Security + HTTPS. |
Fail Securely | Deny access if unsure. | Default to “access denied” if JWT verification fails. |
Master Reference
Principle | Category | Definition | Example | Benefits | When to Use |
KISS (Keep It Simple, Stupid) | Code Quality | Keep design/code simple, avoid overengineering. | Replace deeply nested if-else with strategy pattern. | Easy maintenance, fewer bugs. | When writing any new feature or refactoring. |
DRY (Don’t Repeat Yourself) | Code Quality | Avoid code duplication; reuse logic. | Move repeated DB queries into DAO method. | Consistency, easier updates. | When seeing repeated code blocks. |
YAGNI (You Aren’t Gonna Need It) | Code Quality | Don’t build for hypothetical needs. | Skip creating unused service layers. | Saves time, prevents unused code. | In early dev phases, avoid premature features. |
SOC (Separation of Concerns) | Code Quality | Separate functionalities into distinct layers. | MVC keeps UI, logic, and DB separate. | Easier debugging, testing. | In any layered or modular system. |
SLAP (Single Level of Abstraction Principle) | Code Quality | Each method should operate at one abstraction level. | Business logic method shouldn’t do DB connections. | Clarity, maintainability. | In method design and refactoring. |
SRP (Single Responsibility Principle) | OOP/SOLID | A class has only one reason to change. | UserService handles user ops, not emails. | Modular, easier updates. | Class-level design. |
OCP (Open/Closed Principle) | OOP/SOLID | Open for extension, closed for modification. | Add payment type via new class, not modifying core. | Extensible, avoids breaking code. | Feature expansion without altering core. |
LSP (Liskov Substitution Principle) | OOP/SOLID | Subclasses must be usable as their base type. | Avoid Penguin extends Bird if fly() is broken. | Correct inheritance. | Designing inheritance hierarchies. |
ISP (Interface Segregation Principle) | OOP/SOLID | Smaller, specific interfaces preferred. | Split Animal into Flyable, Swimmable. | Reduces unused methods. | Interface design. |
DIP (Dependency Inversion Principle) | OOP/SOLID | Depend on abstractions, not concretions. | Use PaymentProcessor interface, inject via Spring. | Flexibility, testability. | For dependency management. |
High Cohesion | Architecture | Related code stays together in modules. | Microservice per domain. | Easier changes, better focus. | Modular design. |
Low Coupling | Architecture | Minimal dependency between modules. | API-based service calls. | Reduces ripple effects. | Large-scale systems. |
Encapsulation | Architecture/OOP | Hide implementation, expose only needed APIs. | Private fields, public getters. | Prevents misuse, easier updates. | Data-sensitive classes. |
Design to Interfaces | Architecture | Code against interfaces for flexibility. | Use List over ArrayList type ref. | Easy swapping implementations. | Library/framework integration. |
Event-Driven Architecture | Architecture | Use events instead of direct calls. | Kafka-based microservices. | Decoupling, async handling. | Scalable/distributed systems. |
Fail-Fast | Reliability | Detect and stop early when errors occur. | Validation before DB save. | Early bug detection. | In data validation, pipelines. |
TDD First Law | Testing | Write tests before code. | JUnit before service logic. | Better design, fewer bugs. | For critical logic. |
Testing Pyramid | Testing Strategy | Unit > Integration > UI tests. | 70-20-10 split. | Fast feedback, stable tests. | Test planning. |
Automate Repetitive Tests | QA Automation | Replace manual runs with automation. | Selenium for UI regression. | Saves time, consistent results. | Regression-heavy projects. |
Mock & Stub Isolation | Testing | Simulate dependencies for testing. | Mockito for DB/service calls. | Predictable tests. | Unit testing with dependencies. |
Iterative Development | Agile | Deliver in small increments. | 2-week sprints. | Continuous feedback. | Any Agile environment. |
Continuous Integration (CI) | DevOps | Merge often with automated builds/tests. | Jenkins with Git hooks. | Early detection of errors. | Multi-dev teams. |
Continuous Delivery (CD) | DevOps | Keep code deployable anytime. | Auto-deploy to staging after tests. | Faster releases. | Production-ready projects. |
Feedback Loops | Agile | Short cycles between feedback & change. | Client review per sprint. | Customer satisfaction. | Agile teams. |
Least Privilege Principle | Security | Grant minimal required permissions. | Read-only DB user. | Reduced attack surface. | In security-sensitive systems. |
Fail-Secure Defaults | Security | Deny by default, allow explicitly. | Auth required on all APIs. | Stronger security. | Security-first apps. |
Defensive Programming | Reliability | Assume failures, guard against them. | Null checks, input validation. | Prevents crashes. | Public APIs, critical systems. |
Idempotency | Reliability | Same request multiple times = same result. | Payment API prevents double charge. | Safe retries. | APIs, distributed systems. |