Skip to content
intermediatePhase 49 · Low-Level Design

KISS

Keep It Simple: prefer straightforward solutions over clever ones.

30m
0 problems
Topic Progress0%

What is KISS

KISS stands for Keep It Simple, Stupid. The simplest solution is usually the best one. Avoid unnecessary complexity.

The Principle

KISS: Keep It Simple, Stupid

Most systems work best if they are kept simple rather
than made complicated.

— Kelly Johnson, Lockheed Skunk Works

Why Simplicity Matters

Simple Code:                    Complex Code:
─────────────                   ─────────────
Easy to understand              Hard to follow
Easy to modify                  Risky to change
Easy to test                    Difficult to verify
Easy to debug                   Hard to troubleshoot
Easy to onboard new devs        Steep learning curve

The Simplicity Spectrum

Too Simple ◀──────────────────▶ Just Right ◀──────────────▶ Too Complex

Missing error    Single function     Well-structured      Over-engineered
handling, no     doing everything    with clear           with unnecessary
validation       (big ball of mud)   responsibilities     abstractions

What KISS is NOT

  • Not lazy: Simple ≠ incomplete
  • Not skipping tests: Simple code still needs testing
  • Not avoiding frameworks: Use them when they simplify
  • Not dumbing down: Simple ≠ unsophisticated

Complexity vs Simplicity

Understanding what makes code complex helps you keep it simple.

Sources of Complexity

Source Example KISS Fix
Unnecessary abstraction Abstract class with 1 subclass Just use the class
Over-engineering Plugin system for 2 options Simple if-else
Deep nesting 5 levels of if/else Extract methods
Tight coupling Class depends on 10 others Reduce dependencies
Premature optimization Complex caching for 10 users Simple solution first
Feature creep Adding "just in case" features YAGNI

Complexity Example

// BAD: Over-engineered
public class NotificationFactory {
    private static final Map<NotificationType, Supplier<Notification>> registry = 
        new ConcurrentHashMap<>();
    
    static {
        registry.put(EMAIL, EmailNotification::new);
        registry.put(SMS, SMSNotification::new);
        registry.put(PUSH, PushNotification::new);
    }
    
    public static Notification create(NotificationType type) {
        return registry.getOrDefault(type, DefaultNotification::new).get();
    }
}

// GOOD: Simple and clear
public class NotificationFactory {
    public static Notification create(NotificationType type) {
        return switch (type) {
            case EMAIL -> new EmailNotification();
            case SMS -> new SMSNotification();
            case PUSH -> new PushNotification();
        };
    }
}

Signs of Unnecessary Complexity

  1. Hard to explain: If you can't explain it simply, it's too complex
  2. Hard to test: Many mocks needed for simple functionality
  3. Hard to change: Small changes require many modifications
  4. Hard to debug: Can't trace execution easily
  5. Hard to onboard: New team members take weeks to understand

Simple Design Checklist

  • Can I explain this in 2 sentences?
  • Does each class have one clear purpose?
  • Are there fewer than 3 levels of abstraction?
  • Can I test it without elaborate setup?
  • Would a junior developer understand it?

Examples

Real-world examples of applying KISS in system design.

Example 1: Data Access

// BAD: Over-abstracted
public interface DataAccessStrategy<T> {
    Optional<T> retrieve(QuerySpecification<T> spec);
}

public class DataAccessStrategyFactory {
    public <T> DataAccessStrategy<T> create(DataAccessType type) { ... }
}

// GOOD: Simple and direct
public class UserRepository {
    private final Database db;
    
    public User findById(String id) {
        return db.query("SELECT * FROM users WHERE id = ?", id);
    }
    
    public void save(User user) {
        db.execute("INSERT INTO users ...", user.getArgs());
    }
}

Example 2: Validation

// BAD: Complex rule engine
public class ValidationEngine {
    private List<ValidationRule> rules;
    private ValidationPipeline pipeline;
    private RuleEvaluator evaluator;
    
    public ValidationResult validate(Object obj) {
        return pipeline.execute(rules, evaluator, obj);
    }
}

// GOOD: Simple validation
public class UserValidator {
    public void validate(User user) {
        if (user.getName() == null || user.getName().isEmpty()) {
            throw new ValidationException("Name is required");
        }
        if (!user.getEmail().contains("@")) {
            throw new ValidationException("Invalid email");
        }
        if (user.getAge() < 0 || user.getAge() > 150) {
            throw new ValidationException("Invalid age");
        }
    }
}

Example 3: Configuration

// BAD: Over-engineered config system
public class ConfigurationManager {
    private ConfigSource primarySource;
    private ConfigSource fallbackSource;
    private ConfigCache cache;
    private ConfigValidator validator;
    
    public <T> T get(String key, Class<T> type) {
        return cache.getOrCompute(key, () -> 
            validator.validate(
                primarySource.get(key, type)
            )
        );
    }
}

// GOOD: Simple config
public class Config {
    private final Properties props;
    
    public Config(String filePath) {
        this.props = new Properties();
        props.load(new FileInputStream(filePath));
    }
    
    public String get(String key) {
        return props.getProperty(key);
    }
    
    public int getInt(String key, int defaultValue) {
        String value = props.getProperty(key);
        return value != null ? Integer.parseInt(value) : defaultValue;
    }
}

KISS in System Design

Don't add:
- Caching layer if DB is fast enough
- Message queue if sync works
- Microservices if monolith is simple
- Load balancer for 100 users
- Auto-scaling for predictable load

Do add when needed:
- Caching when DB is slow
- Queue when processing is slow
- Services when teams need independence
- Load balancer when traffic grows
- Auto-scaling when load varies

The YAGNI-KISS Connection

KISS and YAGNI work together:

  • YAGNI: Don't build what you don't need
  • KISS: Build what you need in the simplest way

Both reduce unnecessary complexity.

Practice Problems

0/3solved
Design KISS System

Design a scalable KISS system. Cover high-level architecture, data model, and API design.

Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliability
KISS Scaling

How would you scale KISS to handle 10x the current load? Identify bottlenecks and solutions.

Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decomposition
KISS Failure Modes

Analyze potential failure modes for KISS and design mitigation strategies.

Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradation

Quiz

1. What does KISS stand for?

Question 1 options

2. Which is an example of unnecessary complexity?

Question 2 options

3. What is a sign of unnecessary complexity?

Question 3 options

4. How does KISS relate to YAGNI?

Question 4 options

5. When should you add a caching layer?

Question 5 options

Flashcards

Question

What is KISS?

Answer

Keep It Simple, Stupid. The simplest solution that works is usually the best. Avoid unnecessary complexity in design and implementation.

Question

What are sources of unnecessary complexity?

Answer

Over-engineering, deep nesting, tight coupling, premature optimization, unnecessary abstraction, and feature creep.

Question

What are signs of over-engineering?

Answer

Hard to explain, hard to test, hard to change, hard to debug, new developers take weeks to understand.

Question

Simple design checklist?

Answer

Can explain in 2 sentences? Each class has one purpose? Fewer than 3 abstraction levels? Easy to test? Junior dev understands?

Question

KISS vs YAGNI?

Answer

YAGNI: don't build what you don't need. KISS: build what you need in the simplest way. Both reduce unnecessary complexity.

Revision Notes

Key Takeaways

  • 1.KISS means the simplest solution that works is usually the best
  • 2.Sources of complexity: over-engineering, deep nesting, tight coupling, premature optimization
  • 3.If you can't explain it simply, it's too complex
  • 4.Add complexity only when proven necessary, not preemptively
  • 5.KISS and YAGNI work together to reduce unnecessary complexity

Interview Tips

  • Start with simple solutions, add complexity only when requirements demand
  • Explain why you chose a simpler approach over a more complex one
  • Discuss trade-offs between simplicity and flexibility
  • Mention KISS when explaining design decisions to interviewers

Cheat Sheet

KISS - Cheat Sheet

Definition:
Keep It Simple, Stupid. Simplest solution is best.

Sources of Complexity:

  • Unnecessary abstraction
  • Over-engineering
  • Deep nesting
  • Tight coupling
  • Premature optimization
  • Feature creep

Signs of Over-Engineering:

  • Hard to explain
  • Hard to test
  • Hard to change
  • Hard to debug
  • Hard to onboard

KISS Checklist:

  • Explain in 2 sentences
  • One clear purpose per class
  • <3 abstraction levels
  • Easy to test
  • Junior dev understands

KISS + YAGNI:
YAGNI: Don't build what you don't need.
KISS: Build it simply when you do.