Field notes from
twelve years of shipping mobile.
Notes on stabilizing inherited codebases, rescuing stalled releases, and the judgment calls that separate a real fix from a rewrite — written from the app-rescue work itself, not theory.
Start here
Three topics
- AI coding agents on real mobile appsNotes from running AI coding agents against shipping Android and iOS apps: multi-agent setups, review gates, and proof that a change works.
- Rescuing and stabilizing mobile appsTaking over inherited or stalled apps: stabilizing before a rewrite, store rejections, ad and release problems, and testing that catches regressions.
- Android and iOS side by sideKeeping native Android and iOS apps in step: feature parity, permissions, and what should stay different on each platform.
Three posts to read first
- Don't Trust an Agent That Says the Tests PassedAn agent's report is a claim. The setup I run only accepts evidence a machine or a second agent can check, and the cases where that caught something are on the page.
- Your App Should Still Be Yours When the Developer LeavesMost handovers get the code across. The things that decide whether someone else can ship next week are the history, the keys, and whose name is on the accounts.
- Your iOS and Android Apps Are Supposed to Be the Same Product. One of Them Isn't.Feature disparity between your platforms doesn't show up on any dashboard. It shows up in reviews that compare your app to itself — and in the churn of users who never knew what the other platform had.

Claude Code in Android Studio: The Editor Is the Easy Part
The Claude Code plugin for Android Studio shows diffs in the IDE and lets the agent read lint, but the phone is reached from the terminal. Counts from my agents' transcripts: thousands of leased adb commands, almost no use of Google's android CLI, a screenshot path that kept returning black frames, and a guard hook whose biggest rule mostly blocked commands that only mentioned a script.
- AI Agents
- Claude Code
- Android
- Testing
Working With AI AgentsClaude Code Compact: What Survives, With Counts From My Sessions
Claude Code compact replaces your conversation with a summary. The docs list what survives and what does not. I counted compactions across 4,121 of my own session transcripts, and here is the one rule I follow because of it.
October 26, 20265 min read
Working With AI AgentsClaude Code Hooks: Three I Run Every Day
Three Claude Code sessions each launched about twelve background commands and crowded a 12-core Mac, and no session could see the others. A CLAUDE.md rule cannot fix that; a hook can. Three hooks I run on every session: a budget guard that blocks background fan-out before it starts, a Stop hook that refuses to end a turn on an unverified still-running claim, and a cleanup pass that reaps what a session leaves behind. Which event each one hangs on, what the contract allows, and the two places mine are still wrong.
October 26, 20268 min read
Working With AI AgentsClaude Code Pricing: What Each Plan Includes and What It Costs
Claude Code pricing depends on how you sign in: a subscription plan with usage limits, or pay-per-token through the API. Here is what Anthropic's pricing page and cost docs said on 2026-10-26, quoted and linked, with no numbers from memory.
October 26, 20266 min read
Working With AI AgentsClaude Code Statusline: A Setup That Shows Your Limits
A Claude Code statusline is a shell command that reads JSON on stdin and prints one line under the prompt. Here is the setting, the fields the docs list, the settings.json scopes, and my own script that shows context used and five-hour and seven-day usage with reset times.
October 26, 20265 min read
Working With AI AgentsClaude Code Subagents: When One Beats Doing It Inline
A subagent is its own context window with its own prompt, tools and permissions. The docs say to delegate verbose or self-contained work that can return a summary, and to stay inline when it needs back-and-forth. Here is the shape, the decision table, and the bounded brief I hand over.
October 26, 20265 min read
Working With AI AgentsClaude Code vs Cursor: What I Measured Running Both as Coding Workers
I have used the Cursor CLI and Claude Code as bounded-task workers, and I keep a results log of the tasks I dispatch. Here are the counts from that log (9 Cursor composer rows, 20 Claude Sonnet rows), the failures, one older three-tool benchmark that did not include Claude Code, and the Cursor and Claude claims that come from their own docs.
October 26, 20266 min read
Working With AI AgentsClaude Code YOLO Mode: What It Removes and What I Run Instead
YOLO mode, the bypassPermissions mode behind --dangerously-skip-permissions. Here is what the docs say it removes, why I run auto mode with a long allowlist instead, and the one hook guard I added for a failure I actually saw.
October 26, 20265 min read
Working With AI AgentsCLAUDE.md File: What to Put In It and What to Leave Out
A CLAUDE.md file is loaded into every session, so a long one costs context and, per the docs, reduces adherence. I cut mine from 3,639 words to 512 with a pointer index, then let it regrow to 9,555. Here is what belongs in it, what doesn't, and the numbers from my own git history.
October 26, 20265 min read
Working With AI AgentsCodex Exec in a Script: The Flags, the Exit Codes and a Timeout
Codex exec runs Codex without the interactive screen, so you can call it from a script. The docs cover sandbox, stdin and the output file. They do not describe a timeout, so I added one, plus an exit-code rule: no verdict is never a pass.
October 26, 20265 min read
Working With AI AgentsUsing Codex as the Read-Only Reviewer for Claude Code
I run Claude Code as the agent that plans, edits and dispatches, and Codex as the independent reviewer that returns a binary VERDICT on the changes I send it. Here is where each one sits in my workflow, one real failure the gate caught, and what I did not compare.
October 26, 20265 min read
Working With AI AgentsFlaky Tests or No Result? Seven Bad Runs, Sorted
A run that never produced a test result is not a flaky test and not a failing one. Here are seven red or incomplete runs from my Android logs, how I classified each, the one failure I could reproduce on purpose, and the ones I never explained.
October 26, 20266 min read
Working With AI AgentsGemini CLI vs Claude Code: What I Can and Cannot Say From My Logs
If you want Gemini CLI vs Claude Code, my logs cannot give it to you: I have no Gemini CLI runs, only Gemini models run through Antigravity (22 rows) next to 20 Claude Code rows. Here are the counts, the failures, what Gemini CLI's own README says, and what I did not measure.
October 26, 20266 min read
Working With AI AgentsSwiftUI vs UIKit: Counts From Eight of My iOS Apps
Every one of my eight iOS apps imports both frameworks. Here are the file counts for SwiftUI and UIKit, the UIKit types that keep appearing, Apple's quoted description of the bridging types, and what the counts cannot tell you.
October 26, 20265 min read
Working With AI AgentsXcode MCP: Setting It Up for Claude Code, and What I Saw
Xcode ships an MCP server that an agent started outside Xcode can use to build and act on your project. Here is Apple's setup, quoted, and four things I saw when I wired it to Claude Code on Xcode 27.0 on 2026-09-29.
October 26, 20265 min read
Working With AI AgentsClaude Code Skills: How to Write Ones Your Agent Actually Uses
You write a detailed SKILL.md, and your agent still ignores it and improvises from scratch. I run 130 skills across three Claude Code profiles, and whether one gets used comes down to its description, a lean procedure with detail in side files, and one shared folder instead of copies. Here is what decides whether an agent loads and follows a skill.
October 25, 20269 min read
The App-Rescue PlaybookGoogle Play Target API Level Requirement: A Checklist for an App Nobody Has Touched
Play Console warns that your app must target a newer API level, and the app has not been built in a year. Here is the requirement as Google states it and the order I raise targetSdk in.
October 22, 20266 min read
Working With AI AgentsDon't Trust an Agent That Says the Tests Passed
A change came back with 768 passing tests reported. An independent reviewer, which did not rerun them, found two High bugs those tests did not cover. Another reviewer found no blocking issues in static review and still failed a module, because it could not run the tests itself. Here are the three checks I put between an AI coding agent and the word done: a hook that checks status claims, a binary verdict from a reviewer that did not write the code, and exit codes that separate a failure from a run that never produced a result.
October 21, 20269 min read
Working With AI AgentsWhen Your Coding Agent Hits a Rate Limit: A Fallback Ladder That Keeps Working
Your coding agent just ran out of quota. Waiting kills the afternoon, and pasting the prompt into whatever tool is open wastes your best model on small work. Here is the ordered fallback ladder I run instead: free models first, paid rows one step at a time, automatic retries only when nothing was written, and a journal that decides the order.
October 21, 20268 min read
Working With AI AgentsTwelve Cores, Seven Devices, One Disk
Every guide to running multiple Claude Code agents in parallel says the same thing: give each one its own git worktree. That keeps the files apart. It doesn't keep the machine apart. On my setup, several sessions share a 12-core Mac, a Windows build PC and seven test phones, and what broke was never a merge conflict. It was CPU oversubscription, two test runs fighting over one port, phones left awake around the clock, a disk that went from 56 GB free to 1 GB overnight, and a test run that sat hung for seven hours. The guards that fixed each one, with the real config.
October 21, 20268 min read
Working With AI AgentsWhat Keeps Agent-Written Code Clean When Nobody Reads It
AI agents write most of the code in my apps and I don't read it line by line. The clean architecture still has to hold, because the next feature, the next SDK swap and the next agent all depend on it. What I found: a rule written into a prompt decays, a boundary test doesn't, and a reviewer that isn't the writer catches layer mistakes before they become code. Real tests, a real reviewer verdict, and one boundary that did break.
October 20, 20268 min read
The App-Rescue PlaybookLive Users, Zero Ad Requests
AdMob reported zero ad requests for an app with people actively using it. Consent and app-ads.txt could not explain it: the build that reached the store had been compiled with ads off, because one git-ignored file was missing from the build machine. That case, the same fault on iOS, and the different failure that hides behind a 0% match rate.
October 13, 20267 min read
The App-Rescue PlaybookYour App Should Still Be Yours When the Developer Leaves
If the person who builds your app stopped answering tomorrow, could someone else ship a fix next week? Two real handovers show where that question actually gets decided: eight months of work that had never reached the shared repository, arriving as a single commit, and an agency-built app whose App Store upload key sat in the repository. Then the checklist of what your business has to own, and what a developer should only ever be granted access to.
September 24, 20267 min read
The App-Rescue PlaybookThe Agent Taps the Phone. Nothing Touches the Mac.
An agent will implement your iOS feature, write the test, and run it — then stop at the only question that matters: does this work on a phone? Two walls stand between you and letting it drive the device. The first is iOS itself: on Android a cable and a debugging switch inject a tap into any handset, while nothing on a physical iPhone accepts one without a signed on-device agent and a live tunnel. The second is self-inflicted — the assumption that the agent must work a pointer like a person, which makes the automation unusable even when it works. Here is the receipt from a real session, the three honest tiers of physical-device driving, the exact commands to reproduce the whole loop yourself, and the two bugs a simulator could never have caught.
September 9, 20269 min read
The App-Rescue PlaybookCall, Text, and Share Without Asking for a Single Permission
A row of quick actions under a phone number — call it, text it, save it, share it — looks like a convenience and builds like a permissions grab. The naive version asks the OS to place calls, send texts, and write to the address book: a runtime prompt in front of a button the user tapped because they were ready to act, a store-policy tripwire on Android, and a privacy disclosure on both platforms it never needed. None of it is required, because none of those actions is something the app has to do itself. Here's the permission-free version on Android and iOS, and the per-platform permission audits that prove it.
August 25, 20268 min read
The App-Rescue PlaybookA Correct Fix That Quietly Broke the Feature Next to It
A one-line change did exactly what it was meant to: it made a half-finished unit of work report itself as a failure instead of a fake success. It shipped in the afternoon it was written. Before the day was out it had to be fixed again — because that honest little fix had quietly taught the app to abandon entire batches of work, without an error, a crash, or a single line in any dashboard. That's a regression: a bug your own progress created, invisible from the inside. Here's the one that got caught, and the test that now catches it for good.
August 15, 20265 min read
The App-Rescue PlaybookYour iOS and Android Apps Are Supposed to Be the Same Product. One of Them Isn't.
Most companies with two mobile apps have one real product and one approximation of it. The gap never appears on a dashboard, because parity is nobody's number — it grows one reasonable 'we'll port that later' at a time, and the users on the weaker platform don't file tickets about features they've never seen. Taking a production Android app to iOS, screen by screen, taught me what closing that gap actually takes: an adversarial inventory of every surface, proof that filters mean the same thing and not just look the same, and the discipline to write down which differences are deliberate. Here's the method, with the failures it caught.
August 14, 20269 min read
The App-Rescue PlaybookYour Crash-Free Rate Is 99.5% and Users Still Say the App Is Broken
Your dashboard says 99.5% crash-free and your reviews say the app doesn't work. Both are true, because crashes are the failure class users actually forgive — they're loud, they're recoverable, and the app tells you about them. The expensive ones are silent: a send queue stuck in a state nothing reports, a concurrent write that leaves two people believing different things, a query that quietly stops matching real data. Nobody files a ticket for those. They just leave. Here's what those failures look like from the inside, and what to instrument so the app tells you instead of your reviews.
August 12, 20269 min read
The App-Rescue PlaybookYour App Hasn't Shipped in a Year. The Problem Usually Isn't the Code.
Your app hasn't shipped an update in a year, and everyone has quietly agreed that's fine because nothing is on fire. It isn't fine: a frozen app is on a clock set by Apple and Google, not by you. In four rescues the blocker was never that the code couldn't be changed — it was a pipeline nobody could run, a build only one person could produce, a store gate nobody had decoded, or a platform floor that rose while the team stood still. Here's what's actually decaying, and the order I'd unblock it in.
August 5, 20269 min read
Android ReliabilityYou Cannot Test Android on Every Phone. Here's What to Prove.
You cannot test an Android app on every phone. The device matrix is unbounded, and it changes without you shipping anything — a third-party update can break a flow that was proven working last month. So what do you actually have to prove? A four-device bench with a written ledger of what is and isn't covered, field telemetry filtered hard enough to trust, fallbacks that fail loudly instead of silently, and OEM work that expands only when a recorded failure calls for it.
July 29, 20268 min read
App Store ReviewApp Store Rejection for Requiring WhatsApp: Fixing Guideline 4.2.3(i)
Apple rejected my app under Guideline 4.2.3(i) — Minimum Functionality — for requiring WhatsApp to be installed. Building a complete standalone send path wasn't enough: the next build was rejected on the same guideline. What actually cleared it was the empty first-launch screen, and an appeal made of screenshots instead of argument. What the guideline is really testing, and the five things I'd check before you resubmit.
July 22, 20267 min read
The App-Rescue PlaybookHow I Review AI Changes in a Production Mobile App
The dangerous thing about an AI coding agent isn't that it writes bad code — it's that it will tell you the work is done when it isn't. A skipped test reported as passing. A readiness summary that says PASS over a build that actually died. The method that's held up for me on a production mobile codebase: split the planner from the coder so nothing approves its own work, and keep the state that matters in files instead of the chat. The loop, the artifacts, and the four questions to ask anyone putting AI on your app.
July 13, 20267 min read
The App-Rescue PlaybookHow to Know an Android App Audit Actually Found Nothing
A full audit of an Android codebase came back with one line: no verified bugs found. That's either the best sentence in engineering or the most worthless one — and the difference is entirely in the process. Here's what had to happen first before I'd believe it.
July 10, 20267 min read
The App-Rescue PlaybookBefore You Rewrite an Inherited App, Stabilize It
You inherited an unstable mobile app and someone has already said 'just rewrite it.' A rewrite trades a known set of bugs for an unknown one and deletes years of edge-case handling that exists nowhere else. Here's the five-step playbook I run instead — profile before touching anything, kill the worst pain in place, add boundaries so fixes hold, repair the release pipeline, and only then decide whether a rewrite has actually been earned.
July 8, 20266 min read