Build Systems, not Features
How to make our app logic more resilient to changes
Most devs I mentor write features. I encourage them to design systems.
Here’s how I think about software development:
- Think in Systems
Not just components or endpoints—systems. Take a login flow: it's not just a form and an API call. It's a sequence of decisions:
What do we do if the user’s already authenticated?
What happens after success?
How do we handle different errors?
What triggers the next step?
Designing it as a system means defining its boundaries, behavior, and failure modes—not just getting it to “work.”
- Design Explicit States
A system behaves differently in different situations—so name those situations. Instead of vague boolean flags like isLoading, think in terms of clear phases:
idle
submitting
success
error
unauthorized
Each one implies different UI, different actions, different responsibilities.
Example: a payment flow might go through “ready → processing → confirmed → failed.” Labeling those states avoids confusion and makes side-effects easier to manage.
- Expose → Repeat → Abstract
Most abstractions come too early. I expose everything first—state, actions, effects.
Yes, it’s noisy. That’s the point.
Once I’ve repeated that pattern across a few features, I can see what’s stable and what varies. Only then do I start abstracting—with confidence that I’m not hiding important behavior.
I learnt this the hard way.
Example: Don’t wrap your fetch logic in a “useData” hook on day one. Write it plainly, repeat it in three places, then abstract when the shape becomes obvious.
This mindset has helped me ship complex apps that stay easy to reason about—even months later. Think systems. Name states. Delay abstraction.
It’s slower at the start, faster everywhere else.
#SoftwareEngineering #SystemDesign #FrontendArchitecture #TypeScript #React