I Built a Complex Web App Architecture — Then a Simple Refactor Cut It in Half
How overengineering quietly ruins good products, burns teams out, and how deleting code made me a better engineer
I Built a Complex Web App Architecture — Then a Simple Refactor Cut It in Half
How overengineering quietly ruins good products, burns teams out, and how deleting code made me a better engineer

Created on Copilot
Read here for free.
I was proud of that codebase.
Not “quietly confident” proud. Architect-on-LinkedIn proud.
Multiple layers. Clean separations. Abstract factories feeding domain services that talked to repositories through interfaces that had… interfaces. Diagrams everywhere. Namespaces so deep you needed breadcrumbs to get back home.
And it worked. Mostly.
Then one afternoon, during a perfectly normal code review, a teammate said something that annoyed me more than it should have
“Why don’t we just… remove this?”
Not rewrite. Not redesign. Remove.
That refactor took less than a day. It cut the architecture in half. And it made the app better.
That moment changed how I think about “good architecture” forever.
The Lie We Tell Ourselves About Architecture
Here’s the lie
“More structure now will save us pain later.”
It sounds responsible. It sounds senior. It sounds like something you say right before building a system no one understands.
What we actually mean is
“I’m afraid of future problems I can’t clearly describe.”
So we prepare for everything.
Multiple state layers.
Indirection everywhere.
Config-driven behavior nobody asked for.
A folder called core, another called shared, and one named common—which somehow all mean different things.
And suddenly, adding a button requires touching seven files and explaining it in Slack.
Complexity Feels Productive. Simplicity Feels Illegal.
Here’s the uncomfortable truth
Complex code feels like work. Simple code feels like cheating.
When I built that architecture, I felt busy. Important. Like I was “future-proofing.”
When I refactored it, I felt nervous. Like I was breaking some unspoken engineering law.
Why?
Because we’re trained to fear simplicity.
We equate it with
- “This won’t scale”
- “This is too basic”
- “A senior engineer wouldn’t do this”
Spoiler: senior engineers do this all the time. They just don’t brag about it.
What Actually Went Wrong in My “Perfect” Architecture
Let’s be concrete.
1. Abstraction Without Pressure Is Just Guessing
I abstracted things that had one implementation.
One API client. One data source. One consumer.
I told myself
“We’ll need this flexibility later.”
Later never came. But the complexity did.
Rule I live by now
Don’t abstract until duplication or change forces you to.
If there’s only one version, your abstraction is fiction.
2. Indirection Has a Cost We Rarely Measure
Every extra layer adds
- Cognitive load
- Debug time
- Onboarding friction
When a bug appeared, the question wasn’t
“What’s wrong?”
It was
“Where does this logic even live?”
That’s not scalable. That’s expensive confusion.
3. Architecture That Fights Reading Is Bad Architecture
Here’s a brutal metric I use now
Can a new dev trace a feature in under 10 minutes?
If not, your architecture is serving itself — not the team.
Readable code beats clever code. Every. Single. Time.
The Refactor: What We Actually Changed
We didn’t “rewrite the app.” We deleted assumptions.
Before (over-simplified example)
// UserService -> UserRepository -> ApiClient -> HttpAdapter
this.userService.getUser(id)
Four layers. One API call.
After
fetchUser(id) {
return http.get(`/users/${id}`);
}
That’s it.
No ceremony. No indirection Olympics. Just code that says what it does.
And yes — tests stayed. Types stayed. Structure stayed.
The noise didn’t.
But What About Scale?
Ah yes. The sacred question.
Here’s the thing no one tells you:
Most apps don’t die from lack of scale. They die from lack of clarity.
Scaling problems are good problems. They mean users showed up.
Unreadable code problems? Those happen on day one.
Optimize for
- Team speed
- Feature clarity
- Change confidence
You can refactor for scale later. You can’t recover months of lost momentum.
A Simple Framework I Use Now
Before adding complexity, I ask
1. What real problem am I solving?
Not hypothetical. Not “maybe one day.” Real. Present. Painful.
2. What’s the simplest thing that works today?
Not clever. Not impressive. Just honest.
3. How hard will this be to remove later?
If it’s hard to delete, it better be worth it.
The Senior Skill No One Talks About
Junior devs learn to add code. Senior devs learn to remove it.
That refactor didn’t make me feel smart. It made me feel calm.
The app became easier to reason about. Bugs became boring again. Reviews got shorter.
That’s not a downgrade. That’s progress.
Finally,
If your architecture needs a meeting to explain, it’s already failing.
If every feature touches half the codebase, something’s wrong.
And if deleting code scares you more than adding it? That’s your signal.
Let’s Argue (Politely)
I know some people will disagree. Good.
Tell me
- Where has simplicity failed you?
- When did abstraction actually save the day?
- What’s the most code you’ve proudly deleted?
Drop it in the comments.
Clap if this hit too close to home. Share it with that teammate who loves “enterprise patterns” a little too much.
And if nothing else — Save this for the next time you’re about to overengineer something perfectly simple.
메타데이터
- post_id
- 3fa0fd9c1ab8
- slug
- i-built-a-complex-web-app-architecture-then-a-simple-refactor-cut-it-in-half-3fa0fd9c1ab8
- url
- https://medium.com/@julias3/i-built-a-complex-web-app-architecture-then-a-simple-refactor-cut-it-in-half-3fa0fd9c1ab8
- canonical_url
- https://medium.com/@julias3/i-built-a-complex-web-app-architecture-then-a-simple-refactor-cut-it-in-half-3fa0fd9c1ab8
- author_url
- https://medium.com/@julias3
- status
- ok
- fetched_at
- 2026-06-14 11:28:49