Claude Skills Guide

Claude Code SOLID Principles Implementation

SOLID principles provide a robust framework for writing maintainable, scalable software. When working with Claude Code, guiding the AI to produce code that adheres to these principles requires specific prompting strategies and workflow patterns. This guide shows you how to implement SOLID principles effectively in your Claude Code workflow.

Single Responsibility in AI-Generated Code

The Single Responsibility Principle states that a class or function should have only one reason to change. Claude Code tends to generate large, multipurpose functions when given broad instructions. You can counter this by explicitly stating responsibility boundaries.

Consider a typical user management scenario:

# Instead of this (violates SRP)
class UserManager:
    def create_user(self, data):
        # validation logic
        # database insertion
        # sending welcome email
        # logging the action
        pass
    
    def update_user(self, user_id, data):
        # validation logic
        # database update
        # sending notification
        pass

# Do this instead (follows SRP)
class UserValidator:
    def validate(self, data):
        # only validation logic
        pass

class UserRepository:
    def create(self, user):
        # only database operations
        pass

class UserNotifier:
    def send_welcome(self, user):
        # only notification logic
        pass

When prompting Claude Code, use phrases like “Create a separate validator class for input validation” or “Extract the email logic into its own service.” The tdd skill can help you write tests that enforce single responsibility by defining clear contracts for each component.

Open/Closed Principle Through AI Prompts

The Open/Closed Principle states that software entities should be open for extension but closed for modification. Getting Claude Code to respect this principle means prompting for abstraction from the start.

// Design for extension from the beginning
interface PaymentProcessor {
  process(amount: number): Promise<PaymentResult>;
}

class StripeProcessor implements PaymentProcessor {
  async process(amount: number): Promise<PaymentResult> {
    // Stripe-specific implementation
  }
}

class PayPalProcessor implements PaymentProcessor {
  async process(amount: number): Promise<PaymentResult> {
    // PayPal-specific implementation
  }
}

Prompt Claude with “Create an interface first, then implement specific handlers” or “Make this extensible for future payment providers.” This approach prevents the common pattern of adding endless if-else chains as new requirements emerge.

The frontend-design skill demonstrates this well when creating component systems—prompt it to build a base component with extension points rather than one-off components.

Liskov Substitution Through Contract Enforcement

The Liskov Substitution Principle requires that derived classes must be substitutable for their base classes. Claude Code often generates subclasses with incompatible signatures or behaviors.

// Base class defines the contract
class Bird {
    fly(): void {
        // common flying logic
    }
}

// LSP violation - penguin can't fly
class Penguin extends Bird {
    fly(): void {
        throw new Error("Penguins cannot fly");
    }
}

// LSP compliant - separate abstractions
interface Bird {
    move(): void;
}

interface FlyingBird extends Bird {
    fly(): void;
}

class Sparrow implements FlyingBird {
    fly(): void { /* flying implementation */ }
}

class Penguin implements Bird {
    move(): void { /* swimming implementation */ }
}

When working with Claude, specify “Ensure subclasses can replace parent classes without breaking behavior” or “Use composition over inheritance when behavior differs significantly.”

Interface Segregation for Cleaner Contracts

The Interface Segregation Principle advocates for narrow, specific interfaces rather than broad ones. Claude Code frequently generates “god interfaces” with many methods.

// Instead of a broad interface
type UserManager interface {
    CreateUser() error
    DeleteUser() error
    UpdatePassword() error
    GenerateReport() error
    ExportData() error
}

// Use focused interfaces
type UserCreator interface {
    CreateUser() error
}

type UserDeleter interface {
    DeleteUser() error
}

type PasswordManager interface {
    UpdatePassword() error
}

type Reporter interface {
    GenerateReport() error
}

Prompt Claude with “Split this large interface into focused contracts” or “Create minimal interfaces that clients actually need.” The supermemory skill can help you maintain these interface contracts across sessions by remembering your architectural decisions.

Dependency Inversion for Flexible Systems

The Dependency Inversion Principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions. Claude Code frequently creates direct dependencies that couple your code to specific implementations.

// Tight coupling (avoid)
class OrderService {
    private database = new PostgreSQLDatabase();
    
    createOrder(orderData) {
        this.database.insert(orderData);
    }
}

// Loose coupling (prefer)
class OrderService {
    private database: Database;
    
    constructor(database: Database) {
        this.database = database;
    }
    
    createOrder(orderData) {
        this.database.insert(orderData);
    }
}

interface Database {
    insert(data: any): void;
}

When prompting Claude, use “Inject dependencies rather than instantiating them directly” or “Depend on abstractions, not concrete implementations.” This becomes especially valuable when using the pdf skill to generate reports—you can inject different report generators without modifying the service that uses them.

Practical Workflow for SOLID Implementation

Integrating SOLID principles into your Claude Code workflow involves three consistent practices:

First, define your abstractions before implementations. When starting a new feature, ask Claude to create interfaces or abstract classes first. This establishes the contract that all implementations must follow.

Second, review generated code for responsibility boundaries. After Claude generates code, examine whether each class or function has a clear, singular purpose. Use follow-up prompts to refactor oversized components.

Third, maintain a skill that documents your team’s SOLID conventions. Store prompts that successfully produce compliant code and reuse them. The skill-creator skill can help you build reusable prompts that enforce these patterns across projects.

Common Violations and Corrections

Watch for these frequent SOLID violations when working with Claude Code:

Constructor instantiation appears when Claude writes const service = new SpecificService() inside a class. Correct this by requiring constructor injection or factory patterns.

Feature envy occurs when a class frequently accesses another class’s data. Prompt Claude to “Move methods to the data they operate on” to resolve this.

Primitive obsession happens when simple types replace meaningful abstractions. Ask Claude to “Replace primitive parameters with meaningful types” to create domain-specific classes.

The artifacts-builder skill proves useful when you need to prototype SOLID-compliant designs quickly—the interactive nature helps you iterate on abstractions before committing to implementation.

Conclusion

Implementing SOLID principles with Claude Code requires explicit guidance, consistent patterns, and proactive refactoring. By prompting for abstractions first, enforcing responsibility boundaries, and maintaining clear contracts, you can use AI assistance while building maintainable software. The key lies in treating Claude Code as a pair programmer that needs clear architectural direction rather than a magic solution that understands implicit requirements.

Built by theluckystrike — More at zovo.one