Written by Rodrigo de Miguel
Why I didn't set up microservices for my SaaS
(and how that allowed me to launch sooner and help more))
In software you have to make decisions with imperfect information, own the consequences, and revisit choices as context changes. Most decisions aren’t permanent—pragmatism and refactoring keep you moving.
Deciding when there are no certainties
If there’s one thing that defines working in software —especially when you’re building your own product, bootstrapping, or running tight on resources— it’s this: you have to make decisions without having the full picture.
Not deciding isn’t an option.
Waiting until everything is crystal clear isn’t either.
And still, every decision comes with its own backpack:
- more or less development time
- more or less cost
- more or less technical debt
- more or less room to maneuver tomorrow
Learning to decide and live with what you decided is one of those things that quickly separates a junior profile from a senior one.
A sentence that sticks with you (and why it matters)
I remember my first professional project very clearly.
Fresh out of university, loaded with theory, eager to build, and with real-world experience… basically none.
A team of two, we were going to productize a real-time data ingestion POC with Apache Flink and Kafka. I couldn’t wait to start coding as soon as possible.
Oscar, my first senior and mentor, told me on day one, almost without context, sitting in Indizen’s office:
It’s important that you understand we’re going to have to make decisions, and those decisions will have consequences, and we’ll have to accept them.
At the time it sounded dramatic.
Over the years I realized it wasn’t an exaggeration — it was a key lesson.
He wasn’t talking only about code.
He was talking about owning what you choose. About becoming an adult.
Deciding hurts because it commits you
Making a decision means:
- closing doors
- accepting a cost
- betting on a specific path
And that feels tight, especially at the beginning.
A lot of problems in software projects don’t come from bad decisions, but from decisions nobody dares to make:
- architectures stuck in no-man’s-land
- unclear responsibilities
- the classic “we’ll see later”
That “we’ll see later” usually ends up as:
- more complexity
- more friction
- more mental wear and tear
In the end, not deciding is also a decision.
Although, like almost everything in life, it depends: sometimes the smartest move is not to decide yet and to let the context, the product, or the market mature a bit before committing.
When you’re building a business, consequences weigh more
This becomes critical when:
- you’re pushing a product of your own
- you’re bootstrapping
- there isn’t infinite budget
- you don’t have a huge team behind you
In that scenario:
- every week counts
- every technical decision shows up in your energy
- every mistake gets paid with personal hours
There’s no safety net.
You can’t afford decisions that are theoretically elegant but practically useless.
And you also can’t afford getting stuck out of fear of being wrong.
Think before acting (because everything has consequences)
Before making a technical decision, it helps to ask yourself uncomfortable questions:
- What real problem am I solving?
- What happens if I’m wrong?
- How much will it cost me to maintain this in 6 months?
- Who will carry this decision? Me?
It’s not paperwork.
It’s survival instinct.
Examples of real consequences
| Decision | Possible consequence |
|---|---|
| Choosing a “trendy” technology | Complications maintaining it |
| Over-architecting | More time, less product |
| Complex infra | Cost and mental drain |
| Poor separation of responsibilities | Expensive refactors |
None of this is theory.
You pay learn it over time.
But here comes the other half of the truth
And this is the part many people overlook —or prefer not to say out loud—:
We’re in software. We don’t build bridges or skyscrapers.
That changes the rules quite a bit.
In software:
- you can refactor
- you can change direction
- you can undo decisions
With a cost, sure.
But you can.
It’s not an excuse to decide poorly.
It’s a reminder that decisions aren’t eternal.
The balance of commitment without dogmatism
Here’s the important nuance — and where real experience shows up.
A junior profile usually falls into one of these extremes:
- fear of deciding
- total rigidity (“this is the way and that’s it, the manual says so”)
A senior profile understands that:
- you have to decide
- you have to accept the consequences
- and you have to revisit decisions when the context changes
The context always changes:
- new requirements
- more users (or fewer)
- fewer resources
- more complexity than expected
Not revisiting decisions can be as dangerous as not making them.
Technical decisions ≠ moral decisions
A very common mistake is getting emotionally attached to technical decisions
…and to products too (it’s happened to me more than once)
“I chose this, so I’ll defend it no matter what.”
That’s not professionalism.
That’s ego.
A technical decision doesn’t define who you are.
It defines a hypothesis at a specific moment.
And as good old Oscar used to say:
You made the decisions you could with the data you had back then. Accept it.
Hypotheses get validated… or discarded.
When to revisit a decision
Revisiting isn’t redoing everything from scratch.
It’s evaluating again with data.
Revisit a decision if:
- Requirements have clearly changed
- The cost of maintaining it grows faster than expected
- It creates constant friction in the team (or in you)
- It no longer meets the original goal
- Today there’s a clearly better alternative
If several are true, it’s not betraying the initial design.
It’s technical maturity.
How to change direction without breaking everything
Changing a decision isn’t pressing a button.
But you can do it thoughtfully.
Practical steps (when it’s time)
- Identify which specific decision you want to reverse
- Understand why it was made at the time
- Evaluate the real cost of changing it now
- Define an incremental plan
- Learn from the process, not only from the outcome
Not everything can be undone.
But almost everything can be improved.
A real example in ADOPTA: decide, fail, and correct without drama
I’m going to share a specific case from ADOPTA.
It’s simple — even a bit naive in hindsight — and precisely because of that, it’s useful.
(This happens both to juniors and when you try new or lesser-known libraries.)
The initial decision (fast and logical)
In the flow to register an animal I needed:
- basic data
- multiple photos (5 or 6, for example)
The initial solution was straightforward:
- one endpoint
- one “big” POST
- data and images in a single request
I tested from the browser.
It worked.
It deployed, it validated, and on to the next thing.
Decision made.
Reality shows up (and breaks your hypothesis)
When I tested the flow from mobile, everything started to go sideways.
It wasn’t some weird bug.
It wasn’t some edge case.
It was something much more mundane:
- the phone was sending large photos
- several images together exceeded Express’ body parser limit
Result:
the endpoint failed every time.
The initial decision had consequences.
And it was time to accept them… or change them.
Changing the decision (without rewriting half the system)
The solution was practical:
- separate responsibilities
- one endpoint to create the animal
- another to add images one by one to the gallery
Less pretty on paper.
Much more solid day to day.
Problem solved.
We moved on.
The second hit (production always teaches)
Some time later another problem appeared — more serious this time.
The image processing library (sharp) had a memory leak in certain environments.
In production, performance dropped.
There were several options:
- look the other way
- patch it
- scale servers
- change the approach
I chose to change the approach.
The mature correction: touch only where it hurts
Instead of redoing everything:
- I moved image processing to AWS Lambda
- I left the rest of the backend as-is
The effect was immediate:
- goodbye to the memory leak
- better performance
- a more stable system
Since then, that flow hasn’t caused trouble again.
The real lesson
The first decision wasn’t “wrong.”
It was a reasonable hypothesis with the information available.
When it stopped making sense:
- it was changed
- lessons were learned
- the system was improved
That’s exactly what I mean when I say that in software:
decisions have consequences, but they aren’t life sentences.
The difference between software and concrete
A badly designed bridge can kill people.
A badly designed software system:
- gets refactored
- gets migrated
- gets rewritten
(if you build software for planes, please think everything through twice)
This doesn’t remove responsibility.
It removes paralysis.
Being afraid to fail is normal.
Being afraid to decide can kill a project.
What experience really teaches
Over time you learn that:
- no decision is perfect
- everything has trade-offs
- almost everything is temporary
And that the value isn’t in:
- always being right
- choosing “the best” technology
It’s in:
- understanding the context
- deciding intentionally
- accepting consequences
- learning
- adjusting
That is, to me, what best defines seniority.
Learning to decide and live with it isn’t about being inflexible.
It’s about being responsible.
Decide with what you know today.
Accept the consequences.
And remember that, in software, almost nothing is set in stone.
That doesn’t make you weak.
It makes you professional.