Hacker News new | ask | show | jobs
by kachnuv_ocasek 42 days ago
What exactly do you mean by “make state explicit”?
3 comments

Minimizing side effects (e.g. functional programming) is a good habit in general. But when we're talking at the scale of a company's entire software solution state is the enemy of everything that is good.

You never want to work on a platform with 500k lines of code any of which could mutate variable state at will and the more you can isolate or simplify state usage the better your life will be.

It's also the enemy of evil things your company does.
Depends on the language I'd say, but overall, try to keep state in as few places as possible, and make it more obvious when it's being used. Modern example would be Rust defaulting to immutability for variables, and makes it very clear when you should expect that this variable actually carries state rather than just a value, by prefixing it with `mut `. Other languages might make it the other way around, making constants/read-only variables stick out, and defaulting to "hiding that they have state" basically.
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_.