Hacker News new | ask | show | jobs
by CSMastermind 43 days ago
The behavior of every valid state should be defined and obvious to anyone making changes. Invalid states should be impossible to represent.

All software is actually a state machine and almost all complexity comes from unmanaged state transitions. Especially if you have errors, concurrency, async actions, and distributed systems.

The most important task any software architect has is defining how you know what is true about the system.

Consider a few examples:

await sendEmail(); await saveUser();

That's obvious right? Well what happens when saveUser fails and some retry sends a duplicate email?

---

If you're relying on a webhook to let system A know when system B has changed things, what happens when that failed and now the two systems are out of sync? This is why event-driven architectures often work well: they externalize state transitions.

---

A classic React problem, you define state like this:

``` const [loading, setLoading] = ... const [error, setError] = ... const [data, setData] = ... const [isRefreshing, setRefreshing] = ... ```

Now there's all sorts of invalid states you can be in. Like "loading true, data true, error true".

**

You should strongly prefer: state machines, lifecycle enums, event logs, immutable events, append-only histories, strict versioning, idempotency keys, and explicit workflow stages.

You should avoid like the plauge: hidden globals, timing assumptions, inferred behavior, synchronization, side effects, boolean explosion, mulitple sources of truth.

Event sourcing, CQRS, Redux, Kafka, workflow engines, finite state machines, transactional outboxes, CRDTs, database normalization they all do the same thing _control where the state lives_.