Skip to main content
This lesson shows an automated SOLID principles audit run against an Express.js application (Express Login Demo). The audit identifies common anti‑patterns (monolithic handlers, hardcoded dependencies, fat req/res usage, direct instantiation of low‑level resources, etc.) and produces a structured report with severity ratings and concrete remediation snippets you can drop into the codebase. Below are the command context used while running the audit, trimmed audit prompt, a concise overview of the SOLID principles, a summary of how the audit checks each principle, an executive summary of the audit results, and targeted, code-level remediation examples that make the fixes incremental and testable. Command context shown while running the audit:
Audit prompt (trimmed to essentials used for the SOLID check):

Overview — SOLID principles (concise)

How the audit checks each principle (summary)

  • SRP: Detects functions or files doing multiple distinct tasks (validation, DB access, encryption, token creation, response shaping).
  • OCP: Flags switch/if chains and centralized conditionals that must change to add features.
  • LSP: Looks for concrete new/require usage inside higher-level modules (routes/services), blocking substitutability.
  • ISP: Finds handlers that accept whole Express req/res objects while using only a subset of fields.
  • DIP: Scans for direct instantiation and process.env use inside business logic rather than injected config/abstractions.

Executive summary

  • Application: Express Login Demo
  • Overall SOLID Compliance Score: 4/10
  • Main issues found:
    • Monolithic route handlers that combine validation, DB calls, crypto, and token creation.
    • Hardcoded dependencies: DB client, crypto libs, JWT usage, and process.env reads embedded in logic.
    • Lack of clear abstraction layers (repository pattern, service interfaces).
    • Fat req/res usage making handlers tightly coupled to Express.
  • Priority remediation: Extract auth service, introduce repository abstractions, centralize configuration, and create pluggable auth strategies.

Detailed findings and concrete remediation

  1. Single Responsibility Principle (SRP)
  • Violation: Monolithic auth route handler performing validation, DB access, password checks, JWT generation, and response formatting.
  • Location: routes/auth.js (e.g., /login route, lines ~17–96)
  • Severity: HIGH (9/10)
  • Why: Multiple concerns are combined into a single function, increasing fragility and reducing testability.
Current pattern (anti-pattern):
Remediation — split responsibilities:
Thin route using injected service:
  1. Open/Closed Principle (OCP)
  • Violation: Error handling via switch statements or centralized if chains that require editing core logic to add new error types.
  • Severity: MEDIUM–HIGH (7/10)
Current anti-pattern:
Remediation — handler map/strategy:
To extend handling, add a new key to errorHandlers instead of editing control flow.
  1. Liskov Substitution Principle (LSP)
  • Violation: Direct instantiation of DB pool inside route modules prevents substituting mocks or alternative implementations.
  • Location example: routes/auth.js contains const pool = require('../config/database');
  • Severity: MEDIUM (6/10)
Remediation — inject a repository abstraction: Repository interface (documentation-style class):
Concrete implementation:
Inject PostgresUserRepository in production; tests can inject a MockUserRepository.
  1. Interface Segregation Principle (ISP)
  • Violation: Route handlers depend on full Express req/res objects, though only a few fields are used.
  • Severity: MEDIUM (6/10)
Remediation — pass minimal inputs and a response handler:
Usage in route:
  1. Dependency Inversion Principle (DIP)
  • Violation: High-level auth logic depends on low-level modules (DB pool, bcrypt, jsonwebtoken, process.env), making swapping implementations or mocking in tests difficult.
  • Location examples: routes/auth.js importing DB connection; services directly requiring bcrypt or jsonwebtoken; business logic reading process.env.JWT_SECRET.
  • Severity: CRITICAL (10/10) for direct DB dependency; HIGH for env/crypto dependency.
Remediation — extract and inject abstractions: Config abstraction:
JWT service abstraction:
Crypto abstraction:
Inject these dependencies into authService or controllers rather than requiring them directly inside business logic. Example controller wiring:

Priority remediation plan (phased, practical)

Code quality metrics observed (examples)

  • Cyclomatic complexity: High (login function had 8+ decision points).
  • Lines per function: Large (login function ~79 lines).
  • Coupling: High (routes directly require DB, crypto, jwt).
  • Testability: Poor (no DI; direct env and DB usage).
Run the SOLID audit early and often. Make controllers/routes thin and move logic into services that accept injected collaborators (repositories, crypto, jwt, config). This maximizes testability and extensibility while yielding quick return on refactoring efforts.
Related artifacts and resources
  • The audit tool generated a markdown report (example above) and suggested an architecture:
    • controllers/authController.js (thin routing layer)
    • services/authService.js, jwtService.js, configService.js
    • repositories/userRepository.js (+ concrete DB implementations)
    • strategies/authStrategies.js
    • middleware/validation.js, middleware/errorHandler.js
A dark‑mode screenshot of a GitHub repository page showing a list of markdown files for security and code‑review topics (e.g., input validation, database and session cookie security). The right side shows repository details and metadata.
Where to get the prompts used for these audits:
Next steps for your project (practical checklist)
  • Extract login logic into an authService and inject userRepository and jwtService.
  • Replace direct process.env reads with a configService that can be overridden in tests.
  • Replace require/new of DB/crypto in routes with injected abstractions.
  • Introduce small utility modules (jwtService, cryptoService, errorHandlers) and wire them centrally in a startup file.
Final assessment (concise)
  • The codebase has a working authentication flow but demonstrates strong coupling and several SOLID violations. Start with dependency inversion and single responsibility refactors: extract services, use repository abstractions, and centralize configuration. Those changes will immediately improve testability, reduce coupling, and make the codebase easier to extend without modifying core logic.

Watch Video