← Back to Design Principles

Singleton

Design PrinciplesMid

The Question

What is the Singleton pattern and what are its drawbacks?

What a Strong Answer Covers

  • One instance
  • Python implementation
  • anti-pattern awareness.

Senior-Level Answer

The Singleton pattern restricts a class to a single instance and provides a global access point to it. The canonical implementation makes the constructor private, stores the instance in a static field, and exposes a static `getInstance()` method that either creates or returns the existing instance.

In Python, the pattern is often implemented via `__new__`, a module-level object (Python modules are singletons by nature), or a metaclass. In languages like Java or C#, double-checked locking or initialization-on-demand holder idioms handle thread safety.

Common legitimate use cases include: database connection pools (one shared pool across the app), configuration objects loaded once at startup, logging systems where all components write to the same logger, and thread pool managers. The shared resource argument is the strongest justification.

The drawbacks are significant and worth understanding deeply. First, Singleton introduces hidden global state. Any code anywhere can call `getInstance()` and mutate shared state, making execution order unpredictable and bugs hard to reproduce. Second, it creates tight coupling — callers depend on a concrete class, not an interface, making the dependency invisible in function signatures. Third, testability suffers severely: unit tests cannot easily substitute a mock or reset state between test runs. A singleton database connection in tests bleeds state across test cases unless you add explicit reset hooks — which is a smell. Fourth, in multi-threaded environments, lazy initialization requires careful synchronization to avoid double initialization. Fifth, in distributed systems, a 'single instance' guarantee is meaningless — multiple processes or pods each have their own instance.

The modern alternative in most codebases is dependency injection. Pass the shared resource as a constructor parameter rather than having classes reach out to a global. This makes dependencies explicit, allows substitution in tests, and keeps the single-instance guarantee at the composition root (where the DI container or main function wires things together). You still have one instance — you just don't enforce it inside the class itself.

When you see Singleton in a codebase, it often signals that dependency injection wasn't available or wasn't considered. It's a pragmatic tool, but one with real costs that compound as a codebase grows.

What Separates a 2/3 from a 3/3

2/3 — Passing but Incomplete

Correctly explains the pattern's intent and structure, gives at least one valid use case, and names global state or testability as a drawback.

3/3 — Strong Answer

All of the above plus: explains multiple drawbacks with concrete consequences (not just a list), discusses thread safety concerns, and proposes dependency injection as the modern alternative with an explanation of why it's better.

Common Mistakes

  • Listing drawbacks without explaining their consequences — saying 'it's hard to test' without explaining why (state bleeds between tests, can't inject mocks).
  • Not mentioning thread safety in lazy initialization — this is a classic interview follow-up trap.
  • Conflating Singleton with static classes — they have overlapping behavior but different semantics (Singleton can implement interfaces, be passed as a dependency).
  • Recommending Singleton without any caveats, signaling unfamiliarity with how it complicates large codebases.

Follow-Up Questions

  • How would you make a Singleton thread-safe in Java? — Double-checked locking with volatile, or the initialization-on-demand holder idiom which relies on class loading guarantees.
  • How do you test code that depends on a Singleton? — Add a reset method for tests, use a subclassable Singleton with an overridable getInstance, or — better — refactor to dependency injection.
  • Why is a Python module a Singleton, and when is that good enough? — Python caches modules after first import, so module-level state is shared. Sufficient for config or simple shared utilities.
  • What's the difference between a Singleton and a Monostate pattern? — Monostate allows multiple instances but shares all state via class-level (static) variables — same effective behavior, different enforcement mechanism.

Related Questions

  • Factory
  • Strategy
  • Observer
  • IoC / Dependency Injection
  • High Cohesion / Loose Coupling

Can You Explain This Cold?

Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.

Get Graded — Free Assessment
GrindQuestionsAITechnical interview assessment
TermsPrivacyAbout