Opening the Crashlytics dashboard while the coffee brews has been my habit for a few years now. When a fresh overnight crash shows up as a red number, even a sleepy morning brain snaps awake. I have built apps on my own since 2014, and the four wallpaper apps I run today have crossed fifty million downloads between them. The more that number grows, the less realistic it becomes to chase every crash by myself.
Since I started using Antigravity, the shape of that morning crash-check has slowly changed. For about three weeks I have been pasting stack traces straight into it and asking it to narrow down the cause. I now have a much clearer sense of what I can delegate and what I should keep in my own hands, so let me write it down along the actual flow of the work.
Why I started feeding stack traces to an AI
The trigger was simple: I noticed I was running the same investigation over and over for similar crashes. Open the stack trace in Crashlytics, scan for the frame that belongs to my own code, jump to that spot in the editor, read what surrounds it. The routine is dull precisely because it is familiar, and when crashes pile up I tend to put it off.
My grandfathers were temple carpenters, and they checked every knot and twist in the wood by hand before bringing the plane to it. That sense — that working with your hands to confirm things is itself a kind of devotion — has stayed with me. Crushing crashes one at a time should be the same kind of work, but boredom makes me sloppy in moments. It is exactly that sloppy part I wanted to hand to an AI for the legwork, so I could stay focused on the judgment.
How I actually pass it over
My method is plain. I open the crash in Crashlytics and copy the stack trace along with the affected devices, OS versions, and occurrence count. I paste it into the Antigravity agent with the repository open, and ask something like this.
I am giving you the stack trace for this crash.
- Identify the frame that is likely my own code
- List several possible causes for why this crash happens
- Do not write a fix yet. Show me the cause hypotheses first
[paste the stack trace here]That last line turned out to be the important one. Early on I would ask it to "just fix it" in one shot, and the fix would arrive on top of a shallow read of the cause, leaving me to re-read everything anyway. Make it produce hypotheses first, let me agree, then move to the fix. Once I split it into these two stages, the rework dropped noticeably.
For example, when one wallpaper app threw an out-of-range array access, Antigravity offered three candidate causes.
// where it crashed
let item = wallpapers[selectedIndex]
// hypotheses Antigravity raised
// 1. wallpapers was swapped for an empty array during refresh, leaving a stale selectedIndex
// 2. the array shrank after a filter was applied, but selectedIndex was never updated
// 3. a delete event arrived mid-way through paged loadingThe real cause was the second one — something I had overlooked when I bolted a filter feature on later. Because the hypotheses were concrete, I knew immediately where in my own code to look.
The walls I hit
The first thing that tripped me up was that handing over an obfuscated stack trace left both the AI and me unable to read it. Swift release builds strip symbols, so unless you symbolicate with the dSYM, the frames are just a list of addresses. This is fine when Crashlytics' automatic symbolication is working, but for crashes on a version where the dSYM upload had slipped through, there was almost nothing human-readable. I had to get my own build settings in order before handing anything to Antigravity.
The second is the AI's tendency to over-fix. To prevent the out-of-range access, it would propose wrapping every array access in guard clauses I never asked for. Erring toward safety is not bad in itself, but a fix that suppresses the symptom without touching the root cause (the missed selectedIndex update) invites a different bug later. I now add, every time, "make the smallest change that fixes the cause, not one that hides the symptom."
The third is reproducibility. Even when the Crashlytics count is high, a crash I cannot reproduce on my own machine is hard to confirm. When I ask Antigravity, "can you write the smallest test case that reproduces this crash," it sometimes writes a test that assembles the offending state, and that becomes a foothold for reproduction. Being able to follow the order of writing a red test before fixing was a real gain.
The line I drew in three weeks
What I felt safe delegating was three things: narrowing the stack trace down to my own code's frame, producing several cause hypotheses, and drafting a reproduction test. All of them are the "legwork," and just having that sped up made the morning check far lighter.
On the other hand, which hypothesis to adopt, whether the fix should really go in, and whether to include it in a release — those judgments I keep entirely to myself. The history of each app and my feel for how people actually use it are still mostly unspoken, and I cannot hand them over. The four apps each have their own circumstances, and the same crash carries different priority across them. Weighting that still leans more reliably on a sense I have built over twelve years of running them.
To put a number on it, across the three weeks I handled around thirty new crashes across the four apps. For a little over half of them, the hypothesis landed and the fix came quickly. The rest were cases where the hypothesis missed, or where I could not reproduce the crash and set it aside. As a hit rate it is far from perfect, but for the half that landed, the legwork time I used to spend each morning clearly shrank. Even when it missed, the act of reading a hypothesis and deciding "no, that is not it" left my own thinking more organized. Using the AI's read as a starting board to push my own thinking forward turned out to suit me.
What I want to try next
The next thing I want to streamline is the manual work of pulling a stack trace out of Crashlytics and into the agent. Right now I copy from the screen, but if I could gather just the high-impact crashes every morning, I could spend more time on judgment. Then again, push the automation too far all at once and I suspect I would lose the chance to feel the overall shape of the crashes firsthand, so how far to automate is something I want to decide carefully.
Crushing crashes one at a time is unglamorous, but it is the foundation of solo development. Now that I can offload the legwork to an AI, the freed-up time lets me revisit the design habits sitting just upstream of the cause. I hope this is useful to anyone running apps with a small team in the same way.