← Back to principles

Design Principle

YAGNI — You Aren't Gonna Need It

YAGNI prevents premature optimization and over-engineering. Learn to delay generalization, scope to real requests, and build only what you need now, not later.

YAGNI — You Aren't Gonna Need It

Why This Matters

Think of YAGNI like packing for a trip. You could pack everything "just in case"—winter clothes, summer clothes, formal wear, casual wear, tools, first aid kit, etc. But you'll end up with a heavy suitcase full of things you never use. YAGNI says: pack only what you need for this trip. If you need something later, you can get it then.

This matters because building features "just in case" adds complexity. Each feature needs to be designed, implemented, tested, documented, and maintained. If you never use it, that's wasted effort. Worse, unused features can make the codebase harder to understand and change. YAGNI helps you avoid this by building only what you need now.

In interviews, when someone asks "How would you design this system?", they're testing whether you understand YAGNI. Can you identify what's needed now vs what might be needed later? Do you know when to defer features? Most engineers can't. They build everything "just in case" and wonder why the codebase is complex.

What Engineers Usually Get Wrong

Engineers often think "YAGNI means never plan ahead." But YAGNI doesn't mean don't plan—it means don't build features you don't need yet. You should still think about future requirements and design for change. But don't implement features until you actually need them. The key is: design for change, but implement only what's needed.

Engineers also confuse YAGNI with laziness. YAGNI isn't about avoiding work—it's about avoiding unnecessary work. Building features you don't need is wasteful. Building features you do need is necessary. The challenge is knowing the difference.

How This Breaks Systems in the Real World

A team was building a feature that needed to process 100 items. A developer built it to handle 1 million items "just in case." The optimization used complex algorithms and data structures. The code became hard to understand and debug. But the feature never processed more than 100 items. The optimization was unnecessary and made the code worse.

The fix? Simplify to handle 100 items. Optimize only if you actually need to handle more. This is YAGNI in action—build what you need, not what you might need.

Another story: A team was building a multi-tenant system. They built support for 10 different database types "just in case" they needed to support different databases later. The abstraction was complex and hard to understand. They only ever used PostgreSQL. The abstraction added complexity without providing value. The fix? Build for PostgreSQL. If you need to support other databases later, you can add that support then.


1. Delay generalisation

The future will surprise you. Resist generic solutions until you hold at least two concrete use cases.

1// Premature: full plug-in system with reflection.
2type Plugin = {
3 name: string;
4 execute(): Promise<void>;
5};
6
7const registry: Record<string, Plugin> = {};
8
9export function register(plugin: Plugin) {
10 registry[plugin.name] = plugin;
11}
12
13// Practical: event hooks around today's workflow.
14type InvoiceHooks = {
15 beforeCreate?: (invoice: InvoiceDraft)

2. Scope to the real request

Capture product requirements in concrete language. If the story doesn’t demand multi-tenancy, don’t design for it.

1// Ticket: "Add dark mode toggle to settings".
2// ❌ Overbuilt solution: theme marketplace with persistence, previews, and sandboxing.
3
4// ✅ Start with the toggle the user asked for.
5export function ThemeToggle() {
6 const { theme, setTheme } = useTheme();
7 const isDark = theme === "dark";
8
9 return (
10 <button onClick={() => setTheme(isDark ? "light" : "dark")}>
11 {isDark ? "Switch to light" : "Switch to dark"}

3. Document the decision

Saying “not yet” is easier when you record why. Log the assumption so you can revisit when constraints change.

1## Decision: Single Region Deployment
2
3- **Date:** 2025-02-15
4- **Context:** Only North America traffic. P99 response 90ms.
5- **Decision:** Stay single-region until latency > 150ms or EU traffic > 20%.
6- **Follow-up:** Re-evaluate quarterly.

How to practice YAGNI

  1. Write a test that describes the real user journey. If the test doesn’t fail, you probably don’t need the feature yet.
  2. Budget future work. Estimate cost to add the capability later; if it’s cheap, defer it.
  3. Celebrate deletion. Make pruning dead branches part of your sprint ritual.

Reflection

Identify a module you designed “just in case.” What is its actual usage? How many times has it saved you? If the answer is never, schedule a clean-up.

Takeaway: YAGNI guards energy for real needs. Defer gracefully, document assumptions, and build when evidence demands it.


Interview Questions

Beginner

Q: What does YAGNI stand for and why is it important?

A: YAGNI stands for "You Aren't Gonna Need It." It's a principle that reminds developers to avoid building features or abstractions until there's actual evidence they're needed. It guards against premature optimization and over-engineering. The idea is to solve today's problems with today's solutions, and defer speculative features until real user or system pressure demands them.


Intermediate

Q: Give an example of violating YAGNI. How would you apply YAGNI instead?

A:

Violation: Building a full multi-provider storage abstraction when you only use S3:

1// Premature: full multi-provider abstraction
2abstract class StorageProvider {
3 abstract put(key: string, value: string): Promise<void>;
4}
5
6class S3Storage extends StorageProvider { /* ... */ }
7class GCSStorage extends StorageProvider { /* ... */ }
8class AzureStorage extends StorageProvider { /* ... */ }
9
10// But you only ever use S3Storage

YAGNI approach: Solve today's need, keep the seam obvious:

1type Storage = {
2 put(key: string, value: string): Promise<void>;
3};
4
5const storage: Storage = {
6 async put(key, value) {
7 await s3Client.putObject({ key, value });
8 },
9};
10
11// If you later need GCS, you can swap the implementation
12// The interface is simple enough to change without major refactoring

Key insight: The simple version is easy to change later. If you never need multiple providers, you saved time. If you do need them, the refactor is straightforward because the interface is already clean.


Senior

Q: You're building a social media platform. How do you apply YAGNI when designing the architecture? What would you build in v1 vs. defer? How do you make it easy to add deferred features later?

A:

V1 (YAGNI - build only what's needed now):

  • Single-region deployment (US only)
  • Monolithic service (or 2-3 services max: API, feed, notifications)
  • PostgreSQL for all data (users, posts, comments, likes)
  • Simple feed: SELECT * FROM posts ORDER BY created_at DESC LIMIT 50
  • In-memory caching (Redis) for session management only
  • Synchronous notifications (send email immediately)
  • No real-time features (polling for new posts)

Defer until evidence demands:

  • Multi-region/CDN (wait until you have international users complaining about latency)
  • Microservices (wait until team is >10 people or services have different scaling needs)
  • Specialized databases (wait until PostgreSQL becomes bottleneck - maybe never)
  • Complex feed algorithms (wait until users complain about relevance)
  • Distributed caching strategy (wait until database is slow)
  • Message queue for async notifications (wait until email sending blocks requests)
  • WebSockets/real-time (wait until users request live updates)

Making it easy to add later:

  1. Keep seams obvious:

    // V1: Simple function
    async function getFeed(userId: string): Promise<Post[]> {
      return db.query("SELECT * FROM posts ORDER BY created_at DESC LIMIT 50");
    }
    
    // Easy to evolve to:
    async function getFeed(userId: string): Promise<Post[]> {
      return feedAlgorithm.getPersonalizedFeed(userId); // Swap implementation
    }
    
  2. Document assumptions:

    ## Decision: Single-Region Deployment
    - Date: 2025-01-15
    - Context: 100% US traffic, P99 latency 90ms
    - Decision: Stay single-region until latency > 150ms or EU traffic > 20%
    - Follow-up: Re-evaluate quarterly
    
  3. Use interfaces, not concrete classes:

    // V1: Simple implementation
    interface NotificationService {
      send(userId: string, message: string): Promise<void>;
    }
    
    class SimpleNotificationService implements NotificationService {
      async send(userId, message) {
        await email.send(userId, message); // Synchronous, simple
      }
    }
    
    // Later: Swap to async without changing callers
    class AsyncNotificationService implements NotificationService {
      async send(userId, message) {
        await queue.publish({ userId, message }); // Async, scalable
      }
    }
    
  4. Budget future work: Estimate cost to add later. If it's cheap (like swapping an interface implementation), defer it confidently.

Key insight: YAGNI doesn't mean "never plan." It means "build the simplest thing that works, document why, and make it easy to evolve when evidence demands it." The simple v1 design should have obvious extension points.


Failure Stories You'll Recognize

The Premature Optimization: A team was building a feature that needed to process 100 items. A developer built it to handle 1 million items "just in case." The optimization used complex algorithms and data structures. The code became hard to understand and debug. But the feature never processed more than 100 items. The optimization was unnecessary and made the code worse. The fix? Simplify to handle 100 items. Optimize only if you actually need to handle more. This is YAGNI in action.

The Multi-Database Abstraction: A team was building a multi-tenant system. They built support for 10 different database types "just in case" they needed to support different databases later. The abstraction was complex and hard to understand. They only ever used PostgreSQL. The abstraction added complexity without providing value. The fix? Build for PostgreSQL. If you need to support other databases later, you can add that support then.

The Feature That Was Never Used: A team built a complex feature "just in case" users requested it. The feature took 3 months to build. Users never requested it. The feature added complexity to the codebase and made it harder to maintain. The fix? Don't build features until users actually request them. Use YAGNI to avoid unnecessary work.

What Interviewers Are Really Testing

They want to hear you talk about YAGNI as a principle for avoiding unnecessary work, not laziness. Junior engineers say "build everything just in case." Senior engineers say "YAGNI means building only what you need now. Design for change, but implement only what's needed. Document assumptions and revisit when evidence demands it."

When they ask "How would you design this system?", they're testing:

  • Can you identify what's needed now vs what might be needed later?

  • Do you know when to defer features?

  • Can you design systems that are easy to extend when needed?

  • Delay generalization - resist generic solutions until you have at least two concrete use cases

  • Scope to the real request - capture requirements in concrete language; if the story doesn't demand it, don't design for it

  • Document the decision - saying "not yet" is easier when you record why; log assumptions to revisit when constraints change

  • Write tests that describe real user journeys - if the test doesn't fail, you probably don't need the feature yet

  • Budget future work - estimate cost to add capability later; if it's cheap, defer it confidently

  • Celebrate deletion - make pruning dead branches part of your sprint ritual

  • Keep seams obvious - simple designs should have clear extension points for when evidence demands evolution

  • Use interfaces, not concrete classes - makes swapping implementations easy without changing callers

  • YAGNI guards energy for real needs - defer gracefully, document assumptions, build when evidence demands it

How InterviewCrafted Will Teach This

We'll teach this through real code problems, not abstract principles. Instead of memorizing "YAGNI means You Aren't Gonna Need It," you'll learn through scenarios like "should I build this feature now or later?"

You'll see how YAGNI helps you avoid unnecessary complexity. When an interviewer asks "how would you design this system?", you'll think about what's needed now vs later, and how to design for change without over-engineering—not just "build everything."

  • DRY, KISS & YAGNI - YAGNI is one of three fundamental principles. Learn how DRY, KISS, and YAGNI work together to create maintainable code.
  • KISS — Keep It Simple, Ship It - KISS and YAGNI work together to prevent over-engineering and keep code simple until evidence demands complexity.
  • SOLID Principles - SOLID principles complement YAGNI by providing guidelines for when to apply design patterns and abstractions.

Key Takeaways

Delay generalization - resist generic solutions until you have at least two concrete use cases

Scope to the real request - capture requirements in concrete language; if the story doesn't demand it, don't design for it

Document the decision - saying "not yet" is easier when you record why; log assumptions to revisit when constraints change

Write tests that describe real user journeys - if the test doesn't fail, you probably don't need the feature yet

Budget future work - estimate cost to add capability later; if it's cheap, defer it confidently

Celebrate deletion - make pruning dead branches part of your sprint ritual

Keep seams obvious - simple designs should have clear extension points for when evidence demands evolution

Use interfaces, not concrete classes - makes swapping implementations easy without changing callers

YAGNI guards energy for real needs - defer gracefully, document assumptions, build when evidence demands it

Keep exploring

Principles work best in chorus. Pair this lesson with another concept and observe how your architecture conversations change.