One idea
Singleton is for one object that must have exactly one shared instance in your app process. Use it rarely.
It is not a shortcut for passing data around. If you use it for normal app state, tests and future changes become harder.
When it fits
A Singleton can fit for a logger, config reader, analytics client, or connection manager where creating many copies causes real pain.
Ask one question first: would two instances break something or waste a shared resource? If not, you probably do not need Singleton.
Common misuse
Do not make UserSession, Cart, CurrentOrder, or HackathonTeam a Singleton just because many files need it.
That turns normal business data into hidden global state. A teammate changing it in one place can break another screen without an obvious function call.
Refactor a logger
Before: every service creates its own logger. Later you want one format and one file target, so changes spread everywhere.
After: one Logger instance is exposed through a controlled static method. The constructor stays private.
class Logger {
private static instance: Logger | null = null;
private constructor() {}
static getInstance(): Logger {
if (Logger.instance === null) {
Logger.instance = new Logger();
}
return Logger.instance;
}
info(message: string) {
console.log(`[INFO] ${new Date().toISOString()} ${message}`);
}
}
// usage
const logger = Logger.getInstance();
logger.info('Payment request created');Keep it small
Make the Singleton boring. It should coordinate one shared service, not become a bag of helper methods.
If you are using AI to refactor, prompt it with the exact pain: “Refactor only the logger into a Singleton. Do not change business logic.” Then check the diff for hidden global state.
Safe refactor path
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done
Testing trap
Singletons can make tests leak state. One test changes the instance, and the next test sees the old value.
Prefer stateless Singletons. If the object stores data, add a clear reset method only for tests, or choose dependency injection later in this track.
Do this now
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done