I've read Joel Spolsky's essay about Netscape. I know the standard advice — don't throw away working software, refactor incrementally, strangle the old code. I knew all of this before I opened my terminal and typed laravel new vlinqr.
I did it anyway.
How it started
Vlinqr started as a concept project. A multi-tenant SaaS platform for online stores — something I was building in my free time to practice things I wanted to get better at. Tenancy isolation, subscription billing, AI-powered search. No roadmap. No launch date. Just a playground.
The problem with playgrounds is they grow. The idea started to feel complete. Features connected. The product made sense. So I decided to actually launch it.
That's when I looked at the codebase and felt sick.
I was using the Action pattern. The code wasn't terrible by any objective standard. But months of building without a plan had left everything overlapping. Business logic bled between actions. Things that should have been isolated weren't. I couldn't add a feature without touching three other things that had no business being involved.
I sat down to write a roadmap. Every feature I wanted to add before launch felt like surgery on a patient with no charts. Not because the code was broken — it worked. But the structure made expansion painful in a way I couldn't ignore.
The standard advice (and why I ignored it)
I know what you're supposed to do. Refactor incrementally. Strangle the old code. Don't throw away working software.
But every article about this topic is written from the perspective of teams. Companies with engineers, budgets, stakeholders. The advice assumes you have resources to maintain two systems in parallel while you migrate.
I'm one developer. Working on this between client projects. The calculus is different when it's just you.
So I did what any sane developer would do. I asked an AI for its opinion. Told it I wanted to go with a modular monolith architecture. The response was polite but direct — basically: if the project works, focus on launching it. Prove the idea first. Refactor later.
I pushed back. Opened my terminal. laravel new vlinqr.
What a feeling.
Four perfect days
New project. Clean slate. I set up PHPStan at maximum level. Rector for automated refactoring. Pint for code style. Forced 100% test coverage from the first commit. If I was going to rewrite, I was going to do it right.
You know the feeling. Every file in the right place. Every class has one responsibility. Tests pass on the first run. The dopamine is real.
For four days, I was in love with my code.
Then I did the math. At my current pace, the rewrite would take four to six months. The architecture would be perfect. The test coverage flawless. The code a work of art that nobody would ever use.
Because I know myself. After a week — maybe two — I'd get tired. The excitement of a clean codebase doesn't survive month three. This project would end up in the same dusty folder as my other brilliant ideas for how I'd become a millionaire. Perfectly architected, never launched.
The part nobody writes about
Every rewrite article frames the risk in business terms. Market share. Customer disruption. Revenue loss. Those are real risks, but you can model them, argue about them, make projections.
The risk for a solo developer on a side project is different. It's motivation. And you can't model that.
I've abandoned projects before. Not because they were bad ideas or because the code was broken. Because the distance between where I was and where I wanted to be felt infinite, and the energy to cross it ran out. A rewrite doubles that distance. You're not moving forward — you're rebuilding ground you already covered, just cleaner.
The rewrite doesn't fail because the new code is worse. It fails because you never finish it.
Going back
I admitted defeat. Went back to the same AI agent and asked for a real plan. The answer was embarrassingly simple:
- Any new feature goes into the modular design.
- Refactor small, isolated features first.
- Write the tests that are missing.
That's it. No grand migration strategy. No architectural astronautics. Just three rules.
I started following them. And slowly, things changed.
I drew up architecture guidelines. Wrote conventions for where things belong. Set up AI agents with those guidelines so they could flag coupling, identify what should move, suggest what to rewrite versus what to leave alone.
PHPStan adoption started at level 1, not max. Every week, I bumped it up. Level 2. Level 3. Each bump caught things I hadn't seen, but the codebase was ready for it by then. Rector cleaned up patterns automatically. Test coverage grew step by step — not 100% from day one, but stronger every commit.
The coupling I'd been scared of started dissolving. Not all at once. One module at a time. Extract a service. Move a query. Wrap a boundary. Each change small enough to finish in one session.
What happened
By the end of the first week, something unexpected happened. I started to appreciate the old code. Not because it was beautiful — it wasn't. But it worked. It had handled edge cases I'd forgotten about. It had months of real decisions baked into it, the kind of knowledge you lose when you start from zero.
The codebase still needed work. It still needs work now. But it was a working product, adopting my standards step by step, and I could add new features without dreading it.
Vlinqr launched two months ago. Multi-tenant architecture, subscription billing, AI-powered search, bilingual Arabic and English. A production SaaS that real people use.
If I'd stuck with the rewrite, I'd still be writing tests for a project nobody had ever seen.
What I actually learned
The biggest risk for a solo developer isn't bad architecture. It's never shipping.
Every developer I know has the instinct to rewrite. We read messy code and our first thought is "start over." That instinct gets stronger when you have the tools to do it well. PHPStan, Rector, Pest, modern Laravel — they make a clean rewrite feel so achievable. And it is achievable, technically. But "technically achievable" and "will actually get done" are two different questions.
Refactor what you have. Launch it. Test it in the real world. Let the architecture evolve under the pressure of actual users doing actual things. The codebase won't be perfect. It doesn't need to be. It needs to exist.
I share more like this here: Naram Alkoht
Top comments (0)