> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SOLID Principles Evaluation

> Automated SOLID principles audit of an Express.js login app identifying anti patterns and offering severity rated findings with concrete code level remediation snippets

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:

```text theme={null}
cwd: /Users/jeremy/Repos/[Claude Code for Beginners](https://learn.kodekloud.com/user/courses/claude-code-for-beginners)/Express-login-demo

What's new:
• Fixed issue causing "OAuth authentication is currently not supported"
• Status line input now includes `exceeds_200k_tokens`
• Fixed incorrect usage tracking in /cost.
• Introduced `ANTHROPIC_DEFAULT_SONNET_MODEL` and `ANTHROPIC_DEFAULT_OPUS_MODEL` for controlling model aliases opusplan, opus, and sonnet.
• Bedrock: Updated default Sonnet model to Sonnet 4

> Try `how does <filepath> work?`

? for shortcuts
```

Audit prompt (trimmed to essentials used for the SOLID check):

```text theme={null}
4. INTERFACE SEGREGATION
– Are interfaces too large?
– Do clients depend on methods they don't use?

5. DEPENDENCY INVERSION
– Are we depending on abstractions or concretions?
– Is there proper dependency injection?
– Check for 'new' keyword usage vs injection

Rate SOLID compliance (1–10) with specific violations and fixes.

## Provide:

A structured finding report
A scale of 1/10 on how important each finding is
Remediation: precise code-level fix or config change (snippets welcome)

## Constraints & style:

Be concrete and cite exact code locations and identifiers.
Prefer minimal, drop-in fix snippets over prose.
Do not invent files or functions that aren't present; if context is missing, mark as Unable to verify and say what code would prove it.
Write this into a markdown file and place it in the audits/ folder.

• I'll evaluate this Express application's adherence to SOLID principles by analyzing the codebase structure and implementation.
```

## Overview — SOLID principles (concise)

| Principle | Short definition | Focus during audit |
| - | -: | - |
| Single Responsibility Principle (SRP) | A module/class should have one reason to change. | Look for functions handling validation, DB access, crypto, token generation, and response formatting together. |
| Open/Closed Principle (OCP) | Open for extension, closed for modification. | Identify switch/if chains and hardcoded conditionals that require changing core logic to add behavior. |
| Liskov Substitution Principle (LSP) | Subtypes should be usable wherever the base type is expected. | Look for direct instantiation that prevents substituting mocks or alternate implementations. |
| Interface Segregation Principle (ISP) | Clients shouldn't depend on methods they don't use. | Check for handlers relying on full `req`/`res` instead of the minimal inputs/outputs. |
| Dependency Inversion Principle (DIP) | High-level modules depend on abstractions, not concretes. | Detect `require`/`new` of low-level modules inside high-level code and direct `process.env` usage. |

## 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):

```javascript theme={null}
// routes/auth.js (current anti-pattern)
router.post('/login', async (req, res) => {
  // 1. Validate req.body
  // 2. Query DB for user
  // 3. Compare password with bcrypt
  // 4. Generate JWT
  // 5. Send response or error
});
```

Remediation — split responsibilities:

```javascript theme={null}
// services/authService.js
const bcrypt = require('bcrypt'); // consider replacing with a crypto abstraction in future
module.exports = function createAuthService(userRepository, jwtService, config) {
  return {
    async authenticate(email, password) {
      const user = await userRepository.findByEmail(email);
      if (!user) return null;
      const match = await bcrypt.compare(password, user.password_hash);
      if (!match) return null;
      return { user, token: jwtService.generateToken({ id: user.id, email: user.email }) };
    }
  };
};
```

Thin route using injected service:

```javascript theme={null}
// routes/auth.js (thin route)
const authService = createAuthService(userRepository, jwtService, config);
router.post('/login', async (req, res, next) => {
  try {
    const result = await authService.authenticate(req.body.email, req.body.password);
    if (!result) return res.status(401).json({ error: 'Invalid credentials' });
    res.json({ token: result.token, user: result.user });
  } catch (err) {
    next(err);
  }
});
```

2. 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:

```javascript theme={null}
// Example problematic code
switch (error.code) {
  case 'ECONNREFUSED':
    return res.status(503).json({ error: 'Database connection failed' });
  case '28P01':
    return res.status(503).json({ error: 'Database authentication failed' });
  // Adding new error handling requires editing this switch
}
```

Remediation — handler map/strategy:

```javascript theme={null}
// services/errorHandlers.js
const errorHandlers = {
  'ECONNREFUSED': () => ({ status: 503, body: { error: 'Database connection failed' } }),
  '28P01': () => ({ status: 503, body: { error: 'Database authentication failed' } }),
};

function handleDatabaseError(error) {
  const handler = errorHandlers[error.code] || (() => ({ status: 500, body: { error: 'Database error occurred' } }));
  return handler();
}
module.exports = { handleDatabaseError, errorHandlers };
```

To extend handling, add a new key to `errorHandlers` instead of editing control flow.

3. 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):

```javascript theme={null}
// repositories/userRepository.js (interface-like)
class UserRepository {
  async findByEmail(email) { throw new Error('Must implement findByEmail'); }
}
module.exports = UserRepository;
```

Concrete implementation:

```javascript theme={null}
// repositories/postgresUserRepository.js
const UserRepository = require('./userRepository');
class PostgresUserRepository extends UserRepository {
  constructor(pool) {
    super();
    this.pool = pool;
  }
  async findByEmail(email) {
    const result = await this.pool.query('SELECT * FROM users WHERE email = $1', [email]);
    return result.rows[0];
  }
}
module.exports = PostgresUserRepository;
```

Inject `PostgresUserRepository` in production; tests can inject a `MockUserRepository`.

4. 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:

```javascript theme={null}
// handlers/loginHandler.js
async function loginHandler({ email, password }, responseHandler, services) {
  const { authService } = services;
  const result = await authService.authenticate(email, password);
  if (!result) return responseHandler.unauthorized({ error: 'Invalid credentials' });
  responseHandler.ok({ token: result.token, user: result.user });
}
module.exports = loginHandler;
```

Usage in route:

```javascript theme={null}
router.post('/login', (req, res, next) => {
  const credentials = { email: req.body.email, password: req.body.password };
  const responseHandler = {
    ok: (data) => res.json(data),
    unauthorized: (data) => res.status(401).json(data),
    error: (status, data) => res.status(status).json(data)
  };
  loginHandler(credentials, responseHandler, { authService }).catch(next);
});
```

5. 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:

```javascript theme={null}
// services/configService.js
class Config {
  constructor(env = process.env) {
    this.env = env;
  }
  get jwtSecret() { return this.env.JWT_SECRET; }
  get jwtExpiresIn() { return this.env.JWT_EXPIRES_IN || '24h'; }
}
module.exports = Config;
```

JWT service abstraction:

```javascript theme={null}
// services/jwtService.js
const jwt = require('jsonwebtoken');
module.exports = function createJwtService(config) {
  return {
    generateToken(payload) {
      return jwt.sign(payload, config.jwtSecret, { expiresIn: config.jwtExpiresIn });
    }
  };
};
```

Crypto abstraction:

```javascript theme={null}
// services/cryptoService.js
const bcrypt = require('bcrypt');
module.exports = {
  compare: (plain, hash) => bcrypt.compare(plain, hash),
  hash: (plain) => bcrypt.hash(plain, 10)
};
```

Inject these dependencies into `authService` or `controllers` rather than requiring them directly inside business logic.

Example controller wiring:

```javascript theme={null}
// controllers/authController.js
module.exports = function createAuthController({ userRepository, jwtService, cryptoService, config }) {
  return {
    async login(email, password) {
      const user = await userRepository.findByEmail(email);
      if (!user) return null;
      const ok = await cryptoService.compare(password, user.password_hash);
      if (!ok) return null;
      const token = jwtService.generateToken({ id: user.id, email: user.email });
      return { user, token };
    }
  };
};
```

## Priority remediation plan (phased, practical)

| Phase | Priority | Actions |
| - | -: | - |
| Phase 1 — Critical (immediate) | High | Extract Authentication Service; Implement Repository Pattern; Create Config Service to encapsulate `process.env`. |
| Phase 2 — High | High | Implement Error Handler Strategy (map-based); Extract JWT & Crypto services; Add Validation Service (middleware). |
| Phase 3 — Medium | Medium | Implement pluggable authentication strategies (local, OAuth); Introduce a service container / startup wiring file; Add interface documentation and mocks for testing. |

## 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).

<Callout icon="lightbulb" color="#1CB2FE">
  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.
</Callout>

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`

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/Xs8Ruv7b0QCZf7iM/images/Claude-Code-for-Beginners/Code-Review-with-Claude-Code/SOLID-Principles-Evaluation/github-repo-security-markdown-list.jpg?fit=max&auto=format&n=Xs8Ruv7b0QCZf7iM&q=85&s=2f3b6701e83bb54e236be5ca084c0e15" alt="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." width="1920" height="1080" data-path="images/Claude-Code-for-Beginners/Code-Review-with-Claude-Code/SOLID-Principles-Evaluation/github-repo-security-markdown-list.jpg" />
</Frame>

Where to get the prompts used for these audits:

```text theme={null}
https://github.com/JeremyMorgan/Claude-Code-Reviewing-Prompts
```

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.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/claude-code-for-beginners/module/dd033355-c556-4619-993c-ac717c0f5741/lesson/5b31cb19-905f-4582-9078-640cc8a8c37a" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.