
Singleton Pattern: Only One Instance in the World
One President per country. One DB connection pool. Most famous yet controversial pattern for global managers. Convenient but path to testing hell.

One President per country. One DB connection pool. Most famous yet controversial pattern for global managers. Convenient but path to testing hell.
Why does my server crash? OS's desperate struggle to manage limited memory. War against Fragmentation.

Two ways to escape a maze. Spread out wide (BFS) or dig deep (DFS)? Who finds the shortest path?

Fast by name. Partitioning around a Pivot. Why is it the standard library choice despite O(N²) worst case?

Establishing TCP connection is expensive. Reuse it for multiple requests.

I was reading through the company codebase when I noticed something odd:
const config = Config.getInstance();
const logger = Logger.getInstance();
const db = Database.getInstance();
It was a pattern I hadn't seen before. Every other class used the normal new Car(), new User() syntax, but these classes all had this getInstance() method instead.
I asked a senior developer:
"Why use getInstance() instead of new Config()?"
"That's the Singleton pattern. These objects need to exist only once globally."
I nodded like I understood, but honestly, I didn't. Only once globally? Why not just use a global variable then? Why go through the trouble of creating this getInstance() method?
What confused me even more was that some developers called Singleton a "convenient pattern" while others called it an "anti-pattern." Same thing, completely opposite opinions. What's going on here?
As I researched Singleton, several things didn't click:
1. Why prevent usingnew?
Normal classes work fine with new. But Singleton blocks new and forces you to use getInstance(). I didn't get why this was necessary.
// Global variable
const config = { apiKey: 'abc' };
// Singleton
const config = Config.getInstance();
Both are globally accessible. What's the actual difference?
3. Why do some people call it an "anti-pattern"?It's a design pattern but also an anti-pattern? That makes no sense. Isn't convenience good?
4. What's the multi-threading issue?I saw Java code with synchronized and volatile keywords everywhere. Why is creating a Singleton so complicated?
A foreign developer working in Korea explained Singleton to me like this:
"There's only one President per country.
Calling
new President()100 times shouldn't create 100 presidents. The first elected one should be returned on the 2nd, 3rd calls.That's Singleton."
That metaphor clicked instantly.
Think about an office printer. When developer A asks for "a printer object" and developer B asks for "a printer object," you need to give them the same printer. You can't have two printers.
Same with a database connection pool. The entire service should have only one pool. If module A creates a pool and module B creates another pool, you're wasting connections. That's when I understood: "Things that should exist only once globally" need Singleton.
I also figured out why we use getInstance() instead of a global variable. Global variables occupy memory from program start. But getInstance() creates the instance only when needed (Lazy Initialization). If you never use it, it never gets created, saving memory.
A pattern that ensures only ONE instance of a class exists in the entire program.
Two key principles:
The difference from normal classes becomes crystal clear when you see the code.
After understanding the core concept, I wondered: "How do you actually guarantee only one instance is created?" Turns out there are multiple implementation approaches.
When I first looked up Singleton implementations, I found different approaches for JavaScript, Java, and Python. "Why so many ways?" I thought. But each method solves a different problem.
Create the instance immediately when the class loads. Safest approach if the instance isn't too heavy.
public class EagerSingleton {
// Created immediately at class loading time
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
Create it only when someone asks for it. Efficient but has pitfalls.
class LazySingleton {
static getInstance() {
if (!LazySingleton.instance) {
LazySingleton.instance = new LazySingleton();
}
return LazySingleton.instance;
}
}
if block simultaneously and create TWO instances.This is where it gets complicated. Building a Java server means dealing with multi-threading.
The Problem:// Thread A and Thread B both call getInstance() simultaneously
// Both see instance == null and both execute new Singleton()
// Result: TWO instances created!
To prevent this, we need to lock with synchronized. But locking every time kills performance. That's where Double-Checked Locking comes in.
public class DCLSingleton {
private static volatile DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) { // 1st check (no lock - fast)
synchronized (DCLSingleton.class) { // Lock
if (instance == null) { // 2nd check (with lock)
instance = new DCLSingleton();
}
}
}
return instance;
}
}
When I first saw this, I thought "Why check twice?" But once I understood it, I realized it's genius:
The volatile keyword prevents memory reordering. Without it, the JVM might reorder instructions and return a half-initialized object.
It's genuinely complex. That's why Java has an easier way.
In Java, there's advice from Joshua Bloch (author of Effective Java): Just use Enum.
public enum EnumSingleton {
INSTANCE;
public void doSomething() { ... }
}
__new__ MethodPython also supports Singleton by overriding the __new__ method.
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# Usage
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True
Python has the GIL (Global Interpreter Lock), so you don't need complex thread-safety like Java. Only one thread executes Python code at a time.
In JavaScript, you often don't need a class for Singleton. Modules themselves are singletons.
// logger.js
const logs = [];
export const log = (msg) => logs.push(msg);
export const getLogs = () => logs;
// This object is cached by Node.js/Browser module system.
// It is effectively a Singleton.
Node.js and browser module systems cache loaded modules. Even if you require('./logger') or import './logger' 100 times, the file executes once and the result is reused.
This is the simplest, most natural Singleton. No class needed. No getInstance() method needed.
When I first understood Singleton, I thought "Oh, this is convenient!" It's accessible globally, so it felt useful. I started making everything a Singleton.
But problems emerged later. I've figured out when to use it and when to avoid it.
These three cases are fine for Singleton because:
But using Singleton for other cases causes problems.
When I first heard "Singleton is an anti-pattern," I didn't get it. It's convenient, right? But after I struggled with it myself, I understood.
Global state is hard to track - who changed it, when, where?
I actually hit a bug related to this. Our service had a UserCounter Singleton that tracked user count. Suddenly, the numbers went crazy. 10 users logged in but the counter showed 53.
Took me 3 hours to find the cause. Another teammate had called UserCounter.getInstance().add(43) in a test and never reset it. Global state gets affected from unexpected places.
// Test 1
test('Counter starts at 0', () => {
const counter = new Counter();
expect(counter.count).toBe(0); // ✅ Pass
});
// Test 2 (runs after Test 1)
test('Counter increments', () => {
const counter = new Counter();
counter.increment();
expect(counter.count).toBe(1); // ❌ Fails! (still 2)
});
Tests share state, making isolation impossible.
Good tests should be independent. Test A's success or failure shouldn't affect Test B. But Singleton shares state, making this impossible.
The solution is calling reset() before each test, but that's problematic too:
class Counter {
// ... singleton code ...
// Method forced to exist just for testing
reset() {
this.count = 0;
}
}
// Every test needs this
afterEach(() => {
Counter.getInstance().reset();
});
Having test-specific methods in production code feels wrong. Plus, forgetting reset() causes test failures.
class UserService {
createUser(name) {
const db = Database.getInstance(); // Hidden dependency!
db.save({ name });
}
}
UserService depends on Database, but you can't tell from the constructor.
The function signature createUser(name) looks like it only needs a name. But actually, the Database singleton must already be initialized. That's a hidden dependency.
This makes testing hard. You want to test UserService, but Database.getInstance() tries to connect to a real DB, making tests slow and flaky. You want to use a Mock, but there's no way to inject it.
The class does two things:
This violates SOLID's S (Single Responsibility Principle).
After understanding Singleton's problems, I wondered: "So what should I do instead?"
The answer was Dependency Injection (DI). Instead of "I'll go get it myself" (Singleton), it's "Someone gives it to me".
// ❌ Singleton
class UserService {
createUser(name) {
const db = Database.getInstance();
db.save({ name });
}
}
// ✅ Dependency Injection
class UserService {
constructor(database) { // Explicitly injected
this.db = database;
}
createUser(name) {
this.db.save({ name });
}
}
// Production
const db = new Database();
const userService = new UserService(db);
// Test
const mockDb = { save: jest.fn() };
const userService = new UserService(mockDb);
After switching to this, testing became super easy. Create mockDb and inject it. No real DB connection needed.
True. But here's the difference:
main() creates it once and passes it aroundSimilar result, but DI is way more flexible. Inject Mock during tests, inject real DB in production.
"But if you new Database() everywhere, won't you create multiple instances?"
Yes. That's why we use DI containers.
// container.js
class Container {
constructor() {
this.services = {};
}
register(name, instance) {
this.services[name] = instance;
}
get(name) {
return this.services[name];
}
}
// app.js (entry point)
const container = new Container();
container.register('db', new Database());
container.register('logger', new Logger());
const userService = new UserService(container.get('db'));
const orderService = new OrderService(container.get('db'));
What you register in the container exists only once. Acts like Singleton, but the class itself is just a normal class.
Frameworks like Spring, NestJS, and Angular have built-in DI containers. Just add a decorator like @Injectable() and the framework handles injection automatically.
My first service was Singleton-heavy.
class Analytics {
static getInstance() { /* ... */ }
track(event) { /* ... */ }
}
class Cache {
static getInstance() { /* ... */ }
set(key, value) { /* ... */ }
}
class FeatureFlag {
static getInstance() { /* ... */ }
isEnabled(flag) { /* ... */ }
}
function handleUserSignup(email) {
Analytics.getInstance().track('signup', { email });
if (FeatureFlag.getInstance().isEnabled('new_ui')) {
// ...
}
Cache.getInstance().set(`user:${email}`, { status: 'pending' });
}
It was convenient. Just call getInstance() from anywhere.
handleUserSignup uses from the signatureMocking was especially painful. How do you mock Analytics.getInstance()? Had to mess with global state, making test code messy.
// Dependencies explicitly received
function handleUserSignup(email, { analytics, cache, featureFlag }) {
analytics.track('signup', { email });
if (featureFlag.isEnabled('new_ui')) {
// ...
}
cache.set(`user:${email}`, { status: 'pending' });
}
// Production (created once in container.js)
const container = {
analytics: new Analytics(),
cache: new Cache(),
featureFlag: new FeatureFlag()
};
handleUserSignup('test@example.com', container);
// Test (easy Mock injection)
const mocks = {
analytics: { track: jest.fn() },
cache: { set: jest.fn() },
featureFlag: { isEnabled: () => true }
};
handleUserSignup('test@example.com', mocks);
// Verify track was called
expect(mocks.analytics.track).toHaveBeenCalledWith('signup', { email: 'test@example.com' });
After refactoring:
Code got slightly longer, but maintainability improved dramatically.
So should you never use Singleton? No. I've accepted it's fine in these cases:
Logs need to write to one file/stream globally. Order matters. Logger Singleton makes sense.
Environment variables don't change at runtime. It's read-only global access, so no state issues.
If there's physically one printer, the code should have one printer object too. Singleton reflects reality here.
DB connections are limited resources. Multiple pools waste connections. Managing one pool makes sense.
Common thread: These are "physically singular" or "immutable state" things.
On the flip side, using Singleton for business logic or stateful services causes problems.
In React/Redux apps, the Store is a Singleton.
There is only one state tree for the entire application.
However, Redux avoids the "Hidden Dependency" issue by injecting the store via <Provider store={store}>. Components receive dispatch/state via hooks, not by importing the store directly.
require CacheWhen you require('./module') in Node.js, it executes the file once and caches the result.
Subsequent require calls return the same object.
This is the Singleton pattern built right into the runtime.
Opening a TCP connection to a database is slow (Handshake, Auth, Allocation). We don't want to do this for every request. We create a "Pool" of, say, 10 connections. This Pool object must be a Singleton so that all requests share the same group of connections and don't overwhelm the database.
A lesser-known alternative is the Monostate Pattern. Instead of forcing one instance, we allow multiple instances but make them share the same state.
class Monostate {
static _state = { count: 0 }; // Static state
get count() { return Monostate._state.count; }
set count(value) { Monostate._state.count = value; }
}
const m1 = new Monostate();
const m2 = new Monostate();
m1.count = 10;
console.log(m2.count); // 10!
new Monostate()). Clients don't know it's a singleton.m2 change when I touched m1?"How do you test Singletons without losing your mind?
Add a static method just for tests.
class Database {
// ... implementation ...
// ⚠️ TEST ONLY
static reset() {
Database.instance = null;
}
}
// In test file
afterEach(() => {
Database.reset(); // Clean up!
});
Allow injecting a mock instance.
class Logger {
static instance;
static setInstance(mock) {
Logger.instance = mock;
}
}
// In test
const mockLogger = { log: jest.fn() };
Logger.setInstance(mockLogger);
Singleton often appears as part of other patterns:
IOSFactory) are often SingletonsHere's what I understood about Singleton after studying and struggling with it.
__new__volatile, synchronizedmain()"Don't use it just because it's convenient. Only use it when truly necessary."
Singleton is like a global variable. Convenient now, but toxic as the project grows. The pain really hits when you write tests.
Now I use DI for business logic and reserve Singleton for infrastructure-level things like logging and config. Code became much clearer and testing got easier.
Singleton is ultimately a tradeoff between "convenience" and "maintainability." I chose convenience at first, then switched to maintainability later.