DISCLAIMER: The stats in this post (including the title) were calculated by Claude based upon my git history.
How It All Began
This started as a $work hackathon project — the kind where you get a few days to prototype something and see where it goes.
The original idea was simple: build a UI to replace an unmanageable spreadsheet we were using for testing. Standard CRUD stuff really — forms, tables, the usual enterprise fare. Being 2025, and with “vibe” very much the trend, I naturally looked at the latest and greatest models and settled on Claude Opus to see what it could do.
The plan wasn’t to build just another CRUD app, but to apply a few modern practices along the way: real-time updates via WebSockets (entirely unnecessary for the use case), proper keyboard navigation (who actually uses that?), and full PWA and SPA capabilities — because why not over-engineer things for fun. All of this while writing very little code myself and mostly just directing the train.
Then something clicked.
As I wired up the WebSocket connections, built modal dialogs, and implemented import and export logic, I stopped seeing a spreadsheet replacement. Instead, I started seeing the shape underneath it — the patterns every CRUD application needs but nobody wants to build from scratch. The real-time sync layer. The keyboard shortcut manager. The accessible focus trapping. The LLM's code was not actually bad.
I began to see what this could be.
So I started pulling those patterns out. “Lets just abstract this bit,” I told the LLM. “It’ll make the hackathon code cleaner.”
Then I abstracted another bit. And another. Then I added a custom virtual DOM, because why not, it took about an hour. I benchmarked it against direct DOM manipulation and promptly decided it was pointless in most cases.
The hackathon came, I talked a lot about the security aspects i had implemented with LLM, and then I stopped building a spreadsheet app and started building the framework the spreadsheet app wanted to exist on. The hackathon project became the basis of the Funky framework.
What began as “let me add unnecessary WebSockets to a CRUD app” has so far turned into a JS 'framework' which consists of 74 components, 35 core modules and a custom testing framework running over 8,800 tests in real browsers.
The Numbers That Don't Make Sense
I debate to myself whether what I have built is good or not, but let me start with the stats that broke my brain:
| Metric | Value |
|---|---|
| Timeline | December 4-28, 2025 |
| Total commits | 195 |
| Lines added | 1,297,591 |
| Lines deleted | 490,698 |
| Net lines of code | 806,893 |
| JavaScript | 266,551 lines |
| CSS | 46,670 lines |
| Components | 74 |
| Core modules | 35 |
| Documentation files | 127 |
| Test suites | 858 |
| Individual tests | 8,800+ |
Yes, I spend a lot of time on my computer, while I’ve also been doing actual work during that time. Those that have used an LLM know you can't just let LLM loose on everything as it's not magic... however..
Using the industry standard of ~50 lines of tested, documented code per developer per day, this represents approximately 16,138 person-days of work. That's 64.5 person-years. A team of five developers working full-time would need 13 years to produce this.
Opus came up with those stats but all of this in 24 days....
The No-jQuery Philosophy
Here's a controversial opinion: you don't need jQuery. You don't need React. You don't need Webpack, Vite, or Babel. You definitely don't need npm install downloading half the internet just to render a button.
Funky is vanilla JavaScript all the way down. The development workflow is revolutionary:
- Edit a file
- Refresh the browser
That's it. No build step. No hot module replacement. No watching your terminal for five minutes while webpack does... whatever webpack does.
"But vanilla JS is verbose and painful!" I hear you say.
Is it though? Here's how you create a toast notification:
Funky.Toast.show('Hello world', { type: 'success' });
And here's how you build a full data table with sorting, filtering, pagination, keyboard navigation, and accessibility support:
Funky.Table.init('#my-table', {
data: myData,
columns: myColumns,
pageLength: 25,
selectable: true
});
At times this seemed too easy, I mean re-writing DataTables in a day. I'm fairly sure the original took a bit longer than that. Whether it is robust is another story I have not used any of this code in a production environment. I do however have working examples and I don't see many errors in the logs... It's just javascript.
The Perl
Yes, the backend is Perl.
Yes, Perl. The language everyone assumes is dead but keeps going up in the tiobe list.
The camel abides.
I'm using Mojolicious for the web framework, Minion for background jobs, and OpenAPI::Modern for API validation. It wouldn't be possible without them. I have just glued it together and added JavaScript so far.
I have lots more planned in the future but for now my focus has been with JS.
PSPWA
PSPWA stands for Progressive Single Page Web Application.
The architecture:
- Progressive: Works offline, installable, push notifications
- Single Page: No full page reloads, client-side routing
- Web Application: Not a document, not a website, an application
And it all runs without a compiled JavaScript framework. Just ES5, some discipline, and a lot of Claude conversations.
8,800+ Tests (In Real Browsers)
"You can't test vanilla JavaScript properly," said nobody who actually tried with LLM.
The test runner executes in real browsers, not Node.js pretending to be one. No jsdom. No happy-dom. Actual Chrome, actual Firefox, actual Safari. When we test keyboard navigation, we're testing real keyboard events. When we test focus management, we're testing real focus.
The testing philosophy is simple and entirely written by the LLM:
FunkyTests.describe('ComboBox', function() {
FunkyTests.it('should open dropdown on click', function() {
var combo = Funky.ComboBox.init('#test', { items: items });
TestUtils.click(combo.element);
expect(combo.isOpen()).toBe(true);
});
});
You can of course run the tests under a headless browser for CI, but we recommend running them in real browsers during development to catch quirks early. I have a perl script which does this via WWW::Mechanize::Chrome.
The Learning
Here's what I learned from directing 800,000 lines of code in 24 days:
1. Velocity without direction is just chaos. Claude can write code faster than any human. But without clear architecture and ruthless monitoring, you end up with a mess.
2. Tests are not optional. When you're moving this fast, tests are the only thing preventing complete disaster. Git helps aswell.
3. Documentation that lies is worse than no documentation. We caught Claude documenting features that didn't exist. Now we verify everything and/or force it to implement the feature they describe. Trust, but verify.
The Tagline
"Write less code. Ship more features. Sleep better at night."
That's the promise. Whether we're going to deliver on it... ask me in another 24 days.
If you would like to learn more about funky, visit the official website. This will be open source but my vision is not yet finalised. I will keep you posted.
Top comments (0)