Most people think the important decisions in software happen during development.
They don’t.
They happen before the system even exists.
The Illusion of Flexibility
At the beginning, everything feels reversible.
You can change the stack. Replace components. Rethink architecture.
Nothing feels permanent.
But that’s only because the consequences haven’t appeared yet.
Early decisions don’t lock you in immediately.
They define the shape of future constraints.
And those constraints accumulate quietly.
Architecture Decides Before Code Exists
Before a single feature is shipped, key decisions are already made:
- how services communicate
- how data is stored
- where boundaries exist
- what depends on what
These are not implementation details.
They are structural decisions that define how the system behaves over time.
That’s why architecture decisions tend to age faster than the code built on top of them.
Code can change.
Architecture becomes environment.
And environment is much harder to replace.
Early Decisions Shape Everything After
Architectural decisions are difficult precisely because they are expensive to change and affect the system as a whole .
A simple choice early on:
- monolith vs distributed
- sync vs async
- centralized vs fragmented
doesn’t stay local.
It spreads.
It influences:
- how teams build
- how systems scale
- how failures propagate
That’s why infrastructure decisions end up lasting for decades.
Not because they are perfect.
But because everything grows around them.
You’re Not Rewriting Code — You’re Rewriting Decisions
At some point, teams try to “start fresh.”
Rewrite the system. Fix the architecture. remove technical debt.
It sounds simple.
It never is.
Because what you’re replacing is not code —
it’s accumulated decisions.
Dependencies. Assumptions. Workarounds. Integrations.
That’s why rewriting software from scratch is far riskier than it looks.
And why migration projects rarely have a clean ending.
You’re not moving a system.
You’re trying to rebuild its entire history.
Decisions Outlive Code
Code ages.
It gets refactored, replaced, deleted.
But decisions don’t disappear.
They stay embedded in:
- data models
- system boundaries
- infrastructure assumptions
- operational processes
That’s why old code survives not because it’s good, but because replacing it is hard.
Because removing code is easy.
Removing the decisions behind it is not.
Complexity Is a Result, Not a Mistake
No one designs a system to become incomprehensible.
But over time, it happens.
Not because of one bad decision.
But because of many reasonable ones.
Each decision solves a local problem.
Together, they create global complexity.
Eventually, you get systems where no one fully understands how everything fits together anymore.
And at that point, change becomes risk.
The Real Problem: Decisions Made With the Least Information
The hardest part is this:
Early decisions are made when you know the least.
At that stage:
- scale is unknown
- usage is hypothetical
- constraints are unclear
So teams optimize for assumptions.
Later, reality diverges.
But by then, the system is already shaped.
You Can’t Avoid Early Decisions
Some teams try to delay decisions.
Stay flexible. Avoid commitment. “Decide later.”
That rarely works.
Because not deciding is still a decision.
It just shifts complexity into the future.
And often makes it harder to resolve.
What Good Early Decisions Actually Do
Good early decisions don’t try to predict everything.
They focus on:
- limiting irreversible choices
- isolating critical components
- allowing parts of the system to evolve independently
Because the goal isn’t to be right.
It’s to remain adaptable.
What Actually Lasts
In software, code changes constantly.
But decisions don’t.
They shape:
- how systems evolve
- how teams think
- what becomes expensive to change
The hardest decisions in software aren’t the ones you revisit.
They’re the ones you never question again.