Clean code oop
π§± Claude Skill for clean OOP β full SOLID with one pragmatic carve-out (SRP = cohesion). Cohesive classes + layered architecture across 9 languages.
npx -y skills add bajelanmehran/clean-code-oop --skill clean-code-oopAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Write and refactor clean, object-oriented, maintainable code by applying SOLID properly, with one deliberate carve-out β drop the extreme reading of SRP that demands one class per method or action. Classes are cohesive and grouped by domain concept (one AuthenticationController holding login/register/logout/verify); OCP, LSP, ISP, and DIP are applied in full; and code stays cleanly separated into Request Validation, DTOs, Services, Repositories, and Response/Resource layers, depending on abstractions at real seams. Use this skill WHENEVER the user asks to write, design, scaffold, refactor, clean up, or review code with real structure β controllers, services, business logic, API endpoints, or app modules β even if they never say "clean code", "OOP", or "SOLID". Language-agnostic; examples cover PHP/Laravel, TypeScript/NestJS, Python/FastAPI, Java/Spring, C#/.NET, Go, Rust, Ruby on Rails, and Dart/Flutter.
The file declares its own license as MIT. That is the authorβs claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
9.2 KB, as published. Nobody here has run it
Clean Code & OOP (full SOLID, one carve-out)
Produce clean, object-oriented, maintainable code by applying SOLID properly. There is exactly one deliberate carve-out, and nothing else about SOLID is loosened.
The single carve-out: SRP is not "one method per class"
The Single Responsibility Principle is routinely misread as "a class may have only one method / one action." That misreading spawns a swarm of single-action classes (LoginAction, RegisterAction, LogoutAction...) that fragment one concept across many files. That extreme is the only thing we drop.
Group methods by domain concept (cohesion), not by count:
β
AuthenticationController
login() register() logout() verify() refreshToken()
β every method belongs to the same responsibility: authentication
This is not a violation of SRP β it is SRP's actual definition. SRP means "one reason to change," not "one method." All authentication operations change for the same reason, so they live together. We are restoring SRP's real meaning, not weakening it.
That is the entire carve-out. Everything below is applied in full.
Apply the rest of SOLID in full
- S β Single Responsibility: One reason to change per class. Group cohesive methods by concept (the carve-out above). At the layer level, each layer keeps its single responsibility (see next section).
- O β Open/Closed: Open for extension, closed for modification. Where behavior varies β payment providers, notification channels, export formats, auth strategies β use polymorphism/strategy so new variants are added, not bolted into existing code with conditionals.
- L β Liskov Substitution: A subtype must be usable anywhere its base type is, honoring the base contract (no surprising exceptions, no narrowed inputs). Prefer composition over inheritance to avoid LSP traps in the first place.
- I β Interface Segregation: Keep interfaces focused. A client must not be forced to depend on methods it doesn't use. Split a fat interface into role-specific ones (e.g.
Readable/Writable) rather than one bloated contract. - D β Dependency Inversion: High-level code depends on abstractions, not concretions. Services depend on repository interfaces; external I/O (DB, payment, mail, storage, queue) sits behind an interface so it is swappable and mockable. Inject dependencies via the constructor β never
newthem inside the class.
Scope note (this is correct DIP, not a relaxation): DIP applies to dependencies and seams β the volatile, IO-bound, cross-boundary collaborators a class talks to. Pure data carriers (DTOs, value objects) are not "dependencies" to invert; you pass them, you don't wrap them in interfaces. Putting an interface around a DTO is not SOLID, it's noise.
Clean layer separation (each layer = one responsibility)
Classes group cohesive methods, but responsibilities between layers stay separated. Never collapse these into one fat class:
| Layer | Owns | Never does |
|---|---|---|
| Controller / Handler | Orchestration: receive request β call service β return response. Thin, cohesive by concept. | Business rules, raw DB access, inline validation |
| Request Validation | Validating + shaping incoming data (FormRequest, schema, validator) | Business logic, persistence |
| DTO | Typed, immutable data crossing layer boundaries | Behavior, DB awareness |
| Service | Business logic; orchestrating repositories/other services | HTTP concerns, query building, response formatting |
| Repository | Data access behind an interface; returns domain objects/DTOs | Business rules, HTTP, validation |
| Response / Resource | Shaping the outgoing payload | Leaking internal models or DB columns directly |
A controller method should read like a table of contents: validate β delegate to service β return resource. If business logic leaks into the controller, or queries leak into the service, that is the thing to fix.
Clean-code essentials (always on)
- Intention-revealing names.
verifyOtp, notdoStuff. A caller shouldn't need the implementation to understand the call. - Small, focused methods at one level of abstraction. Extract when a block needs a comment to explain "what."
- Guard clauses over nesting. Return/throw early; keep the happy path un-indented.
- No magic values. Name constants and enums.
- Explicit errors. Throw typed/domain exceptions or return result types; don't swallow errors or return ambiguous nulls.
- Immutability where practical. DTOs and value objects are read-only.
- Constructor injection for dependencies, so classes are testable.
Avoid both failure modes
- β Over-fragmented (the carve-out target): one class per method/action, cohesive concepts scattered across files, indirection nobody reuses, patterns applied for their own sake.
- β Under-structured: a 400-line controller method doing validation + business rules + SQL + JSON shaping; God classes; logic copy-pasted across files; dependencies
new-ed inline.
Target: cohesive classes, fully separated layers, proper SOLID applied at real seams.
Workflow
When the user asks to write new code:
- Identify the domain concept(s) β that defines the cohesive class boundaries.
- Build the layers: Controller β Service β Repository, plus Request Validation, DTO, and Response/Resource.
- Apply OCP/LSP/ISP/DIP: depend on abstractions at seams, inject dependencies, use polymorphism for real variation points.
- Write clean methods with revealing names and guard clauses.
When the user asks to refactor / clean up / review existing code:
- Read it and name the concrete smells (fat controller, leaked queries, God class, one-class-per-action fragmentation, magic values, hidden dependencies...).
- Propose the target structure briefly.
- Apply the changes β move logic to its rightful layer, group cohesive methods, invert dependencies, extract names.
- Preserve behavior; don't silently change functionality.
Output format
- Produce clean code in the user's language (or the one they're already using), idiomatic to that language/framework.
- Add a short "Decisions" note for the structural choices β especially the cohesion choice and where each SOLID seam sits. One line each, e.g.:
- Kept
login/register/logout/verifyin oneAuthenticationControllerβ cohesive concept, not one-action-per-class. UserRepositorybehind an interface (DIP) so it's swappable and mockable.PaymentGatewayis an interface with provider implementations (OCP) β new providers added, not edited in.
- Kept
- Don't annotate every line or lecture on theory. The code plus a few decision notes should speak for themselves.
Reference files
For full, idiomatic beforeβafter examples, read these as needed:
references/layered-architecture.mdβ detailed layer responsibilities, naming, and dependency direction.references/examples-php-laravel.mdβ a fat controller refactored into Request / Service / Repository / Resource, Laravel-idiomatic.references/examples-typescript-nestjs.mdβ the same concept in NestJS with DTOs, providers, and dependency injection.references/examples-python-fastapi.mdβ the same concept in FastAPI: cohesive auth router, ABC repository, Pydantic DTOs,DependsDI.references/examples-java-spring.mdβ the same concept in Spring Boot: port interface, constructor injection, record DTOs, Bean Validation.references/examples-csharp-dotnet.mdβ the same concept in ASP.NET Core: interface repository, DI in Program.cs, record DTOs.references/examples-go.mdβ the same concept in Go: cohesive handler struct, interface repository (DIP), composition-root wiring.references/examples-rust.mdβ the same concept in Rust (axum): cohesive controller, trait repository behindArc<dyn>,thiserrordomain errors.references/examples-ruby-rails.mdβ the same concept in Rails: service object, repository over ActiveRecord, form-object validation, serializer.references/examples-dart-flutter.mdβ the same concept in a Flutter app: cohesive notifier/controller, abstract repository, DI via injection.
Read a reference file when the task is in that language/framework or when you need the detailed layer contract; the guidance above is enough for most tasks.