So true, I find that in a lot of cases the humans are overly prescriptive. If they just tell the agent to discover what is there first and recommend me what to do it will most of the time recommend something good. They instead just say, without looking into what is already there and the possible choices that led us there, "I know exactly what I want, build XYZ make no mistakes" and the agent doesn't push back enough unfortunately.
we've mostly gotten past the slop issue in this new world of working together with AI
the next problem is people keep accidentally undoing functionality
this is happening because everyone is more in everyone else's business. hit an issue? fix it right away without nagging anyone
the problem is it's unclear how intentional the current behavior is and what absolutely should not be rolled back
you can say tests should be this. but they don't capture the fact that you discarded seemingly valid solutions 1-4 and settled on 5. someone later (an agent) might thing "oh 3 is simpler"
any additional document also doesn't feel like it solves the problem because it's yet another thing that can drift
where is the source of truth for how your software is supposed to work?
Over the last 30 days, Lovable Realtime sent 1.11 TB of application payload. If we'd sent every result as a full snapshot, we estimate it would have been around 40 TB.
Three optimizations account for the difference:
1. Don't send what didn't change. A change in the database doesn't always change what a subscriber sees. Maybe it touched a field they can't read, or only bumped a timestamp. So we recompute their authorized view, fingerprint it, and compare. 88.4% of the time nothing had changed, so we sent nothing.
2. Send a patch, not the whole thing. When the result did change, we send a patch if it's smaller than a full snapshot. New subscriptions still got the full snapshot. Patches cut the payload volume of changed updates by 94.5%.
3. Don't resend what the client already has. When a client reconnects, it tells the server the fingerprint of the state it's holding. The server rechecks authorization and loads current data. If the fingerprints still match, it replies with a tiny "resumed" frame and the client keeps what it had. On top of the first two, this saved another ~50%.
All in, that's about a 97% reduction. 💪
I built a small simulator so you can watch it happen. Change a field, revoke a permission, or trigger a deploy, and see exactly what goes over the wire:
While working on Lovable Realtime, I found a gap in the Firestore change events we consume. For a delete, we have the document’s last update time, but not the deletion’s commit timestamp.
Events can arrive out of order. Using delivery time could let a late delete remove a document that’s already been recreated.
My first thought was to add a nanosecond to the last update time. But that is implicit and can be lost to accidental rounding.
Instead, I added a logical counter. A document last updated at (T10, 0) gets a synthetic delete at (T10, 1). It sorts after that revision and before a recreation at a later timestamp.
That handles ordering revisions of the same document. It doesn’t settle ordering against reads.
Suppose a document was updated at T10, read at T20, and deleted at T30. The synthetic delete (T10, 1) sorts before the read, even though the deletion happened afterward.
So the type also tracks the source, read or revision, and a domain defining which timestamps we can compare. Different DBs provide different guarantees, and we don't wanna compare across DBs.
If the deleted revision’s timestamp is at or after a comparable read timestamp, we know the deletion came after that read. If it’s earlier, the order is unknown.
There’s now a separate method for ordering revisions. Comparing a synthetic delete against a read can return “unknown,” which the service handles with a fresh strong read.
The counter orders revisions. The explicit “unknown” keeps us from treating that order as something it can’t tell us.
If you’ve handled missing delete timestamps, how did you resolve the ambiguity?
One of the things that motivates me most working at Lovable is lowering the barrier of entry into building software. Previously you had to learn so much before you could build anything real. Now you can dive in head-first and start building real software, and learn a lot in the process! Lovable is a great learning tool if you wanna get into building software!
I keep seeing the same failure mode with coding agents. Someone describes a whole feature, walks away, and comes back to 20,000 lines of code that no one can meaningfully review. Buried in there are hundreds of small decisions the agent made alone, decisions you used to walk over and settle with your PM or another engineer. So now you have a system you don't really know. I think of it as cognitive debt, and codebases carrying enough of it turn into monsters you can't evolve past a certain point.
The way out is old and boring: work in small steps. Cut the feature into pieces that each do something real for a user, ship a piece, learn from it, decide the next one. Each piece stays small enough that you can follow what the agent is doing and catch the decisions that matter before they compound. This is not one-thing-at-a-time, either. You can run many of these loops in parallel, and the size of your steps is what decides how many you can supervise without losing the plot.
People hear small steps and assume it trades away speed. What it trades is scope, and only for a while. That was always the real tradeoff hiding inside speed vs quality.
Earlier this summer the Lovable frontend quietly stopped using Firestore. It now runs on our own realtime engine one engineer built in 40 working days.
The engine queries and composes data from the databases we already had, fully reactively — deltas pushed to every subscriber the moment their data changes. Every payload is shaped in our backend per the caller’s permissions, so fields you’re not allowed to see never leave the server. And even authorization itself is reactive: revoke someone’s access and their stream closes mid-session.
Today it peaks at ~1M concurrent subscriptions with 99.9% availability, and it rolled out to every user with zero incidents.
How did we move fast? A serious test rig from day one — unit, integration, e2e, and smoke tests. That’s what let us move fast without breaking things.
The human provided architectural specs. The agents did the coding.