One Flask todo app, built twice. Replacing its heavy dependencies with small, AI-written, purpose-built code cut the third-party production footprint from 216,727 to 32,654 lines of code (LOC), an 85% reduction. A shared test suite held behavior identical across both builds, and the security-sensitive libraries stayed with the experts.
Measured 2026-07-15 with cloc 2.06, production scope
(code that ships to prod; dev/test toolchain excluded).[1]
Full method, per-step diff trail, and
the argued stopping point in research notes.
flask remainsWhen code was expensive to write, pulling in a general-purpose library was usually the rational choice. You got a working solution cheaply, and accepted the costs: unknown maintainers, often far more code than you need, and a "good enough" fit to your actual problem. When AI can produce a narrow implementation on demand, and a test suite can hold it in place, it's worth asking dependency by dependency whether it is worth the trade-off to borrow versus build. For some, owning a small, purpose-built module you can read end to end turns out to be the better deal. How many is a dial, not a doctrine. Your app, your team, and your risk profile set it.
The example web app is a Python full-featured Flask + SQLite + login todo app, kept in two behavior-identical variants: a baseline on the conventional Python web stack, and a limited variant whose heavy dependencies were replaced one at a time. A shared black-box test suite, byte-identical across both, is the regression net each swap had to pass.[2] Behavior is held constant, so the only difference left to measure is how much third-party code each variant ships.
Every number on this page comes from cloc, an open-source line counter. Point it at
a directory and it walks the tree, works out each file's language, and reports blank, comment,
and code lines separately. The figures here are the code column only, counted against a
production-only virtualenv built from the lockfile.
Third-party production code, before and after
Lines of code that ship to production, excluding the dev/test toolchain. The same app, the same tests passing, 85% less dependency code underneath it.
SQLAlchemy alone was 61.8% of all shipped dependency code: 133,937 lines
for an app with two tables and a dozen simple queries. Email validation cost another ~39k: the
768-line email-validator drags in an entire IDNA + DNS stack (idna 19,389
and dnspython 19,300) to format-check a string. Together, the ORM and the email stack
are 80% of everything that ships. In this app, the framework glue was
never the bloat. The engines were.
What five lines in pyproject.toml actually install
Left: the dependency list as a developer sees it. Right: the 16 packages it resolves to in production, sized by lines of code. Line thickness tracks each declaration's share of the footprint.
cloc run. The 16
packages sum to 216,669; 58 LOC of stray vendored files, attributable to no single
package, complete the 216,727 baseline.Email validation is the cut I'd call a judgment call rather than a clear win. What's
legal per the RFC grammar is far broader than what most apps want to
accept, so shedding the ~39k-line stack hands you the job of drawing that line. It cuts both
ways: the limited variant went deliberately narrower than the spec,
then quietly accepted all-numeric TLDs like user@example.123 until the shared
suite caught it.
Owning a small validator buys precise control, and cheap code includes cheap tests. If you'd rather not carry that, the expert-maintained library remains a good answer.
Each removal replaced one library with purpose-built first-party code and landed as a single git commit. A step only counted when the unchanged test suite passed afterward. Plotted as a running total, the descent belongs almost entirely to those two engines: the last three steps together shed about 4% of what the first two did. By then, diminishing returns made stopping the easier case to argue.
Third-party LOC still shipping after each removal
Running total after the dependency named below each column was replaced. The final column (green) is the shipped limited variant.
The stopping point deserves as much attention as the cuts. The one declared dependency left is
flask; everything still shipping is Flask's own transitive core. Most of it is the
kind of code I'd leave with the experts: password hashing
(werkzeug.security, required anyway), autoescaping as the XSS
defense on every template, and cookie signing that the first-party CSRF and
remember-me modules deliberately ride on rather than reinvent. In this experiment, AI-written
replacement earned its keep against oversized general-purpose engines whose behavior a test suite
can pin down. That is a much narrower claim than rewriting the platform or the
cryptography.[3] The numbers here show what each swap
cost and saved. How far to take it in your app is your call.
What one line in pyproject.toml still installs
The limited variant's entire dependency list. It resolves to Flask's own transitive core: seven packages, 32,596 LOC, none of it rewritten. Bars are on the same scale as the baseline figure above.
cloc run. The 58 LOC of stray vendored files
noted above ride along with these seven packages, bringing the shipped total to 32,654.Every number below comes from re-running the same production-scoped cloc measurement
after each commit. ~409 lines of dependency code shed for every first-party line
added. The 450 lines added are small enough to read in one sitting. The 184,073 they
replaced were not.
| Step | Dependency removed | 3P shed | 1P added | 3P total | 1P total (app/) |
|---|---|---|---|---|---|
| 0 | — (baseline clone) | — | — | 216,727 | 964 |
| 1 | SQLAlchemy, Flask-SQLAlchemy (+ typing_extensions) | 137,490 | +100 | 79,237 | 1,064 |
| 2 | email-validator (+ idna, dnspython) | 39,457 | +21 | 39,780 | 1,085 |
| 3 | Flask-WTF | 520 | +39 | 39,260 | 1,124 |
| 4 | WTForms (+ i18n catalogs) | 5,923 | +195 | 33,337 | 1,319 |
| 5 | Flask-Login | 683 | +95 | 32,654 | 1,414 |
| Total | 184,073 | +450 | 32,654 | 1,414 |
Side story: the same trade
exists in dev tooling. python-dotenv (766 LOC, local-only .env loading;
prod injects real env vars) is a dev dependency here, and the limited variant still swapped it for
a 12-line first-party loader. Dev deps can shrink too, though selectively. I'm not about to
replace pytest.
None of this says third-party dependencies were a mistake. Flask is still here, and so is every library doing work I'd rather not own. What moved is the cost of the alternative: "add the dependency" used to be the cheap answer almost by definition, and for a few of these it stopped being. The 85% came from two decisions rather than from discipline. An ORM for two tables, and an RFC-complete validator for a format check.
The practical version is smaller than the headline. Look at your own footprint the way
cloc sees it, find the general-purpose engines you're using a sliver of, and price
the purpose-built version now that writing it is cheap. A few will be worth owning. Most won't
be, and the ones holding your passwords and your session cookies are the last place I'd start.
The part I'd stress hardest is the tests. Every removal here was safe because a byte-identical black-box suite, cheap to write, could tell me the app still behaved the same afterward. Without that regression net, you have no evidence a swap preserved behavior. Without it, you're replacing working code and finding out in production.
This is one small app, measured once. A larger team, an older codebase, or compliance requirements I don't have would each argue for keeping more of the stack. My claim is narrow: writing the replacement yourself got cheap, so the build-versus-borrow call is worth making again, one dependency at a time.