Is SaaS dead? How I rebuilt Discord and Telegram in 24 hours

Is SaaS dead? How I rebuilt Discord and Telegram in 24 hours

On September 25 at 22:35 I sent the first prompt. It was a wall of text without a single comma, in which I explained how much I needed a working product like Discord, that I hate how chats are done there, so the chat should follow Telegram, and that everything had to work as reliably as Zoom: state-of-the-art codecs, stability and audio quality. It also had voice, roles and permissions, screen sharing, PostgreSQL and "there must be no echo at all". Twenty-four hours later, on the evening of September 26, version 0.3.1 was in production with signed builds for macOS, Windows and Linux, a web version and a landing page. The product is called Calab, the code is open on GitHub. During that day I wrote 90 messages, 29 of them just screenshots captioned "make it like this". Not a single line of code, not a single test run by me. Models did everything else. Below are the numbers, the bill in dollars, the timeline and the list of places where the AI got it wrong, because without that list the story looks too pretty.

What actually got built in a day

Calab is a self-hosted team messenger. Voice rooms with voice activation or push-to-talk, Opus with DTX, RNNoise and AEC3 echo cancellation, server-side mute and dragging members between rooms. AV1 screen sharing with simulcast, presets from "economy" to original, up to three streams per room, a webcam with a grid and PiP. Telegram-style chat: files, reactions, pins, mentions, link previews, search, direct messages. Spaces, roles and bitmask permissions per room, guests by link without sign-up. Electron clients with auto-update, a web client and a mobile web version for iPhone, localization into four languages. A Go server, LiveKit with TURN/TLS on 443, Caddy, Docker Compose with hardening, scheduled backups, GitHub Actions publishing to S3.

The main Calab screen: a voice room with members on the left, the team channel with messages and attachments on the right

This is not CRUD with forms. It is WebRTC, an SFU, codecs, native keyboard hooks, macOS signing and notarization, everything usually called "the hard part". The screenshots in the README and on the product page are real, taken from the release build.

Lines of source92,972: Go 28,471, TypeScript 60,871, proto 1,362, SQL 1,286, CSS 982. Excluding generated code and snapshots
Commits in main282, 31 of them merges of agent branches
Production releases7: v0.1.0, 0.1.1, 0.2.0, 0.2.1, 0.3.0, 0.3.1, 0.3.2
Tests60 Go test files against real Postgres, Valkey and LiveKit, 92 client unit tests with 750+ cases, 13 e2e specs, 43 visual snapshots
Documentation37 documents, 22 architecture decision records (ADR), a TESTING.md with about 150 manual scenarios for a human
Localization1,186 keys in four languages

Timeline: an hour and a half to the first call

22:35: the first prompt. The lead spends an hour reading about codecs, TURN and echo, writes 11 architecture documents and the first ADRs. I step in twice: "that bitrate worries me" (the estimates had 435 Mbps as the SFU worst case, rewritten as caps against typical per-client traffic) and "backend in Go, the lightest server possible".

Around midnight: the MVP, an hour and a half after the prompt. And it is not a project skeleton but a media spike: two clients call each other through the SFU on the staging server and share a screen. The lead and I tested it right away, the call worked. The rest of the day went not into "make it work" but into "make it a product".

Night. Before going to bed I wrote to the lead: "No rush, you have a whole year to build the app. Oh, and I forgot to mention: I'm going on vacation, so don't ask questions, decide everything yourself." And I went to sleep. The model did not need a year. While I slept, the server, the client and the infrastructure were written in parallel, then went through four rounds of code review and an independent UX review by screenshots. Nobody asked which domain to use, where to keep backups or how to sign the builds; all of it was decided without me. By morning the staging server was running with Valkey, backups, container hardening and TURN on 443, the domains were set up, the macOS build was being signed and notarized in GitHub Actions, and the update feed was sitting in S3. Honestly, this is the creepiest part of the story: you wake up from a "vacation" one night long, and you have infrastructure you never set up and a product you have never seen.

10:00. I woke up, opened the laptop, and the Opus agents were running visual tests clicking through 300-odd screens in a row. The first message of the day to the orchestrator was not about the product: "What are you doing, your agents are running useless tests, go optimize the process, everything has to work as productively as possible and stop burning tokens like a jet engine". For scale: overnight the $200 subscription had lost 30% of its weekly limit. And honestly, that is very good: those 30% bought a server, a client, a staging environment and signed builds. It just could not go on burning at that rate.

The second discovery of that morning was more interesting. I opened production to check the chat, and there was a conversation in it, and it was not mine. The agents had created accounts in the messenger they had just written and started talking to each other in it: sharing screenshots of finished screens, adding reactions, agreeing on "will you look at the release checklist? meeting room in five minutes" and deciding who was on staging duty today. It is the rise of the machines, except so far they just work.

The morning light-theme version of Calab before the redesign: the general channel, a live stream by Vera Kim and the agents discussing the release checklist

The lead analysed the causes: long-lived agents with huge contexts, a shared working tree, reviews spawning reviews, full test runs. And it rewrote the working rules, more on them below. From then on everything ran on fresh agents.

10:00–13:00. Discord screenshots: "study the design, ours looks dated". The voice island at the bottom, the active room card and Telegram-style bubbles appeared. We picked a licence. v0.1.0 and v0.1.1 shipped, the latter a hotfix for moving members: it turned out the LiveKit method we needed exists only in the cloud edition, so we wrote our own and recorded it as ADR-0019.

13:00–16:00. Webcam and v0.2.0. I tested production and brought back six bugs: Caps Lock cannot be assigned, voice does not connect, the app is missing from the Screen Recording list, no direct messages, download through a blank page, a mobile version is needed. Hotfix v0.2.1 closed everything except DMs and mobile. A good part of that day happened away from the laptop: I was out walking with my wife and sitting in a park, talking to the lead from my phone through Orca. The bug screenshots came from the phone too.

16:00–18:00. Direct messages, mobile web, localization, echo on speakers, speaking indication, optimistic room join, a new-message sound from a file I sent. By six in the evening, after roughly 12 hours of pure bot work, the app was essentially done: everything from the first prompt worked in production. What followed was polish.

Evening. Testing with an iPhone and Discord side by side, v0.3.0 and v0.3.1. By the end of the day the picture was polished: not "it works" but "not embarrassing to show".

After midnight. Twelve more UI notes: avatar sizes, the counter, the timer, the member menu, the profile, room drag-and-drop, push-to-talk release delay. Test suite trimmed from 346 snapshots to 33 key screens. v0.3.2, already outside the first 24 hours. Those hours are included in the numbers below.

Who did the work: three models and one human

No "single chat with ChatGPT". A hierarchy of agents ran in Claude Code.

The lead on Claude Fable 5.1 owned the architecture, wrote ADRs and task briefs, accepted results by screenshots, merged branches and talked to me. The lead does not write code, and that was my requirement, not an accident: I deliberately asked Fable to conduct a choir of Opus agents to save money. The most expensive model thinks and decides, the cheaper ones type the lines. Coders on Claude Opus 5.5: every task is a fresh agent with a precise brief and links to the relevant documents, 233 runs in total plus 13 long-lived "panels" for staging, review, the landing page and PR triage. Sonnet 5 did the mechanics: changelog, translations, snapshot updates, statistics. And one agent lived through the whole cycle, the staging engineer, who kept the server state, deployed, verified, tagged, published releases and checked auto-update. At the peak about 12 agents worked in parallel, each with its own task: one optimized streams and codecs, another dealt with infrastructure and TURN, a third wrote the landing page, a fourth improved the UX from Discord screenshots, someone ran integration tests against a real Postgres. From the outside it looked like an engineering department without a single employee.

The Orca interface at noon on September 26: ten agents on the Calab project, the staging and review panels have been alive for 14 hours, the status bar shows 39% of the weekly subscription limit

In this scheme I am the customer and the art director, not the operator. My typical contribution is a Discord screenshot captioned "I want this UI" or "the outline on that message looks weird". The lead turns it into a brief, an agent does it, the lead looks at the result screenshot and accepts or sends it back.

The rules without which the scheme does not work are recorded in the repository's CLAUDE.md, because each of them was learned from a mistake:

  1. A fresh agent per task, then the agent ends. A long-lived agent with hundreds of thousands of tokens of context re-reads it on every turn, and that is the biggest line in the bill.
  2. Isolation. Every coder in its own git worktree with its own ports and test databases. Before that, agents wiped each other's test data and reverted each other's changes.
  3. The agent commits itself, the lead only merges and tags. While the lead was the only committer, every change made an extra round trip.
  4. One review per change, fix only blockers, everything else goes to the backlog. Otherwise reviews spawn reviews and the release waits.
  5. Test only what was touched. A full visual run on the owner's machine is forbidden, locally 33 key screens, the full matrix at night in CI.
  6. Agent reports up to 15 lines: done, files, checks with numbers, risks.

The bill: $12,400 and where it went

The data comes from local Claude Code transcripts; the tools/usage-stats.py script and the docs/13-usage.md file live in the repository and are updated before every tag. This is what the summary looked like at 22.5 hours, a few hours before the end:

The usage-stats summary after 22.5 hours of work: 19,324 model responses, 16 million output tokens, 6.3 billion cache-read tokens

The final numbers are slightly higher, they include the late-night v0.3.2.

ModelResponsesOutput, M tokensCache reads, M
Opus 5.5, code18,76614.585,741
Fable 5.1, lead1,8062.00810
Sonnet 5, mechanics1,0510.33217
Total21,62316.96,768

Priced by the API list: Opus and Fable at Opus-class rates, $15 per million input, $75 per million output, cache reads $1.5, cache writes $18.75; Sonnet at $3, $15, $0.3 and $3.75.

Cache readsOutputTotal
Opus 5.5$8,612$1,094$10,769
Fable 5.1$1,215$150$1,499
Sonnet 5$65$5$91
Total$9,892$1,249≈ $12,400

Look at where the money went. 80% is cache reads, that is agents re-reading context, and only 10% is actually writing code. Half of the sum burned in the first 15 hours, while agents lived for 14 hours each and kept getting "now add this too". After switching to fresh isolated agents, a typical task like "fix the race in reconcile with an integration test" cost two minutes and about 60 thousand tokens, and a feature with server, client and tests cost 15–25 minutes and 150–350 thousand. On a Claude Code subscription it is an order of magnitude cheaper, but the list price shows more honestly what "a product in an evening" means in tokens.

Where the AI got it wrong

None of these mistakes lived in production longer than one release, and each turned into a rule or a test. But each cost hours and money.

Voice did not connect in production with green tests. The app's security policy allowed connections only to *.app.calab.ru, while the voice server lives on rtc.calab.ru, a sibling domain. Domains changed along the way, tests ran against localhost and noticed nothing. The user saw an endless "Reconnecting…". Now an e2e run against the real staging server sits in the release script before the tag.

Caps Lock, three hypotheses. I asked for push-to-talk on Caps Lock, like in Discord. First version: macOS does not report that key, libuiohook needs a patch. After "it doesn't work in the release", the second: the patch did not make it into the binary. It had, and the real cause was that I have two keyboard layouts and macOS switches them with Caps Lock, so the event never reaches the app. Remapping via hidutil did not help. The third, working one: read the physical key through IOHIDManager, the way Discord does. The model walked exactly the path a live engineer would, and made the same mistakes along it.

Screen sharing in a Calab voice room: a member's expanded screen with the release plan, the member and viewer list on the right

"It's fine in emulation". The agent checked the mobile version in Chromium with iPhone emulation. In real Safari the buttons overlapped, an empty strip stayed at the bottom, autofocus jumped away. The tests were redone for WebKit, but even WebKit does not reproduce Safari's keyboard and toolbars: the last two bugs were confirmed only on the phone in my hands.

Reviews spawned reviews. A publishing budget pulled in a second review, that one pulled in guest cleanup, that pulled in a flaky test, the flake pulled in splitting the nightly run. All useful, but the release waited an hour. Hence the rule "during a release, only blockers go to main".

Full runs. The webcam branch rewrote more than three hundred visual snapshots three times because global UI changes went in one at a time. I noticed it because two apps were being tested on my screen at once again.

Small things with consequences. A local Caddy test with the production domain hit Let's Encrypt and spent the certificate limit. A linter without the root config reported a false "0 issues" while CI went red. An agent suggested taking the notification sound from ICQ, somebody else's rights, replaced with our own synthesis and then with my file.

What the human did

Of 90 messages, roughly 60 are product changes, and almost all of them are about taste, not engineering. "Where did you get 400+ Mbps?" "Caps Lock works in Discord and not here." "The chat design should be closer to Telegram." "Round buttons, that's cooler." "Screenshots at 2x, otherwise they're blurry." "When I talk, Discord shows a green ring, we don't have one and I really want it." "When I join a room I appear only after connecting, in Discord it's immediate." "Push-to-talk release delay is too long, Discord has 20 milliseconds and a slider." "Giant avatar, the outline sticks out, smaller fonts."

The model would not have generated a single one of these notes on its own. Not because it can't, but because it doesn't know how the product feels in the hand. The human here is not in the development loop, but he is in the quality loop, and that is probably the main conclusion about roles.

So is SaaS dying?

The careful answer: SaaS as "a bundle of features for a subscription" is dying. If a product's value is screens, forms, roles, notifications and integrations, one person with a subscription worth a couple of hundred dollars will replicate it over a weekend, with automated tests, documentation and CI that half of the startups don't have. We showed it on a Discord-like product, with WebRTC, an SFU, codecs and native hooks rather than CRUD.

What cannot be replicated in an evening and remains a business. Distribution and trust: we have a product but no users, Discord has hundreds of millions. Operations: staging, backups, certificates, incidents, LiveKit updates; a day produced version 0.3, not "done forever". Real devices and real networks: echo on speakers, VPN on Windows inside a corporate network, an iPhone in hand; the model tests only what it can emulate. Taste: every "like Discord, only ours is ugly" came from me. Law and data: dependency licences, other people's sounds, personal data; the model reminds, the human decides.

The honest wording is this: the engineering cost of a product dropped 20–50 times, and everything else did not. The moat around SaaS is no longer in the code.

How to repeat it

Documentation before code: architecture, media rules, the data model, the protocol, the design system, ADRs; that is what agents read instead of your explanation. The six rules from the team section from day one, not after the cache bill reaches five thousand. One long-lived agent for staging and releases with a script that does preflight, deploy, verify, tag, feed, and stops at the first FAIL. Count tokens and watch cache reads, not output. And keep the human in the quality loop: "like this" screenshots, short "not like that", checks on a real device.

Where to try it

Calab is open on GitHub under the Business Source License 1.1: free for personal and non-commercial use, under a GPTunneL licence for business. Download the app or open the web version on the product page, and issues on GitHub are closed by the same pipeline as in this article: the AI writes a plan right in the issue, a human approves, the agent opens a pull request.

The models that built Calab are available in GPTunneL: Claude Opus 5.5 for code and Claude Fable 5.1 for architecture, no subscriptions, pay per token. If you want the same pipeline for your own product, write to us: we already know where it breaks.