Development2026 · 06 · 6 min read

Signals over subscriptions

Why I reach for Angular signals and SignalStore before I reach for a manual subscription.

For years the reflex in Angular was: inject a service, subscribe, remember to unsubscribe. Signals quietly removed most of that ceremony — and with it, a whole category of bugs.

The core idea

A signal is just a value that knows who's reading it. Read it in a template or a computed, and the framework tracks the dependency for you. No stream, no teardown, no leaked subscription.

ts
const count = signal(0);
const doubled = computed(() => count() * 2);

count.set(3);
console.log(doubled()); // 6

Rule of thumb

Reach for a signal for state you own. Keep RxJS for genuine streams — events, websockets, debounced input.

Scaling up with SignalStore

On Sudoku Rival, room state and player penalties lived in a single NgRx SignalStore. One source of truth meant synchronization drift simply stopped being a class of bug I had to think about.

A well-modeled store doesn't fix sync bugs — it makes them impossible to write.

ts
export const RoomStore = signalStore(
  withState({ players: [], status: 'idle' }),
  withComputed(({ players }) => ({
    ready: computed(() => players().length >= 2),
  })),
);

The lesson that keeps repeating: design the state before the screens. Everything downstream gets simpler.

Related notes