Last quarter, I deleted 48% of our backend code.
No new bugs.
No outages.
No customer complaints.
And no one noticed.
That was the moment I realized something uncomfortable:
Most code exists to make developers feel safe, not to make products better.
The Backstory
We had a “mature” codebase:
- Service layers
- Repository patterns
- DTO transformers
- Event emitters
- Abstractions for “future scale”
- Utility folders with 200+ helpers
It looked impressive.
It was also slowing us down.
Every new feature required:
- Touching 6 files
- Updating 3 interfaces
- Writing adapters for edge cases that never happened
- 2 days of PR discussions about naming
Velocity was dying quietly.
The Experiment
I asked one simple question:
If we were rebuilding this feature today, what would we NOT build?
Then I started removing things.
Not refactoring.
Deleting.
What I Removed
1. Generic Abstractions
We had abstractions for:
- Payment providers (we only use one)
- Notification services (we send one type of email)
- Caching strategies (we use Redis. Period.)
Removed all generic layers.
Result: nothing broke.
2. Premature Microservices
We had split logic into separate services because:
“We’ll scale one day.”
Traffic never justified it.
Merged them back into a single service.
Latency improved.
Deployment got simpler.
No scaling issues.
3. “Reusable” Utilities Nobody Reused
Half the helpers were used exactly once.
Inline.
Delete.
Move on.
The Metrics
Before cleanup:
- 220k lines of backend code
- 14 services
- Avg feature cycle: 6–8 days
After cleanup:
- 114k lines of backend code
- 6 services
- Avg feature cycle: 3–4 days
Bug rate: unchanged.
The Real Problem
We overestimate:
- Future scale
- Hypothetical flexibility
- Edge cases that never happen
We underestimate:
- Simplicity
- Readability
- The cost of cognitive load
Every abstraction has rent.
Most teams forget to calculate it.
Why This Happens
Developers are rewarded for:
- Cleverness
- Architecture diagrams
- Pattern knowledge
- “Senior-level” structure
We are not rewarded for:
- Deleting code
- Simplifying systems
- Saying “we don’t need this”
So we optimize for visible sophistication.
Not operational clarity.
The 3 Rules I Follow Now
1. Build For Current Reality
If the scale isn’t here, don’t architect for it.
When it arrives, you’ll have better information anyway.
2. Duplication Is Cheaper Than Abstraction
Until duplication becomes painful.
Then refactor.
Not before.
3. Measure Complexity Like a Cost
Every new layer adds:
- Onboarding time
- Debugging time
- Cognitive overhead
- Review friction
If it doesn’t reduce real risk, it’s expensive decoration.
What I Learned
The most valuable code I wrote last year was the code I deleted.
And the best architecture decision was admitting:
We are not Google.
Most startups and teams don’t need:
- 15 services
- Hexagonal architecture
- Event-driven everything
- Enterprise-grade patterns
They need clarity.
The Hard Truth
A lot of “clean architecture” is career-driven design.
It signals seniority.
It feels advanced.
It looks good in interviews.
But production systems reward boring decisions.
Final Thought
If your system disappeared tomorrow and you rebuilt it in a week…
What would you NOT rebuild?
Start there.
Curious:
How much of your current codebase would survive a rebuild?
Top comments (4)
It is amazing how often abstractions are built for scale that never arrives.
Great reminder that boring solutions often win in production.
I think many developers need permission to simplify. Posts like this give that permission.
I have seen teams stuck in endless PR discussions because the architecture was too layered. Simpler systems reduce friction.