- Separate concerns into layers (presentation, business logic, data access) — each layer should depend only on the layer below it.
- Design for testability: inject dependencies, avoid global state, use interfaces for external services.
- Use dependency injection to decouple components — pass dependencies through constructors or function parameters, not global imports.
- Keep modules loosely coupled with well-defined interfaces between them.
- Use dependency injection for external services and cross-cutting concerns.
- Prefer composition over inheritance for flexible, testable designs.