Patterns: creation · 3 MIN READ

Singleton: once does not mean global mutable state

Understand one-instance intent and choose an explicit lifetime.

Understand one-instance intent and choose an explicit lifetime. Singleton controls access to one instance. Trace the worked Go example, then compare the benefit with the cost before applying this design to your own project.

Creational pattern · A MAP OF THE IDEA

  1. Owner creates a once function
  2. First call constructs the value
  3. Later calls receive that same value

Useful when

  • One owner needs lazy immutable initialization
  • Creation should happen at most once

Watch out

  • Global mutation hides coupling between tests
  • Once is not a transient-error retry policy

Implementation & operation cost: Medium

Fit depends on the task

Small tool or CLI2/5
Domain-rich application2/5
Independent distributed services2/5

Editorial suitability, not benchmarks or a universal ranking. Styles describe different axes and can be combined.

01

The design decision

Singleton controls access to one instance. Go can initialize once with sync.OnceValue, but a package-global mutable object hides dependencies and shares state across tests. Prefer one explicitly constructed application dependency unless unique identity is a genuine requirement.

02

A complete Go example

Read this program from the entry point and follow the calls shown in the diagram. The output is included at the end so you can check the behavior without a compiler.

go
package main

import (
	"fmt"
	"sync"
)

type Config struct{ Region string }

func main() {
	calls := 0
	load := sync.OnceValue(func() *Config { calls++; return &Config{Region: "local"} })
	a, b := load(), load()
	fmt.Println(a == b, calls)
}

// Output:
// true 1
Read the code

The closure creates one Config for its owner and returns the same pointer twice. OnceValue synchronizes initialization, not future mutation of Region. Keeping the owner local makes the lifetime visible and avoids a hidden process-wide dependency.

03

Tradeoffs and the simpler alternative

OnceValue caches a panic as well as a result; it is not a retry mechanism for transient startup failures. Separate immutable configuration from mutable clients, and inject the latter. Shared uniqueness can complicate parallel tests, shutdown and replacement.

TAKE THIS WITH YOU

Synchronizes creation but not later object mutation. OnceValue caches a panic as well as a result; it is not a retry mechanism for transient startup failures. Separate immutable configuration from mutable clients, and inject the latter. Shared uniqueness can complicate parallel tests, shutdown and replacement.

Check your understanding

Which statement best describes Singleton: once does not mean global mutable state?

Go to the source

Progress stays in this browser. No account needed.