Our Team's quest to developing TANGO on Android
Howdy! Last time we wrote about using agentic AI to gather local business data for the app. This post is the bigger swing: we used AI to build our entire Android app from scratch, and then we tried to answer a question almost nobody publishing these "we built X with AI" posts actually answers.
Not "did AI help?" Of course it helped. The real question is: how much did the AI do on its own, with no human correcting it?
Of 499 real prompts, we were able to have it port :
Without human intervention (A1, fully autonomous): 386 = 77.4%
With some human intervention (A2-A4): 113 = 22.6%

To answer it without hand-waving, we did something a little obsessive. We went back through every development session — 107 of them, 686 individual prompts, roughly 129 MB of raw conversation transcripts — and graded each prompt on whether the AI completed it autonomously or needed us to step in. The headline: of 499 real engineering prompts, 77% were completed fully autonomously. Only ~1% truly failed, and almost all of those were the AI service hiccupping for a minute, not the AI getting the problem wrong.
But the number isn't the interesting part. The interesting part is which 77%, which 23%, and what the pattern tells you about where these tools are genuinely ready to fly solo and where they still need a human hand on the wheel. So let's get into it. If you want to see the data for yourself, click on the google drive link at the bottom of this blog post.
We had an app, just on the wrong phone
Tango already existed on iOS, in Swift. A big chunk of Aggies are on Android, though, and "we'll do Android later" is how a lot of student startups quietly cede half their market. The catch is that a port is not a copy-paste. iOS and Android share almost nothing at the code level because they use different languages (Swift vs. Kotlin), different UI frameworks (SwiftUI vs. Jetpack Compose), different navigation, different image loading, different everything. The backend is shared (we run on Supabase, Postgres, RPCs, realtime, storage), but every screen and every call into that backend has to be rebuilt from the ground up.
The traditional answer is "hire an Android engineer for a few months." As students, that's not a real option. So we tried the unreasonable thing: rebuild the whole Android app by directing Claude Code: an agentic coding tool running on Claude Opus, feature by feature, using our iOS codebase as the spec.
Four weeks later we had onboarding, the singles matching flow, the couples collaborative date-planning features, group chats, maps and locations, an app-wide dark mode, push notifications, and a signed build sitting in Google Play's internal testing track. Almost every line was written by the AI.
This wasn't "type a wish, receive an app." The workflow that emerged was a tight loop, and the shape of that loop turns out to be the whole game:
1. Point at the reference. Almost every prompt was some version of "here's how iOS does X (in ../TangoIOS), make Android match." The existing app was the spec, which removed an enormous amount of ambiguity. The reference was simply the source code from which the Tango IOS version lived at.
2. Let the agent work. Claude would read both codebases, plan, and make the changes — often touching 5–15 files, running the Kotlin compiler itself, and fixing its own build errors before handing back.
3. We intervened when it got stuck. A pasted Logcat error, a screenshot of the broken UI, or a copy of the Supabase schema dump. This step, it turns out, is where autonomy is won or lost.
4. Course-correct on judgment. "Use red here," "that's the wrong screenshot," "actually the server is the bug, not the client."
The single best decision we made was keeping a recent database backup in the repo. When the AI didn't know the exact name of an RPC or the columns on a table, it would just grep the schema.sql dump and find the truth instead of guessing. More on that below — it's a recurring hero of this story.
How we measured it (for the skeptics, and ourselves)
Before the results, a word on method, because "77%" is only worth anything if you trust how it was computed.
We built a small three-stage pipeline. First, distill: each raw transcript was compressed into a faithful digest of the conversation flow — prompt, the actions the AI took, the result — with the giant tool-output payloads stripped so the signal wasn't buried. Second, grade: we read all 107 digests and scored every prompt on a four-level autonomy scale:
- A1 (fully autonomous): the AI understood, did the work, and got it right with no corrective input.
- A2 (minor nudge): one clarification or a small correction.
- A3 (significant intervention): multiple rounds, an interruption, or a denied action.
- A4 (failed or abandoned) in that session.
Third, aggregate: join the grades with mechanical signals (tool calls, errors, interrupts) into one row per prompt, then roll it up.
We excluded 187 "artifact" turns from the denominator. These aren't real prompts, they're automatic context-compaction summaries, the //compact command, interruption markers, and pure "nice, that worked!" messages. The biggest source was a surprise: in very long sessions, the transcript replays earlier turns. Our single largest "session" logged 170 turns but contained only 51 unique prompts, the other 119 were duplicates and housekeeping. Counting those would have inflated the corpus and flattered the result, so every excluded row is flagged and auditable.
The grade measures "did a human have to correct the AI," not "is the code bug-free. A1 is a statement about autonomy, not perfection. The grades were spot-checked back against the raw session files: prompts, edit counts, and outcomes all matched, and the whole dataset passed schema validation with zero violations.
With that out of the way:

Broken down by the kind of work:

What the AI was genuinely great at
Greenfield features, built whole. This was the biggest surprise. In one session, the AI built our entire friends-groups feature from nothing. This involved fourteen new Kotlin files: data models, a repository layer wrapping a dozen Supabase calls, two ViewModels, realtime group chat, discovery, and profile sheets, plus wiring a fifth tab into the navigation. When the first build failed with fourteen compile errors, it read its own errors and fixed every one (Material3 API renames, missing imports, a serialization builder), then chased a cascade of backend mismatches we'd never have caught by hand. The pre-login intro/onboarding system (eight new files including a connectivity monitor and animated slides) went the same way. So did the couples "Daily Spark" feature, an entire singles-mode transition flow, and more.
Mechanical work and big sweeping refactors. The app-wide dark-mode conversion was a 35-prompt marathon: replace every hardcoded color with semantic Material3 tokens, screen by screen, matching how iOS uses its semantic colors. The AI even caught the subtle root cause that would've broken the whole thing: on Android 12+, dynamic color was silently overriding our palette, so it set dynamicColor = false before anything else. Refactors hit 100% autonomy in our data, and tooling/config 95%, for the same reason: the spec is unambiguous and success is mechanically checkable.
Root-causing from a single log. This is where the tool felt less like autocomplete and more like a teammate. Over and over, we'd paste a crash and get an exact diagnosis:
> fetchSharedLocations failed: Could not find the function public.fetch_locations_by_ids_rpc(p_location_ids) … Perhaps you meant … (p_ids)
That one was a single wrong parameter name causing silent failure — the AI spotted it instantly. Elsewhere it diagnosed a deserialization crash as decodeSingle (which expects a JSON array) being used on an RPC that returns a bare scalar; a Supabase Functions plugin that was never installed in the client; and — a real head-scratcher — a delete that returned HTTP 200 but removed zero rows, because the couple_sparks table had no DELETE row-level-security policy, so the "success" log was a lie. A human would've spent an afternoon on each of those. The AI averaged a couple of tool calls.
Database-aware work. Because we kept the schema dump in the repo, the AI treated the database as a first-class source of truth. It would grep the backup to confirm an exact RPC signature, discover that the real location_hours table is one row per location with per-day columns (not the shape it first assumed), and rewrite the model to match. Our /add-location workflow would research a venue on the web, then emit the correct INSERTUPDATE SQL, and in one run it even noticed a venue had permanently closed and refused to add stale data (See our last blog post on this!)
Shipping. It ran a security review of the diff (and caught that we were logging a couple's private invite code), then walked us all the way through generating a signing key, configuring the release build, and producing a signed AAB for the Play Store. This included untangling a genuinely confusing Google Play "your upload key doesn't match" situation by comparing SHA-1 fingerprints.
Where we still mattered: the honest 23%
This is the part we think is most useful to other builders, so we're not going to dress it up.
Stateful, cross-system debugging is the final boss. The hardest single thing in the entire project was keeping iOS and Android perfectly in sync, in real time, during the couples flow — two partners on two phones, planning a date together, each screen needing to update the instant the other taps. That one feature became a multi-day marathon and absorbed the large majority of all the human intervention (and, full transparency, the frustration) in the whole corpus.
What made it so hard is instructive: the bugs lived between the platforms, where neither side alone looks wrong. A few real ones we hit:
Casing. Swift formats UUIDs in uppercase CBF4B5A3-…); Kotlin used lowercase cbf4b5a3-…). Every cross-device lookup silently missed. Nothing in either codebase was "wrong."
Channel topic mismatch. Android subscribed to group-messages:<id>; iOS broadcast on group_<id>. Messages just… never arrived.
A missing field. iOS's vote payload required a type field that Android wasn't sending, so iOS's JSON decoder silently dropped every vote from Android. No error, no crash, just nothing.
The AI could fix each of these, once it had logs from both sides. But isolating them required a human running the actual two-phone test, capturing the evidence, and feeding it back. Realtime state across two clients and a server is exactly the class of problem where the AI needed us most, and it's the ceiling on "just let it run."
Confidently wrong is a real failure mode. A couple of times the AI wrote a fix on an unconfirmed theory and we had to slow it down. The sharpest example: we couldn't delete a user from the database, and it confidently authored a migration to "fix" a foreign-key constraint — which didn't work, because the actual blocker was a different table entirely. The lesson the user drove home in the moment ("this is why I asked you to confirm before writing a fix") became our rule: make it diagnose and prove the root cause before it's allowed to change anything. Treat a confident plan as a hypothesis, not a conclusion.
Judgment is still ours. The AI can write the button, but it can't decide it should be red instead of blue, notice we handed it the wrong screenshot, or feel that a flow is confusing. A meaningful slice of the A2/A3 cases were product and UX calls, not engineering — exactly the work you want a founder doing anyway.
One genuinely frustrating session was just trying to run a shell script on Windows. The AI would cycle through plausible fixes (CMD quoting, WSL paths) because it couldn't see that the user's bash was WSL without the right tool on PATH. Anything that depends on local environment state it can't observe is a weak spot.
A handful of "failures" in our data were the AI service being momentarily overloaded or hitting a usage limit. We counted them anyway, which is part of why the success rate is conservative.
The pattern, if you're trying to build this way
Pulling it together, the thing we'd actually tell another developer:
The AI was near-flawless when it had a clear spec (the iOS code), ground truth (the schema dump), and fast verification (its own compiler, a pasted log, a screenshot). It struggled when the truth lived somewhere it couldn't reach: across two devices, on the user's machine, in a product decision only a human can make. Most of the 23% isn't "the AI can't", it was just "we didn't show it enough."
Give it the database, literally. Keeping a schema/data dump in the repo did more for reliability than any clever prompting. It turned "guess the RPC name" into "grep for the RPC name."
Mind the context tax. The compaction-and-replay we found in our own logs is a real operational detail: very long sessions get expensive and noisy. Shorter, well-scoped sessions with the right context up front beat one heroic marathon.
What this means for our student venture
Step back and the number that matters isn't 77%. It's this: a tiny student team shipped a second full platform in about four weeks, for roughly the cost of an AI subscription, with one person at the keyboard. That's the difference between Tango being iOS-only and Tango being for every Aggie with a phone.
So we're leaning all the way in. We're keeping the AI-driven build model and investing in the things that made it work best: faster paths to get logs, screenshots, and database state into the agent's hands; while keeping a human firmly in the loop on the cross-platform, stateful problems where it still needs us. And we built a repeatable way to measure all of this, so as we keep shipping, we can keep checking our own claims instead of vibe coding this whole thing.
If you want to dig into the receipts, we put together a full report grading all 686 prompts, with the methodology and the raw per-session logs. Check it all out at our Google Drive Link:
Thanks to everyone cheering us on through the Mays AI Venture Velocity Challenge, and to the tools and mentors making this pace possible.
Follow us on our socials to stay tuned with our development. Thanks and Gig'Em!

