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.
const count = signal(0);
const doubled = computed(() => count() * 2);
count.set(3);
console.log(doubled()); // 6Rule 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.
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.