home / skills / athola / claude-night-market / code-quality-principles

code-quality-principles skill

/plugins/conserve/skills/code-quality-principles

This skill provides guidance on KISS, YAGNI, and SOLID to improve readability and maintainability of Python code.

npx playbooks add skill athola/claude-night-market --skill code-quality-principles

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
7.5 KB
---
name: code-quality-principles
description: Provides guidance on fundamental software design principles to reduce
  complexity, prevent over-engineering, and improve maintainability.
category: development
tags:
- design
- principles
- clean-code
- architecture
tools: []
complexity: low
estimated_tokens: 600
---
## Table of Contents

- [KISS (Keep It Simple, Stupid)](#kiss-keep-it-simple-stupid)
- [YAGNI (You Aren't Gonna Need It)](#yagni-you-arent-gonna-need-it)
- [SOLID Principles](#solid-principles)
- [Quick Reference](#quick-reference)
- [When Principles Conflict](#when-principles-conflict)
- [Integration with Code Review](#integration-with-code-review)

# Code Quality Principles

Guidance on KISS, YAGNI, and SOLID principles with language-specific examples.


## When To Use

- Improving code readability and maintainability
- Applying SOLID, KISS, YAGNI principles during refactoring

## When NOT To Use

- Throwaway scripts or one-time data migrations
- Performance-critical code where readability trades are justified

## KISS (Keep It Simple, Stupid)

**Principle**: Avoid unnecessary complexity. Prefer obvious solutions over clever ones.

### Guidelines

| Prefer | Avoid |
|--------|-------|
| Simple conditionals | Complex regex for simple checks |
| Explicit code | Magic numbers/strings |
| Standard patterns | Clever shortcuts |
| Direct solutions | Over-abstracted layers |

### Python Example

```python
# Bad: Overly clever one-liner
users = [u for u in (db.get(id) for id in ids) if u and u.active and not u.banned]

# Good: Clear and readable
users = []
for user_id in ids:
    user = db.get(user_id)
    if user and user.active and not user.banned:
        users.append(user)
```

### Rust Example

```rust
// Bad: Unnecessary complexity
fn process(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    data.iter()
        .map(|&b| b.checked_add(1).ok_or("overflow"))
        .collect::<Result<Vec<_>, _>>()
        .map_err(|e| e.into())
}

// Good: Simple and clear
fn process(data: &[u8]) -> Result<Vec<u8>, &'static str> {
    let mut result = Vec::with_capacity(data.len());
    for &byte in data {
        result.push(byte.checked_add(1).ok_or("overflow")?);
    }
    Ok(result)
}
```

## YAGNI (You Aren't Gonna Need It)

**Principle**: Don't implement features until they are actually needed.

### Guidelines

| Do | Don't |
|----|-------|
| Solve current problem | Build for hypothetical futures |
| Add when 3rd use case appears | Create abstractions for 1 use case |
| Delete dead code | Keep "just in case" code |
| Minimal viable solution | Premature optimization |

### Python Example

```python
# Bad: Premature abstraction for one use case
class AbstractDataProcessor:
    def process(self, data): ...
    def validate(self, data): ...
    def transform(self, data): ...

class CSVProcessor(AbstractDataProcessor):
    def process(self, data):
        return self.transform(self.validate(data))

# Good: Simple function until more cases appear
def process_csv(data: list[str]) -> list[dict]:
    return [parse_row(row) for row in data if row.strip()]
```

### TypeScript Example

```typescript
// Bad: Over-engineered config system
interface ConfigProvider<T> {
  get<K extends keyof T>(key: K): T[K];
  set<K extends keyof T>(key: K, value: T[K]): void;
  watch<K extends keyof T>(key: K, callback: (v: T[K]) => void): void;
}

// Good: Simple config for current needs
const config = {
  apiUrl: process.env.API_URL || 'http://localhost:3000',
  timeout: 5000,
};
```

## SOLID Principles

### Single Responsibility Principle

Each module/class should have one reason to change.

```python
# Bad: Multiple responsibilities
class UserManager:
    def create_user(self, data): ...
    def send_welcome_email(self, user): ...  # Email responsibility
    def generate_report(self, users): ...     # Reporting responsibility

# Good: Separated responsibilities
class UserRepository:
    def create(self, data): ...

class EmailService:
    def send_welcome(self, user): ...

class UserReportGenerator:
    def generate(self, users): ...
```

### Open/Closed Principle

Open for extension, closed for modification.

```python
# Bad: Requires modification for new types
def calculate_area(shape):
    if shape.type == "circle":
        return 3.14 * shape.radius ** 2
    elif shape.type == "rectangle":
        return shape.width * shape.height
    # Must modify to add new shapes

# Good: Extensible without modification
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...

class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius
    def area(self) -> float:
        return 3.14 * self.radius ** 2
```

### Liskov Substitution Principle

Subtypes must be substitutable for their base types.

```python
# Bad: Violates LSP - Square changes Rectangle behavior
class Rectangle:
    def set_width(self, w): self.width = w
    def set_height(self, h): self.height = h

class Square(Rectangle):  # Breaks when used as Rectangle
    def set_width(self, w):
        self.width = self.height = w  # Unexpected side effect

# Good: Separate types with common interface
class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...

class Rectangle(Shape):
    def __init__(self, width: float, height: float): ...

class Square(Shape):
    def __init__(self, side: float): ...
```

### Interface Segregation Principle

Clients shouldn't depend on interfaces they don't use.

```typescript
// Bad: Fat interface
interface Worker {
  work(): void;
  eat(): void;
  sleep(): void;
}

// Good: Segregated interfaces
interface Workable {
  work(): void;
}

interface Feedable {
  eat(): void;
}

// Clients only implement what they need
class Robot implements Workable {
  work(): void { /* ... */ }
}
```

### Dependency Inversion Principle

Depend on abstractions, not concretions.

```python
# Bad: Direct dependency on concrete class
class OrderService:
    def __init__(self):
        self.db = PostgresDatabase()  # Tight coupling

# Good: Depend on abstraction
from abc import ABC, abstractmethod

class Database(ABC):
    @abstractmethod
    def save(self, data): ...

class OrderService:
    def __init__(self, db: Database):
        self.db = db  # Injected abstraction
```

## Quick Reference

| Principle | Question to Ask | Red Flag |
|-----------|-----------------|----------|
| KISS | "Is there a simpler way?" | Complex solution for simple problem |
| YAGNI | "Do I need this right now?" | Building for hypothetical use cases |
| SRP | "What's the one reason to change?" | Class doing multiple jobs |
| OCP | "Can I extend without modifying?" | Switch statements for types |
| LSP | "Can subtypes replace base types?" | Overridden methods with side effects |
| ISP | "Does client need all methods?" | Empty method implementations |
| DIP | "Am I depending on abstractions?" | `new` keyword in business logic |

## When Principles Conflict

1. **KISS vs SOLID**: For small projects, KISS wins. Add SOLID patterns as complexity grows.
2. **YAGNI vs DIP**: Don't add abstractions until you have 2+ implementations.
3. **Readability vs DRY**: Prefer slight duplication over wrong abstraction.

## Integration with Code Review

When reviewing code, check:
- [ ] No unnecessary complexity (KISS)
- [ ] No speculative features (YAGNI)
- [ ] Each class has single responsibility (SRP)
- [ ] No god classes (> 500 lines)
- [ ] Dependencies are injected, not created (DIP)

**Verification:** Run `wc -l <file>` to check line counts and `grep -c "class " <file>` to count classes per file.

Overview

This skill explains core software design principles—KISS, YAGNI, and SOLID—to reduce complexity, avoid over-engineering, and make code easier to maintain. It gives practical guidelines, quick questions to ask during design, and concise language-specific examples to illustrate good vs bad choices. Use it as a compact reference for refactoring, code review, and design decisions.

How this skill works

The skill inspects common design antipatterns and translates principles into actionable checks: prefer simple, explicit solutions (KISS); avoid implementing features until required (YAGNI); and follow SOLID for class and module structure. It highlights red flags, offers quick decision questions, and provides minimal code examples to show clearer alternatives. Use the checklists and metrics to guide reviews and small refactors.

When to use it

  • Refactoring code to improve readability and reduce technical debt
  • Preparing checklist items for code reviews or PRs
  • Designing new modules where maintainability is a priority
  • Teaching team members basic design trade-offs and patterns
  • Deciding whether to add abstractions or keep a simple implementation

Best practices

  • Ask a single question per principle (e.g., 'Is there a simpler way?') before adding complexity
  • Favor explicit, readable code over clever one-liners or dense abstractions
  • Defer abstractions until a second or third use case appears—apply YAGNI
  • Split responsibilities so each class/module has one reason to change (SRP)
  • Inject abstractions for external systems when you have multiple implementations (DIP)

Example use cases

  • During a pull request review, flag speculative features and request justification or removal
  • Refactor a 500+ line file into focused modules and services to satisfy SRP
  • Replace a switch-based type handler with polymorphism to satisfy OCP and reduce future edits
  • Remove a premature abstraction and replace it with a simple function until additional variants emerge
  • Add dependency injection for testability once a concrete dependency has multiple implementations

FAQ

When should I ignore these principles?

For throwaway scripts, one-off data migrations, or extreme performance hotpaths where simplicity or direct optimizations outweigh long-term maintainability.

How do I resolve conflicts between principles (e.g., KISS vs SOLID)?

Use project scope as the tiebreaker: favor KISS in small projects, add SOLID patterns incrementally as complexity or reuse grows.