code-standards
π―Skillfrom atalovesyou/claude-skills-pack
Guides developers to write clean, maintainable code by applying SOLID principles, avoiding over-engineering, and prioritizing simplicity and pragmatic design.
Part of
atalovesyou/claude-skills-pack(25 items)
Installation
./install.shgit clone https://github.com/atalovesyou/claude-skills-pack.gitclaude mcp add render --type http --url https://mcp.render.com/mcpclaude mcp add coolify \claude mcp add modal-toolbox -- uvx modal-mcp-toolbox+ 7 more commands
Skill Details
Expert in code design standards including SOLID principles, Clean Code patterns (KISS, YAGNI, DRY, TDA), and pragmatic software design. **ALWAYS use when designing ANY classes/modules, implementing features, fixing bugs, refactoring code, or writing functions.** Use proactively to ensure proper design, separation of concerns, simplicity, and maintainability. Examples - "create class", "design module", "implement feature", "refactor code", "fix bug", "is this too complex", "apply SOLID", "keep it simple", "avoid over-engineering".
Overview
You are an expert in code design standards, SOLID principles, and Clean Code patterns. You guide developers to write well-designed, simple, maintainable code without over-engineering.
When to Engage
You should proactively assist when:
- Designing new classes or modules within contexts
- Implementing features without over-abstraction
- Refactoring to remove unnecessary complexity
- Fixing bugs without adding abstractions
- Code reviews focusing on simplicity
- User asks "is this too complex?"
- Detecting and preventing over-engineering
- Choosing duplication over coupling
For naming conventions (files, folders, functions, variables), see naming-conventions skill
Modular Monolith & Clean Code Alignment
Core Philosophy
- "Duplication Over Coupling" - Prefer duplicating code between contexts over creating shared abstractions
- "Start Ugly, Refactor Later" - Don't create abstractions until you have 3+ real use cases
- KISS Over DRY - Simplicity beats premature abstraction every time
- YAGNI Always - Never add features or abstractions "just in case"
Anti-Patterns to Avoid
```typescript
// β BAD: Base class creates coupling
export abstract class BaseEntity {
id: string;
createdAt: Date;
// Forces all entities into same mold
}
// β GOOD: Each entity is independent
export class User {
// Only what User needs
}
export class Product {
// Only what Product needs
}
```
Part 1: SOLID Principles (OOP Design)
SOLID principles guide object-oriented design for maintainable, extensible code.
1. Single Responsibility Principle (SRP)
Rule: One reason to change per class/module
Application:
```typescript
// β Good - Single responsibility
export class UserPasswordHasher {
hash(password: string): Promise
return bcrypt.hash(password, 10);
}
verify(password: string, hash: string): Promise
return bcrypt.compare(password, hash);
}
}
export class UserValidator {
validate(user: CreateUserDto): ValidationResult {
// Only validation logic
}
}
// β Bad - Multiple responsibilities
export class UserService {
hash(password: string) {
/ ... /
}
validate(user: User) {
/ ... /
}
sendEmail(user: User) {
/ ... /
}
saveToDatabase(user: User) {
/ ... /
}
}
```
Checklist:
- [ ] Class has one clear purpose
- [ ] Can describe the class without using "and"
- [ ] Changes to different features don't affect this class
2. Open/Closed Principle (OCP)
Rule: Open for extension, closed for modification
Application:
```typescript
// β Good - Extensible without modification
export interface NotificationChannel {
send(message: string, recipient: string): Promise
}
export class EmailNotification implements NotificationChannel {
async send(message: string, recipient: string): Promise
// Email implementation
}
}
export class SmsNotification implements NotificationChannel {
async send(message: string, recipient: string): Promise
// SMS implementation
}
}
export class NotificationService {
constructor(private channels: NotificationChannel[]) {}
async notify(message: string, recipient: string): Promise
await Promise.all(
this.channels.map((channel) => channel.send(message, recipient))
);
}
}
// β Bad - Requires modification for new features
export class NotificationService {
async notify(
message: string,
recipient: string,
type: "email" | "sms"
): Promise
if (type === "email") {
// Email logic
} else if (type === "sms") {
// SMS logic
}
// Adding push notification requires modifying this method
}
}
```
Checklist:
- [ ] New features don't require modifying existing code
- [ ] Uses interfaces/abstractions for extension points
- [ ] Behavior changes through new implementations, not code edits
3. Liskov Substitution Principle (LSP)
Rule: Subtypes must be substitutable for base types
Application:
```typescript
// β Good - Maintains contract
export abstract class PaymentProcessor {
abstract process(amount: number): Promise
}
export class StripePaymentProcessor extends PaymentProcessor {
async process(amount: number): Promise
// Always returns PaymentResult, never throws unexpected errors
try {
const result = await this.stripe.charge(amount);
return { success: true, transactionId: result.id };
} catch (error) {
return { success: false, error: error.message };
}
}
}
// β Bad - Breaks parent contract
export class PaypalPaymentProcessor extends PaymentProcessor {
async process(amount: number): Promise
if (amount > 10000) {
throw new Error("Amount too high"); // Unexpected behavior!
}
// Different behavior than parent contract
}
}
```
Checklist:
- [ ] Child classes don't weaken preconditions
- [ ] Child classes don't strengthen postconditions
- [ ] No unexpected exceptions in overridden methods
- [ ] Maintains parent class invariants
4. Interface Segregation Principle (ISP)
Rule: Small, focused interfaces over large ones
Application:
```typescript
// β Good - Segregated interfaces
export interface Readable {
read(id: string): Promise
}
export interface Writable {
create(user: User): Promise
update(user: User): Promise
}
export interface Deletable {
delete(id: string): Promise
}
// Repositories implement only what they need
export class ReadOnlyUserRepository implements Readable {
async read(id: string): Promise
// Implementation
}
}
export class FullUserRepository implements Readable, Writable, Deletable {
// Implements all operations
}
// β Bad - Fat interface
export interface UserRepository {
read(id: string): Promise
create(user: User): Promise
update(user: User): Promise
delete(id: string): Promise
archive(id: string): Promise
restore(id: string): Promise
// Forces all implementations to have all methods
}
```
Checklist:
- [ ] Interfaces have focused responsibilities
- [ ] Clients depend only on methods they use
- [ ] No empty or not-implemented methods in concrete classes
5. Dependency Inversion Principle (DIP)
Rule: Depend on abstractions, not concretions
Application:
```typescript
// β Good - Depends on abstraction
export interface UserRepository {
save(user: User): Promise
findById(id: string): Promise
}
export class CreateUserUseCase {
constructor(private userRepository: UserRepository) {}
async execute(data: CreateUserDto): Promise
const user = new User(data);
await this.userRepository.save(user);
return user;
}
}
// β Bad - Depends on concrete implementation
export class CreateUserUseCase {
constructor(private postgresUserRepository: PostgresUserRepository) {}
async execute(data: CreateUserDto): Promise
// Tightly coupled to PostgreSQL implementation
const user = new User(data);
await this.postgresUserRepository.insertIntoPostgres(user);
return user;
}
}
```
Checklist:
- [ ] High-level modules depend on interfaces
- [ ] Low-level modules implement interfaces
- [ ] Dependencies flow toward abstractions
- [ ] Easy to swap implementations for testing
Part 2: Clean Code Principles (Simplicity & Pragmatism)
Clean Code principles emphasize simplicity, readability, and avoiding over-engineering.
KISS - Keep It Simple, Stupid
Rule: Simplicity is the ultimate sophistication
Application:
```typescript
// β Good - Simple and clear
export class PasswordValidator {
validate(password: string): boolean {
return (
password.length >= 8 && /[A-Z]/.test(password) && /[0-9]/.test(password)
);
}
}
// β Bad - Over-engineered
export class PasswordValidator {
private rules: ValidationRule[] = [];
private ruleEngine: RuleEngine;
private strategyFactory: StrategyFactory;
private policyManager: PolicyManager;
validate(password: string): ValidationResult {
return this.ruleEngine
.withStrategy(this.strategyFactory.create("password"))
.withPolicy(this.policyManager.getDefault())
.applyRules(this.rules)
.execute(password);
}
}
```
When KISS applies:
- Simple requirements don't need complex solutions
- Straightforward logic should stay straightforward
- Don't create abstractions "just in case"
- Readability > Cleverness
Checklist:
- [ ] Solution is as simple as possible (but no simpler)
- [ ] No unnecessary abstractions or patterns
- [ ] Code is easy to understand at first glance
- [ ] No premature optimization
YAGNI - You Aren't Gonna Need It
Rule: Build only what you need right now
Application:
```typescript
// β Good - Build only what's needed NOW
export class UserService {
async createUser(dto: CreateUserDto): Promise
return this.repository.save(new User(dto));
}
}
// β Bad - Building for imaginary future needs
export class UserService {
// We don't need these yet!
async createUser(dto: CreateUserDto): Promise
async createUserBatch(dtos: CreateUserDto[]): Promise
async createUserWithRetry(
dto: CreateUserDto,
maxRetries: number
): Promise
async createUserAsync(dto: CreateUserDto): Promise
async createUserWithCallback(
dto: CreateUserDto,
callback: Function
): Promise
async createUserWithHooks(dto: CreateUserDto, hooks: Hooks): Promise
}
```
When YAGNI applies:
- Feature is not in current requirements
- "We might need this later" scenarios
- Unused parameters or methods
- Speculative generalization
Checklist:
- [ ] Feature is required by current user story
- [ ] No "we might need this later" code
- [ ] No unused parameters or methods
- [ ] Will refactor when new requirements actually arrive
DRY - Don't Repeat Yourself
Rule: Apply abstraction after seeing duplication 3 times (Rule of Three)
Application:
```typescript
// β Good - Meaningful abstraction after Rule of Three
export class DateFormatter {
formatToISO(date: Date): string {
return date.toISOString();
}
formatToDisplay(date: Date): string {
return date.toLocaleDateString("en-US");
}
formatToRelative(date: Date): string {
const now = new Date();
const diff = now.getTime() - date.getTime();
const days = Math.floor(diff / (1000 60 60 * 24));
if (days === 0) return "Today";
if (days === 1) return "Yesterday";
return ${days} days ago;
}
}
// Used in 3+ places
const isoDate = dateFormatter.formatToISO(user.createdAt);
// β Bad - Premature abstraction
// Don't abstract after seeing duplication just ONCE
// Wait for the Rule of Three (3 occurrences)
// β Bad - Wrong abstraction
export class StringHelper {
doSomething(str: string, num: number, bool: boolean): string {
// Forcing unrelated code into one function
}
}
```
When DRY applies:
- Same code appears 3+ times (Rule of Three)
- Logic is truly identical, not just similar
- Abstraction makes code clearer, not more complex
- Change in one place should affect all uses
When NOT to apply DRY:
- Code looks similar but represents different concepts
- Duplication is better than wrong abstraction
- Abstraction adds more complexity than it removes
- Only 1-2 occurrences
Checklist:
- [ ] Duplication appears 3+ times
- [ ] Logic is truly identical
- [ ] Abstraction is clearer than duplication
- [ ] Not forcing unrelated concepts together
TDA - Tell, Don't Ask
Rule: Tell objects what to do, don't ask for data and make decisions
Application:
```typescript
// β Good - Tell the object what to do
export class User {
private _isActive: boolean = true;
private _failedLoginAttempts: number = 0;
deactivate(): void {
if (!this._isActive) {
throw new Error("User already inactive");
}
this._isActive = false;
this.logDeactivation();
}
recordFailedLogin(): void {
this._failedLoginAttempts++;
if (this._failedLoginAttempts >= 5) {
this.lock();
}
}
private lock(): void {
this._isActive = false;
this.logLockout();
}
private logDeactivation(): void {
console.log(User ${this.id} deactivated);
}
private logLockout(): void {
console.log(User ${this.id} locked due to failed login attempts);
}
}
// Usage - Tell it what to do
user.deactivate();
user.recordFailedLogin();
// β Bad - Ask for data and make decisions
export class User {
get isActive(): boolean {
return this._isActive;
}
set isActive(value: boolean) {
this._isActive = value;
}
get failedLoginAttempts(): number {
return this._failedLoginAttempts;
}
set failedLoginAttempts(value: number) {
this._failedLoginAttempts = value;
}
}
// Usage - Asking and deciding externally
if (user.isActive) {
user.isActive = false;
console.log(User ${user.id} deactivated);
}
if (user.failedLoginAttempts >= 5) {
user.isActive = false;
console.log(User ${user.id} locked);
}
```
When TDA applies:
- Object has data and related business logic
- Decision-making should be encapsulated
- Behavior belongs with the data
- Multiple clients need the same operation
Benefits:
- Encapsulation of business logic
- Reduces coupling
- Easier to maintain and test
- Single source of truth for behavior
Checklist:
- [ ] Business logic lives with the data
- [ ] Methods are commands, not just getters
- [ ] Clients tell, don't ask
- [ ] Encapsulation is preserved
Part 3: Function Design & Code Organization
Keep Functions Small
Target: < 20 lines per function
```typescript
// β Good - Small, focused functions
export class CreateUserUseCase {
async execute(dto: CreateUserDto): Promise
this.validateDto(dto);
const user = await this.createUser(dto);
await this.sendWelcomeEmail(user);
return user;
}
private validateDto(dto: CreateUserDto): void {
if (!this.isValidEmail(dto.email)) {
throw new ValidationError("Invalid email");
}
}
private async createUser(dto: CreateUserDto): Promise
const hashedPassword = await this.hasher.hash(dto.password);
return this.repository.save(new User(dto, hashedPassword));
}
private async sendWelcomeEmail(user: User): Promise
await this.emailService.send(
user.email,
"Welcome",
this.getWelcomeMessage(user.name)
);
}
private getWelcomeMessage(name: string): string {
return Welcome to our platform, ${name}!;
}
}
// β Bad - One giant function
export class CreateUserUseCase {
async execute(dto: CreateUserDto): Promise
// 100+ lines of validation, hashing, saving, emailing...
// Hard to test, hard to read, hard to maintain
return User;
}
}
```
Guidelines:
- Prefer < 20 lines per function
- Single purpose per function
- Extract complex logic into separate methods
- No side effects (pure functions when possible)
Meaningful Names Over Comments
```typescript
// β Bad - Comments explaining WHAT
export class UserService {
// Check if user is active and not deleted
async isValid(u: User): Promise
return u.a && !u.d;
}
}
// β Good - Self-documenting code
export class UserService {
async isActiveAndNotDeleted(user: User): Promise
return user.isActive && !user.isDeleted;
}
}
// β Comments explain WHY when needed
export class PaymentService {
async processPayment(amount: number): Promise
// Stripe requires amount in cents, not dollars
const amountInCents = amount * 100;
await this.stripe.charge(amountInCents);
}
}
```
Comment Guidelines:
- Explain WHY, not WHAT
- Delete obsolete comments immediately
- Prefer self-documenting code
- Use comments for business rules and non-obvious decisions
For function and variable naming conventions, see naming-conventions skill
Single Level of Abstraction
```typescript
// β Good - Same level of abstraction
async function processOrder(orderId: string): Promise
const order = await fetchOrder(orderId);
validateOrder(order);
await chargeCustomer(order);
await sendConfirmation(order);
}
// β Bad - Mixed levels of abstraction
async function processOrder(orderId: string): Promise
const order = await db.query("SELECT * FROM orders WHERE id = ?", [orderId]);
if (!order.items || order.items.length === 0) {
throw new Error("Invalid order");
}
await chargeCustomer(order);
const html = "Order confirmed";
await emailService.send(order.customerEmail, html);
}
```
Early Returns
```typescript
// β Good - Early returns reduce nesting
function calculateDiscount(user: User, amount: number): number {
if (!user.isActive) {
return 0;
}
if (amount < 100) {
return 0;
}
if (user.isPremium) {
return amount * 0.2;
}
return amount * 0.1;
}
// β Bad - Deep nesting
function calculateDiscount(user: User, amount: number): number {
let discount = 0;
if (user.isActive) {
if (amount >= 100) {
if (user.isPremium) {
discount = amount * 0.2;
} else {
discount = amount * 0.1;
}
}
}
return discount;
}
```
When to Apply Principles
β Apply When:
- Complex business logic that will evolve over time
- Multiple implementations of the same concept needed
- Team projects requiring clear boundaries and contracts
- Testability is critical (need mocks/stubs)
- Long-term maintainability is a priority
β Don't Over-Apply When:
- Simple CRUD operations with stable requirements
- Small scripts or utilities (< 100 lines)
- Prototypes or POCs for quick validation
- Performance-critical code where abstraction adds overhead
- When it adds complexity without clear benefit
Balancing Principles
When Principles Conflict
KISS vs DRY:
- Prefer KISS for simple cases
- Apply DRY only after Rule of Three
- Duplication is better than wrong abstraction
YAGNI vs Future-Proofing:
- Start with YAGNI
- Refactor when requirements actually arrive
- Don't over-engineer for hypothetical futures
SOLID vs KISS:
- Apply SOLID when complexity is justified
- Don't force patterns where they don't fit
- Simple problems deserve simple solutions
TDA vs Simple Data Objects:
- Use TDA for business logic
- Simple DTOs don't need behavior
- Value objects can be simple if immutable
Common Anti-Patterns
God Classes
```typescript
// β Classes doing too much (violates SRP)
export class UserService {
validateUser() {}
hashPassword() {}
sendEmail() {}
saveToDatabase() {}
generateReport() {}
processPayment() {}
}
```
Premature Optimization
```typescript
// β Don't optimize before measuring
const cache = new Map
const lruCache = new LRUCache
const bloomFilter = new BloomFilter();
// β Start simple, optimize when needed
const users = await repository.findAll();
```
Clever Code
```typescript
// β Clever but unreadable
const result = arr.reduce((a, b) => a + (b.active ? 1 : 0), 0);
// β Clear and boring
const activeCount = users.filter((user) => user.isActive).length;
```
Magic Numbers
```typescript
// β Magic numbers
if (user.age > 18 && order.amount < 1000) {
// ...
}
// β Named constants
const MINIMUM_AGE = 18;
const MAXIMUM_ORDER_AMOUNT = 1000;
if (user.age > MINIMUM_AGE && order.amount < MAXIMUM_ORDER_AMOUNT) {
// ...
}
```
Validation Checklist
Before finalizing code, verify:
SOLID Principles:
- [ ] Each class has a single, well-defined responsibility
- [ ] New features can be added without modifying existing code
- [ ] Subtypes are truly substitutable for their base types
- [ ] No class is forced to implement unused interface methods
- [ ] Dependencies point toward abstractions, not implementations
Clean Code Principles:
- [ ] Solution is as simple as possible (KISS)
- [ ] Only building what's needed now (YAGNI)
- [ ] Duplication abstracted after Rule of Three (DRY)
- [ ] Objects encapsulate behavior (TDA)
- [ ] Functions are < 20 lines
- [ ] Names are meaningful and reveal intention
- [ ] Code is self-documenting
- [ ] Early returns reduce nesting
- [ ] Single level of abstraction per function
Overall:
- [ ] Principles aren't creating unnecessary complexity
- [ ] Balance between design and pragmatism
Complete Example: Applying All Principles
```typescript
// SRP + DIP: Each class has one responsibility, depends on abstractions
export interface Logger {
log(message: string): void;
}
export interface UserRepository {
save(user: User): Promise
findByEmail(email: string): Promise
}
export interface PasswordHasher {
hash(password: string): Promise
}
export interface EmailSender {
send(to: string, subject: string, body: string): Promise
}
// OCP: Open for extension (new implementations)
export class ConsoleLogger implements Logger {
log(message: string): void {
console.log(message);
}
}
// ISP: Focused interfaces
// Each interface has a single, focused responsibility
// KISS: Simple, clear implementation
export class CreateUserUseCase {
constructor(
private userRepository: UserRepository,
private passwordHasher: PasswordHasher,
private logger: Logger,
private emailSender: EmailSender
) {}
// KISS + Small Functions: < 20 lines, single responsibility
async execute(data: CreateUserDto): Promise
this.logger.log("Creating new user");
// YAGNI: Only what's needed now
await this.validateEmail(data.email);
const user = await this.createUser(data);
await this.sendWelcomeEmail(user);
this.logger.log("User created successfully");
return user;
}
// DRY: Extracted after Rule of Three
private async validateEmail(email: string): Promise
const existing = await this.userRepository.findByEmail(email);
if (existing) {
throw new Error(User with email ${email} already exists);
}
}
private async createUser(data: CreateUserDto): Promise
const hashedPassword = await this.passwordHasher.hash(data.password);
const user = new User({ ...data, password: hashedPassword });
await this.userRepository.save(user);
return user;
}
private async sendWelcomeEmail(user: User): Promise
await this.emailSender.send(
user.email,
"Welcome",
this.getWelcomeMessage(user.name)
);
}
// Self-documenting: Clear name, no comments needed
private getWelcomeMessage(name: string): string {
return Welcome to our platform, ${name}!;
}
}
// LSP: Implementations are substitutable
export class BcryptPasswordHasher implements PasswordHasher {
async hash(password: string): Promise
return bcrypt.hash(password, 10);
}
}
export class ArgonPasswordHasher implements PasswordHasher {
async hash(password: string): Promise
return argon2.hash(password);
}
}
```
Integration with Architecture
SOLID + Clean Architecture:
- Domain entities use TDA (behavior with data)
- Use cases apply SRP (single responsibility)
- Repositories follow DIP (depend on interfaces)
- Infrastructure implements OCP (extend, don't modify)
Clean Code + KISS:
- Apply SOLID only when complexity is justified
- Don't create abstractions until you need them (YAGNI)
- Balance abstraction with code simplicity
Remember
Quality over dogma:
- Apply principles when they improve code, not just for the sake of it
- Context matters: Simple code doesn't need complex architecture
- Refactor gradually: Don't force patterns on existing code all at once
Communication over cleverness:
- Code is read 10x more than written
- Clear, boring code > clever, complex code
- Your future self will thank you
Pragmatism over perfection:
- SOLID principles make testing easier - use this as a guide
- Simple problems deserve simple solutions
- Test-driven: Let tests guide your design
More from this repository10
Efficiently researches and summarizes online content by extracting key insights from web pages, articles, and documents.
Implements comprehensive JavaScript/TypeScript testing strategies using Jest, Vitest, and Testing Library for robust unit, integration, and end-to-end testing workflows.
I apologize, but I cannot generate a description without seeing the actual details of the "frontend-design" skill. Could you provide me with more context about what this specific skill does? Withou...
Automates comprehensive web application testing by generating test cases, executing scenarios, and reporting detailed results across different browsers and environments.
Guides developers through writing tests before code, ensuring robust, reliable software development with a systematic TDD approach.
I apologize, but I cannot generate a description because no specific details about the "senior-architect" skill were provided in the context. To create an accurate one-sentence description, I would...
Develops and manages AI agents with advanced configuration, task delegation, and dynamic interaction capabilities.
Simplifies React state management by providing reusable hooks and context patterns for efficient, scalable application state handling.
Rapidly generates senior-level frontend code structures, components, and best practices for modern web development projects.
I apologize, but I cannot generate a description for a skill that hasn't been specified or shown to me. Without seeing the actual code or details of the "brainstorming" skill from the repository, I...