# Singleton and Why It Is Often an Anti-Pattern — Design Patterns

Source: https://www.skillbyai.com/en/design-patterns/c-singleton

> One instance, at a cost.

## Intent and problems

**Intent:** ensure a class has only one instance and provide a global access point to it, typically through a private constructor and a static `getInstance()`. Legitimate needs for a single shared instance exist: a connection pool, a configuration object, a logger. The problems come from the **global access**: any code can reach the instance, so dependencies are hidden, tests share state and are hard to isolate, and swapping the implementation for a fake is awkward. Singletons also complicate concurrency and lifecycle (when is it created and cleaned up?). **Prefer** creating one instance at the application's entry point (the *composition root*) and passing it to whoever needs it. In JavaScript and Python, a **module-level instance** is already a de facto singleton because modules are evaluated once and cached, so the classic class machinery is rarely needed.

## Who owns the instance?

Deciding how many instances exist and who creates them shapes testability.

![Three ideas: Singleton and its problems, Prototype and cloning, Dependency Injection.](assets/figures/design-patterns/section-3-map.svg) — Figure 3.1 — Single instances, copies and injected dependencies.

## Classic singleton versus a module instance

Same outcome, less ceremony; injection is better still.

```typescript
// Classic GoF singleton
class Config {
  private static instance: Config | undefined;
  private constructor(readonly values: Record<string, string>) {}
  static getInstance(): Config {
    if (!Config.instance) Config.instance = new Config(loadFromEnv());
    return Config.instance;
  }
}

// Module-level instance: modules are evaluated once and cached
// db.ts
export const db = createPool({ connectionString: process.env.DATABASE_URL });

// Better for testing: create once at startup, pass it in
class OrderRepository {
  constructor(private pool: Pool) {}
}
const pool = createPool({ connectionString: process.env.DATABASE_URL });
const orders = new OrderRepository(pool);
```

## Single instance is not the same as Singleton

Having one instance of something is often correct. The anti-pattern is the global access point that lets any code grab it without declaring the dependency.

**Quiz:** Why is Singleton often considered an anti-pattern?

- [x] Global access hides dependencies and makes tests share state
- [ ] It always uses too much memory
- [ ] It cannot be used in TypeScript
- [ ] It creates a new object on every call

*Answer:* Global access hides dependencies and makes tests share state. The global access point is the main problem.
