A side-by-side experiment
PHP single file
vs JavaScript stack
Same todo app, built twice. Magic-link auth, projects, due dates, five views.
Identical UX. One version is 621 lines in a single file. The other is 1,275 lines
across five files using Bun, Hono, React 19, Drizzle, Vite, TypeScript, and
Tailwind via CDN — the same Tailwind setup as the PHP version.
Live: todo-app-php ·
todo-app-js.
The numbers
| Metric |
PHP |
JS |
| Lines of code | 621 | 1,275 |
| Files (code) | 1 | 5 |
| Dependencies | 0 | 78+ direct (379 in node_modules) |
| Build time | 0 s | ~10 s |
| Deploy time (from push) | ~5 s | ~30–60 s |
| Docker image size | ~30 MB | ~150 MB |
| Memory at runtime | ~20 MB | ~140 MB |
| First paint | 1 round-trip | 3 round-trips |
| Cold start | <100 ms | ~500 ms |
PHP single file
no framework · no build · 1 file
Pros
- No build step
- No dependencies
- Deploy =
git pull
- Iteration: edit → save → refresh
- Runs on $1/mo shared hosting
- No npm audit, no supply chain attacks
- One mental model (1 file)
- Junior dev gets it in an hour
- No dependency upgrades that break the app
- Zero state management bugs (everything in DB)
- Always fresh data (full reload)
- SEO friendly out of the box
- 7× less memory, 5× smaller image
- Pieter Levels makes $2M+/year this way
Cons
- No type safety
- Hard to test (global state, refactor first)
- Page reload on every action (300–500 ms blink)
- No optimistic UI
- Migrations run on every request
- Without a framework, larger apps fall apart
- No free API for mobile clients
- Single-threaded
php -S for dev (need php-fpm for prod)
- Levels.io style has "amateur" reputation
- Large refactors mean grepping everywhere
- Hurts past ~3,000 LOC
- No autocomplete for DB row fields
JavaScript stack
Bun · Hono · React 19 · Drizzle · Vite · TypeScript
Pros
- Type safety end-to-end (Drizzle generates types from schema)
- Optimistic UI, drag & drop, animations
- Real-time features easy (WebSocket, SSE)
- REST API for free (mobile, browser extensions can consume it)
- Smooth UX, no page reloads
- More testable (clear server/client split, pure functions)
- Migrations run once on boot
- IDE autocomplete for every DB field
- Safer large refactors
- Bun is a fast runtime
- React has a massive community
- Same language frontend and backend
- Scales better for teams
Cons
- 2× more code for the same functionality (after dropping custom CSS)
- Build pipeline (Vite, esbuild, plugins) = more things that can break
- Dep upgrades break things (React 19, Tailwind 4, Vite 7 — all breaking)
npm audit + Dependabot maintenance overhead
- 80+ packages = 80+ maintainers you have to trust
- 7× more memory
- 5× larger Docker image
- Deploy needs build step + container restart
- 3 round-trips before first paint
- 8 useState hooks in one component → state bugs
- HMR sometimes drops, restart the dev server
- Onboarding a new dev takes hours (Node? Bun? cache?)
- Shared hosting unusable — VPS minimum
- SPA has worse SEO without SSR
- Sometimes fight TS with
as or ! to silence the compiler
Why 3 round-trips on JS?
A single-page app can't render the first useful screen until it knows
who you are and what to show you. Those two questions are
sequential — you can't ask the second until the first returns.
JS waterfall
0 ms GET /
→ empty <div id="root"></div> shell
50 ms parse HTML, fetch /assets/*.js + *.css
250 ms React mounts, useEffect fires
GET /api/auth/me — "who am I?"
350 ms auth resolves, next useEffect fires
GET /api/state?view=today — "what should I show?"
450 ms USER SEES UI
PHP timeline
0 ms GET /
→ server reads session, queries DB, renders full HTML
100 ms USER SEES UI
PHP is fast because the server already knows everything. The browser only
has to draw what arrives in the first response. SPAs can match this with
SSR + streaming + inline state — but that's hundreds of lines of additional
setup that you get for free with server-rendered HTML.
When to pick which
Pick PHP single file when
- You're a solo developer
- It's a side project or hobby
- You want to ship an MVP in a week
- Indie SaaS with low–mid traffic
- Internal tools, admin panels
- Hosting on the cheapest VPS
- You want to ship today
- Minimal maintenance for years
- "Form submit + reload" UX is acceptable
Pick JavaScript stack when
- Team of 2+ developers
- Planning a mobile app over the same API
- Real-time or collaborative features
- Drag & drop, rich UI interactions
- App with a 5+ year lifespan
- UX matters (animations, instant feedback)
- You have time and energy for the dep tree
The JavaScript stack solves problems most solo projects don't have.
PHP single file is a tool proportional to the size of most projects.
Pieter Levels isn't right because "PHP is best" —
he's right because he picks tools by project size,
not by hype.