On Saturday, a user opened a Discord ticket. They had paid for Gibwork Plus through the Gibwork Android app. The payment had gone through and Google Play showed an active subscription, but Gibwork hadn't unlocked their membership.
Not exactly the kind of message you want to wake up to.
This is the kind of bug that gets your attention immediately. Someone trusted your product enough to pay for it. The money left their account, Google confirmed the purchase, and our system failed to deliver what they had bought.
We replied to the user and started investigating. At that point, all we knew was that the payment had worked and the product had not. Somewhere between those two events, the system had lost the user.
I thought this was going to be another story about fixing a production bug. Instead, it reminded me that the most valuable thing AI gives me today isn't the ability to write code—it's the ability to understand complex systems much faster.
Debugging has changed
I've been coding with AI since ChatGPT launched. I started with GitHub Copilot autocomplete, moved on to asking chat models for small functions, and eventually reached the point where most of the code I write begins with an AI assisted conversation. I've gone from accepting single line tab completions to exhausting my 5hr usage limit with just one prompt.
A significant part of Gibwork has been built with Claude, GPT, Codex, or whichever tool made the most sense at the time. Writing code with better models is fun, but raw code generation is no longer the part that excites me most. This subscription issue made that especially clear.
Following the user through the system
I pulled the production logs, grabbed the failed RevenueCat webhook payload, and gave Codex the relevant code from both our mobile app and backend. It followed the identity flow across the app, RevenueCat, the backend, and the database. Within a few minutes, it pointed to the assumption that was breaking.
RevenueCat had delivered the webhook exactly as expected, but our API responded with a 500. The useful part of the error looked like this:
invalid input syntax for type uuid: "$RCAnonymousID:..."
The bug became obvious once we understood what each system believed. RevenueCat sometimes identifies users using anonymous IDs that look like this:
$RCAnonymousID:...
Our backend expected every App User ID arriving from RevenueCat to already be the UUID of an existing Gibwork user, something like:
550e8400-e29b-41d4-a716-446655440000
That assumption held until it didn't.
The backend passed RevenueCat's anonymous identifier directly into a PostgreSQL query against a UUID column. PostgreSQL rejected it, the webhook failed, and the subscription record was never created.
Google Play had completed the purchase and RevenueCat knew about the subscription, but our backend couldn't connect that purchase to the correct user. That was the entire bug:
RevenueCat and our backend had different ideas about who the customer was.
Like most production bugs, it wasn't caused by a complicated algorithm. It was caused by an identity edge case I hadn't accounted for when I built the original flow.
The first thing we did was restore the user's access and let them know everything was working again. After that, we changed the identity flow so RevenueCat's anonymous identifiers are no longer blindly treated as internal user UUIDs. We also reviewed restores and renewals to make sure the same mismatch couldn't quietly return a month later.
The patch wasn't the interesting part
A few years ago, reaching that understanding would've taken me much longer. Not because the eventual fix was complicated, but because tracing one user across a mobile app, a billing platform, a webhook service, a backend, and a database takes time. That's where AI has fundamentally changed the way I work.
People often talk about AI as if writing code is the main event. I don't think it is anymore. The most valuable part isn't generating another React component or API endpoint; it's shortening the distance between something broke and I understand why.
Production issues rarely live inside one neat function. They stretch across logs, payloads, third party services, application state, database constraints, and assumptions made months earlier. Understanding that entire chain has always been the slow part of software engineering.
Today, I can investigate that chain in one conversation. That doesn't mean the model is always right, or that I stop reading code and blindly accept every suggestion. It means I can understand a much larger system, much faster than I could before.
Leverage doesn't remove responsibility
The user didn't care whether the bug came from my code, AI generated code, RevenueCat, or PostgreSQL. They paid for Gibwork Plus and didn't receive it. That outcome was ours to own.
AI helped diagnose the incident, but it didn't reply to the customer, decide whether the proposed fix was safe, or think through renewals, duplicate events, and future edge cases. And it certainly won't be the one answering the next support ticket if I get those wrong.
Those responsibilities are still mine. If anything, AI makes them even more important because it lets us build systems faster than ever before.
The lesson wasn't that AI can fix production bugs for me. It was that AI can help me understand my systems—and the assumptions hidden inside them—much faster than before.
The biggest advantage is no longer moving from an empty file to generated code. It is moving from the system failed to I understand exactly why.
That, for me, is the best part of AI coding.