Skip to main content
How-ToAugust 25, 2026·Updated August 28, 2026·21 min read

What Shipping an AI-Built iOS App Taught Us About Vibe Coding

We built and shipped an iOS app almost entirely through Claude Code. Eight of the ten critical review findings sat in persistence, sync and account identity — and not one sat in the UI we were watching every day. Here is the rules file, the review loop and the audit prompt that came out of it, and the failure behind each one.

ByAmol Pomane·Founder, Vmobify
What Shipping an AI-Built iOS App Taught Us About Vibe Coding — illustration

What does vibe coding actually mean once you ship?

It means the typing is delegated and the engineering is not — and the gap between those two things is where every problem in this post lives. Writing code by prompt is genuinely fast. Getting that code through App Store review, onto strangers' devices, and into a state where you can still change it in three months is the part nobody demos.

The app this post draws on is AI Baby Growth Tracker, which went live on the App Store on 16 August 2026. It is a paediatric growth-tracking app built on the WHO Child Growth Standards, with on-device storage, a subscription, and an AI coaching layer. That is as much product description as this post needs, because the app is the evidence rather than the subject.

What is worth reporting is the shape of the build:

  • 431 commits and 52 merged pull requests on the iOS repository between 27 February and 22 July 2026 — about five months.
  • 737 commits on the Android repository over a similar window.
  • A rules file that reached 23 sections and roughly 45KB, plus a separate 41KB equivalent for Android.
  • 98 of those 431 commits reference a code review performed by an agent rather than a person.
  • Five full revisions of the product requirements before the architecture settled.
  • 19 issues raised across one seven-pull-request review batch — 10 of them classified critical, and 8 of those 10 in the same part of the codebase. More on what those actually were below, because the distribution is the most useful thing in this post.

One number we are deliberately not going to give you is a growth number. The app has been live for nine days at the time of writing and has no meaningful rating count, so anything we said about installs or retention would be noise dressed as evidence. In our portfolio across 300+ apps managed since 2013 we have a lot of growth data; we have none for this one yet, and inventing some would undermine the only thing this post is actually good for.

What follows is the governance that made the code survivable: the rules file, the review loop, the banned patterns, the audit prompt, and the two failures that were expensive enough to become permanent process.

Three App Store screenshots from AI Baby Growth Tracker showing the dashboard with WHO percentile tracking, the growth chart with velocity and z-score, and the vaccination schedule view.
The shipped product, three of its App Store screenshots. It is here as evidence rather than as the subject — the point of this post is the process that got it through review, not the app itself.

Why does the rules file decide the quality of everything after it?

Because an agent has no memory of your last correction, and without a written rule it will confidently reintroduce the same mistake in the next session — so the rules file is the only thing that compounds. Everything else you do resets.

This is the single biggest difference between vibe coding a prototype and vibe coding something you intend to maintain. In a prototype, you correct the model in chat and move on. Over five months and four hundred commits, the corrections you made in week two are gone by week six unless they were written down, and you will find yourself typing "don't hardcode colours" for the ninth time.

Our file ended up with a line at the bottom that reads: this file is the law; every line of code must comply; update after each sprint with new learnings. That framing turned out to matter more than any individual rule. Once the file is the law, three useful things follow:

  • The reviewer has something to review against. "This looks wrong to me" is an opinion; "this violates section 15" is a finding.
  • Disagreements get settled once. A decision recorded in the file stops being relitigated every sprint.
  • The file becomes a scar record. Reading ours top to bottom is a list of everything that has already gone wrong, which is far more useful to a new contributor — human or agent — than a style guide written in advance.
Where to start

The corollary is that a rules file written before you start is mostly guesswork. Ours began short and grew every sprint. The sections that carry real weight — banned anti-patterns, the repeat-issues list, the findings register — all exist because something specific broke first. If you are starting today, write the skeleton, then treat every review finding as a candidate rule.

What belongs in a CLAUDE.md, and what is noise?

Anything an agent can get wrong in a way you would have to correct twice belongs in the file; anything it can look up from the code does not. That single test removes most of the bloat people put in these files.

Concretely, our file earns its size on five kinds of content: locked technology choices with versions, architectural boundaries stated as rules rather than preferences, design tokens with the hardcoded values that are forbidden, a canonical name reference so agents stop inventing type names, and the review checklists. What it deliberately does not contain is explanations of how Swift works, restatements of the framework documentation, or aspirational descriptions of code quality.

Here is a starting skeleton — the structure, not our content. Copy it and fill each section with rules your own review findings produce:

# <App Name> — Coding Standards & Architecture

## 1. Tech Stack (Locked)
Pin exact versions. State what is forbidden to add. Minimal dependencies is a rule, not a vibe.

## 2. Project Structure
The directory tree, and which layers may import which. Be explicit: "Core/ must never import Features/".

## 3. Architecture
The pattern, stated as rules. Which types may appear in a View. Where business logic is not allowed.

## 4. Dependency Injection
How objects are constructed. Where the container lives. What agents must never instantiate directly.

## 5. Navigation
One navigation model. Name the anti-pattern explicitly (ours: nested navigation stacks inside tabs).

## 6. Design System (Token-Based)
Every colour, spacing and type token by name — plus the literal values that are banned.

## 7. State Management & Concurrency
Which observation mechanism. What is forbidden. Where isolation boundaries sit.

## 8. Persistence
The model layer, migration policy, and what must never leak into the UI layer.

## 9. Accessibility (Non-Negotiable)
Labels, contrast, touch targets, motion. Written as pass/fail, not as guidance.

## 10. Testing
What must have a test before merge. What may not be mocked.

## 11. Git Workflow
Branch naming, commit format, PR rules. Never commit to main.

## 12. Error Handling
Where errors may be swallowed (nowhere). What a service must do on failure.

## 13. Anti-Patterns (Forbidden)
Paired code blocks: the wrong version, then the right one. This section grows every sprint.

## 14. Code Review Checklist
What the reviewer checks, in order.

## 15. Agent Self-Validation
The list an agent must confirm BEFORE reporting completion.

## 16. Performance Budgets
Numbers, not adjectives.

## 17. Canonical Names Reference
Every type, service and property name. Stops agents inventing near-miss duplicates.

## 18. Review Process
Who reviews, against what, and what blocks a merge.

## 19. Full Audit Prompt
The periodic whole-codebase review, written out and reusable.

---
*This file is the law. Update after every sprint with new learnings.*

The two sections that surprised us by being the most valuable were 17, the canonical names reference, and the anti-patterns list. Without a name reference, agents invent plausible variants — a service called GrowthService in one file and GrowthManager in another — and you get duplicate implementations that both work and slowly diverge.

Six-part project rules file covering architecture, data model, privacy, accessibility, the review loop, and a history of repeat issues.
Persistent context is governance, not prompt decoration. If a constraint must survive the next session—or a mistake can recur—put it in the rules file.

How do you review code you did not write line by line?

You stop reviewing lines and start reviewing invariants — because reading four hundred commits of generated Swift at the character level is neither possible nor useful. The question changes from "is this code good?" to "does this code violate anything we have already decided?"

That reframing is what makes the volume manageable. A human reviewing AI output line by line becomes a bottleneck within a week and starts rubber-stamping by week three. A human reviewing against a written invariant list can process a large diff quickly, because most of the check is mechanical and can be delegated.

The invariants worth checking, in the order we check them:

  • Layer boundaries. Did anything in the core layer start importing from features? This is the violation that is cheapest to fix immediately and most expensive to fix in month four.
  • Data-loss risk. Anything touching persistence, migration or sync gets read properly, by a person. This is the one category where we do not trust a delegated review.
  • Silent error handling. Generated code loves swallowing errors. We ban the pattern by name and grep for it.
  • Design token compliance. Hardcoded colours and spacing creep in constantly and are trivially greppable.
  • Accessibility. Labels, touch targets, motion gating — all mechanically checkable, all routinely missing from generated UI code. We wrote Apple's accessibility guidance into the rules file as pass/fail items rather than linking to it, because a rule an agent has to go and read is a rule it will approximate.
  • File size. We cap views, view models and services at 300 lines. It is an arbitrary number that works, because agents will otherwise grow a single file indefinitely rather than proposing an extraction.

Notice how much of that list is greppable. That is deliberate. An invariant you can express as a search is an invariant you can enforce at zero marginal cost forever, and it is the difference between a standard and an aspiration.

Why did we make an agent review every pull request?

Because the agent that wrote the code is the worst possible judge of it, and a second agent with the rules file and no attachment to the work catches things the author will defend. We made it mandatory before every merge, and 98 of our 431 commits reference that review.

The loop is simple enough to copy:

  1. Run the build gate. Zero errors required before review starts — a reviewer should never be spending attention on things a compiler already knows.
  2. Launch a review agent against the changed files, instructed to treat the rules file as law.
  3. The reviewer classifies every finding as Critical (build failure, data-loss risk, security, wrong architecture), Important (rule violations, anti-patterns, missing accessibility, logic bugs) or Suggestion.
  4. Fix all Critical, rebuild, re-review. Then fix all Important, rebuild, re-review.
  5. Suggestions get logged to a backlog and explicitly do not block the merge.
  6. Merge only at zero Critical and zero Important.

The severity split is the part that makes it survivable. Without it, a reviewer returns forty items of mixed importance and the author either fixes everything (slow) or triages by instinct (unreliable). With it, the merge condition is unambiguous and the backlog absorbs everything genuinely optional.

The other rule that earned its place: the reviewer must be a different agent instance with no context from the implementation session. An agent asked to review its own work in the same conversation will agree with itself. This is not a subtle effect — it is the difference between a review that finds things and a review that produces a paragraph of praise.

It is worth being concrete about what these reviews actually caught, because "the agent found some issues" is not evidence of anything. Across one batch of seven pull requests the reviewer raised 19 issues, 10 of them critical. The critical ones were not style problems. A representative sample:

  • Sign-out deleted local health records immediately after the auth call succeeded — while the sign-out dialog told the user their data would stay on the device. Data loss, and a dialog that lied about it.
  • Sync writes were enqueued in an unstructured task that could be cancelled if the app was backgrounded straight after a write, so the outbox entry never persisted and the cloud upload was lost permanently.
  • A schema migration was missing a version. An existing install upgrading to a build with new fields would hit a store whose checksum no longer matched the registered schema.
  • One account's data survived into another account's session because the sign-out path preserved children, photos and entitlements, and the cleanup only ran on a different code path.
  • A migration cancelled the user's own custom reminders because it matched on a notification prefix that user-configured reminders also used.

Every one of those is a data-integrity or privacy failure, and none is visible from reading a diff casually. The distribution is the single most useful thing we learned. Sorting all 19 by the part of the system they broke:

  • Persistence and migration — 6 issues, 5 of them critical. The largest cluster by some distance.
  • Account and identity — 2 issues, both critical. Small cluster, perfect critical rate.
  • Data completeness, validation, and notification timing — 7 issues, 3 critical between them.
  • Everything presentational — 4 issues, none critical.

Read that as a shape rather than a scoreboard. Eight of the ten critical findings sat in persistence, sync, migration or account identity, and not one critical finding was in the layer you can see on screen. Generated code did not produce ugly views. It produced plausible code that failed at the boundaries where state crosses a lifecycle event — the app being backgrounded mid-write, a user signing out, an install upgrading into a new schema, an account being switched.

That is a directly actionable conclusion, and it is the one we would give anyone starting this way: put your human attention on the state layer and let the review agent have the UI. The reverse — eyeballing screens and trusting the persistence code because it compiled — is the default, and it is exactly backwards.

One finding is worth singling out because it appeared twice, in two separate pull requests months apart: the unstructured-task sync problem was found, fixed, and then reintroduced in a later feature. That recurrence is precisely the argument for the repeat-issues list in the next section. A fix that does not become a written rule is a fix with a half-life.

One honest limitation. A delegated review is excellent at mechanical compliance and poor at judging whether the feature is the right feature. Product judgement never got delegated, and should not be.

Flow diagram of the mandatory review loop — build gate with zero errors, review by a separate agent treating the rules file as law, classification into critical, important and suggestion, then fix and re-review before merge, with critical and important blocking the merge and suggestions going to a backlog.
The return arrow is the whole diagram. A review that cannot send work back is a formality, and a severity scale is what stops the loop running forever.

Which anti-patterns had to be banned by name?

The ones that came back. We keep an explicit "History of Repeat Issues" list of the dozen violations that appeared in multiple review rounds, because a pattern that recurs is a documentation failure rather than a model failure.

Ours, verbatim in spirit, includes: hardcoded colour literals anywhere in feature code; passing a persistence model straight into a view instead of a display type; putting the observation macro on services when it belongs only on view models; silencing errors with an optional-try inside a service; view models calling repositories directly instead of going through a service; nested navigation stacks inside tabs; using the brand accent colour for danger states; and a handful of framework-specific mistakes that are only meaningful in our stack.

The general lesson transfers even if none of those specifics apply to you. The format that works is a paired code block — the forbidden version and the correct version, side by side, with no prose between them. Agents follow a diff far more reliably than they follow a description, and so do humans skimming at speed.

Watch out

A good number of ours came from a single source: the compiler got stricter than the training data. Swift's strict concurrency model invalidated a lot of patterns that were idiomatic a couple of years ago, and generated code reached for the old ones by default. If your language or framework has had a recent breaking shift in how correctness is enforced, expect that shift to be the single largest source of repeat findings, and write the new rules down first.

Two further observations from watching which bans held:

  • Bans that can be grepped hold. Bans that require judgement drift. "No hardcoded colours" is enforceable forever. "Keep view models focused" is not, which is why it became a 300-line cap instead.
  • A ban needs its replacement in the same breath. Telling an agent not to use something without naming what to use instead produces a creative third option you will like even less.

There is also a self-validation list — seventeen items an agent must confirm before it reports completion, covering things like no duplicate types, no files over the line limit, and every new view model having a matching factory. It exists because "done" from an agent means "I stopped", not "it is correct", and a checklist converts one into the other more cheaply than a review round does.

What does the full-codebase audit prompt look like?

Diff-scoped review can never catch cross-file drift, so every third sprint we run a whole-codebase audit against ten fixed dimensions — and that is the pass that finds the problems the per-PR reviews structurally cannot.

The failure that forced this: a field added to one AI context struct and not to the two sibling structs that were supposed to mirror it. Every individual pull request was correct. The inconsistency existed only in the relationship between files, and a reviewer looking at changed files will never see it.

Here is the prompt, generalised so you can adapt it. It is written for Android below, since that is the platform most people reading this will be pointing it at — swap the framework specifics for your own:

You are the CTO of this Android app. This is a COMPREHENSIVE CODEBASE AUDIT,
not a diff review. Read and cross-reference ALL source files. Explore the full
directory tree first. Do not limit yourself to recently changed files.

Find violations across ALL 10 dimensions simultaneously.

DIMENSION 1 — Architecture & layer boundaries
Entities/DTOs reaching Composables directly; ViewModels calling repositories
instead of use cases; any :core module importing from :feature; duplicate types
defined in more than one file.

DIMENSION 2 — Concurrency
GlobalScope usage; coroutines launched without a lifecycle-aware scope; blocking
calls on the main dispatcher; mutable state shared across coroutines without
synchronisation; suspend functions that swallow CancellationException.

DIMENSION 3 — Design token compliance
Hardcoded Color(0xFF...) values; .dp literals outside the spacing scale;
TextStyle constructed inline instead of referencing the type scale; hardcoded
strings in Composables instead of stringResource.

DIMENSION 4 — Accessibility
Every clickable without contentDescription or semantics; decorative images not
marked as decorative; touch targets under 48dp; animations not gated on the
reduce-motion setting.

DIMENSION 5 — Localisation
Hardcoded user-facing text; string concatenation instead of placeholders;
plurals implemented with if/else instead of a plurals resource.

DIMENSION 6 — File size
Every file over 300 lines: path, line count, recommended extraction.

DIMENSION 7 — Error handling
runCatching or try/catch that discards the error; empty catch blocks; !! force
unwraps in production code; catch blocks that never surface state to the UI.

DIMENSION 8 — Logging
android.util.Log or println in non-test code; any logging of personal data.

DIMENSION 9 — Cross-file consistency
For every repository interface: verify the implementation and the fake/mock both
implement every method. For every DI module: verify each binding resolves. For
every screen: verify a matching ViewModel and preview exist.

DIMENSION 10 — Build & release
Debug flags left enabled; API keys in source or gradle files; minify/shrink
disabled for release; targetSdk below the current Play requirement.

OUTPUT FORMAT:
### CRITICAL — FILE:LINE — DIMENSION — description — fix
### IMPORTANT — FILE:LINE — DIMENSION — description — fix
### SUGGESTION — FILE — DIMENSION — description
### SCORECARD: each dimension PASS / PARTIAL / FAIL with a count

Dimension 1 is worth calling out because it is where the compounding damage happens. Layer boundaries are the constraint agents erode most readily — not maliciously, but because importing the thing you need is locally the shortest path. Stating the boundary as a rule ("core must never import features") and auditing for it periodically is the difference between a codebase you can still restructure in month six and one where every module depends on every other. Android's own architecture guidance is a reasonable starting point for the Android version of that rule; the specific layering matters far less than writing one down and enforcing it.

Two details make this work. The fixed dimensions mean successive audits are comparable — you can see a dimension move from FAIL to PARTIAL to PASS across sprints. And the demand for file and line references makes every finding checkable, which matters more than it sounds when the next section explains what happens to findings that are not.

Two-panel comparison contrasting what per-pull-request review catches — layer boundary violations, hardcoded colours, swallowed errors, missing accessibility labels, oversized files — with what only a full-codebase audit can find, including a field added to one struct and missed in its siblings, a protocol method with no matching mock, and duplicate types that both compile.
The right-hand column is the argument for the periodic audit. Nothing there is visible to a reviewer looking at a diff, because every individual change was correct.

How do you stop findings from silently disappearing?

Give every finding an ID and a status in one register, and treat "not mentioned" as an invalid status — because the most expensive failure of this whole project was not bad code, it was good findings quietly evaporating.

Costly failure

The post-mortem, recorded in our rules file on 27 July 2026: an end-to-end audit produced roughly 150 findings. A plan was written that claimed to contain them all. Roughly 70 were never scheduled, never deferred, and never even contradicted — they existed in a phase document and were silently skipped. Four rounds of code review did not catch it.

The reason four rounds of review missed it is worth sitting with, because it generalises to any AI-assisted workflow: reviewers check what was done, not what was left out. Every commit reviewed was fine. The defect lived in the gap between the audit and the plan, and nothing in the process was looking at that gap.

The three rules we added:

  • Every finding gets a stable ID in a single register file the moment it is recorded anywhere — with columns for source document, severity, one-line description, status, and the commit that closed it.
  • Status is one of a fixed set: open, in-progress, fixed, deferred with a reason, or wrong-finding with evidence. Never blank. "Not mentioned" is not a status.
  • A sweep is not done until a rule prevents recurrence. Fixing every instance of a problem without adding the check that stops it coming back is a temporary result.

The underlying principle is that prose findings scattered across several documents cannot be diffed, and IDs can. Before declaring any programme complete, you diff the register against the source documents and assert that every finding appears exactly once with a terminal status. It is unglamorous and it is the single highest-value process change we made.

If you take one thing from this post into your own project, take this one. It costs a markdown table and it closes the failure mode that review cannot see.

Stepped bar diagram showing 150 audit findings narrowing to about 80 that were scheduled, deferred or contradicted, and about 70 that were silently skipped despite four rounds of code review.
This is the most expensive thing that happened on the project, and no line of code was wrong. It is the failure mode a findings register exists to make impossible.

What should you check before you vibe code an app?

Check the things that are expensive to change later and invisible while the code is flowing — architecture, data model, store policy and the identifiers you cannot take back. Speed at the start is what makes month three either pleasant or unrecoverable.

The pre-flight list we would now run before writing a line:

  • Decide the architecture and write it down as rules. Not as a diagram. As sentences an agent can be held to and a reviewer can cite.
  • Lock the dependency list. Agents add libraries casually. Every dependency is a future migration, a licence question and a privacy declaration.
  • Design the data model and its migration story first. This is the one area where generated code can lose user data, and the one where we never delegated the review.
  • Settle the permanent identifiers. Bundle identifier and package name cannot be changed after publishing — we cover why in our guide to publishing a first app.
  • Check your dependency list against your privacy declaration. Every library an agent adds can change what you must declare in your App Store privacy details. Keep an inventory in the repository and update it when a dependency lands, because reconstructing it from memory at submission is how declarations end up wrong.
  • Read the store policies that apply to your category before building. Ours is a health app with a subscription and data about children, which pulls in materially stricter rules. Discovering that at submission is a rewrite; the common blockers are in our App Store rejection guide.
  • Set up the git workflow on day one. Branch per change, never commit to the main branch, PR for everything. Agents are perfectly happy to commit directly to main if you let them, and you lose the review surface entirely.
  • Write the accessibility rules before the UI exists. Retrofitting labels and touch targets across a finished app is dramatically more work than generating them correctly the first time.
  • Decide what you will never delegate. Ours: product judgement, anything touching money, anything touching user data, and the final read of a migration.
Start this clock early

And one scheduling point that has nothing to do with code. If you are shipping to Android with a new personal developer account, the closed testing requirement puts a fortnight between you and production regardless of how fast the build goes. Start that clock early; it is not a code problem and it will not be solved by working faster.

What would we do differently on the next one?

Six things, and only one of them is about prompting. The rest are decisions we made in the first fortnight that we then paid for over the following five months.

One: we would have watched the state layer instead of the screens. This is the correction we would make first, because it is the one the data proves. Eight of ten critical findings sat in persistence, sync, migration and account identity. None sat in the UI. Yet the thing we looked at every day was the UI, because it is the thing you can see — you run the app, the screen renders, it feels finished. Meanwhile the sync write that never persisted looked exactly like the sync write that did. We would now spend the daily human pass on the state layer and hand the screens to the review agent, which is the opposite of what feels natural.

Two: we would have written the first ten sections of the rules file before the first commit. Ours grew reactively, and that was defensible for the sections that encode genuine surprises. It was not defensible for architecture, layer boundaries, design tokens, error handling and logging — every one of which was predictable on day one, and every one of which we instead learned by having an agent violate it and a reviewer catch it. That is an expensive way to author a document you could have written in an afternoon.

Three: we would not have built production code against moving requirements. Five full revisions of the product spec before the architecture settled, and each revision invalidated work that already existed. The failure was not the churn — early churn is normal and healthy. The failure was treating each revision as something to implement rather than something to absorb. We would now hold the data model and architecture until the requirements stop moving and build throwaway prototypes in the gap, on the grounds that a prototype you delete costs less than a migration you have to write.

Four: we would have turned every fix into a rule the same day. One bug demonstrates this better than any argument. A sync write enqueued in an unstructured task, which could be cancelled if the app was backgrounded, was found and fixed in one pull request — and reintroduced months later in another, by an agent implementing an unrelated feature the same way. The first fix was correct. It just was not written down, so it had a half-life. A correction that lives only in a diff protects exactly the code it touched and nothing else.

Five: we would have audited for absence, not just for defects. The most expensive failure on this project produced no bad code at all. An audit generated roughly 150 findings, a plan claimed to contain them, and about 70 were never scheduled, never deferred and never even contradicted. Four rounds of review missed it, because every review looks at what was written and none looks at what was left out. We now give every finding an ID and a mandatory status in a single register, and diff the register against the source documents before calling anything complete. It costs a markdown table and it closes a hole that code review structurally cannot see.

Six: we would have verified less often and in bigger batches. There is a note in our Android rules file, dated 14 August 2026, written after a session that found and fixed two real bugs correctly and took far longer than it should have — full rebuild and reinstall cycles to check a single field, paragraphs of theorising about framework internals before running the one unit test that would have answered it, an agent spawned to write an inventory from material already in hand. None of that was wrong. All of it was slower than necessary, and all of it felt productive at the time, which is what makes it worth naming. The rule we ended up with: code a logical cluster of changes, run the gate once at the end, and never rebuild an app to answer a question the source code already answers.

The pattern underneath all six is the same. Every one is a governance decision we made implicitly, by default, while paying attention to the code. Vibe coding did not create these problems — a team of humans writing this app by hand could have made every one of them. What it did was remove the friction that normally forces the decision into the open. When implementation is slow, you notice that your requirements are still moving, because the cost of rework is visible. When implementation is fast, the rework is cheap enough to hide, and you can churn for five months without ever confronting the fact that you never settled the spec.

That is the honest summary of building this way. The bottleneck moved from writing code to deciding what good means, writing it down precisely enough to be enforced, and building the loops that catch drift. None of that is automated by a better model, because it is the part where you decide what you actually want. If you are building this way and want a second opinion on the growth side once it ships, talk to our team — and if you are still pre-launch, our pre-launch playbook is the work worth doing while the build finishes.

Frequently Asked Questions

Can you really ship a production iOS app with AI-generated code?+

Yes — ours went live on the App Store on 16 August 2026 after 431 commits and 52 pull requests. But the code generation was never the hard part. Architecture, data-model safety, accessibility and store policy compliance all remained human responsibilities, and App Store review does not grade on how the code was written.

How big should a CLAUDE.md file be?+

Ours reached about 45KB across 23 sections, but size is an output rather than a target. The test for including something is whether an agent could get it wrong in a way you would have to correct twice. Anything it can read from the code does not belong in the file.

Should an agent review its own code?+

No. An agent asked to review work it produced in the same session will largely agree with itself. Use a separate instance with no context from the implementation session, instructed to treat your rules file as law, and give it a severity scale so the merge condition is unambiguous.

Why do you need a full-codebase audit if every pull request is reviewed?+

Because diff-scoped review cannot see cross-file drift. A field added to one struct and missed in two sibling structs is invisible to a reviewer looking only at changed files — every individual PR is correct and the codebase is still inconsistent. We run a ten-dimension full audit every third sprint for exactly that class of problem.

What is the most common way AI-assisted projects go wrong?+

In our experience, findings disappearing. An audit produced about 150 findings on our project, a plan claimed to contain them all, and roughly 70 were never scheduled or contradicted. Four review rounds missed it, because reviewers check what was done rather than what was left out. A findings register with mandatory statuses closes that gap.

Does vibe coding actually save time?+

It removes typing time and moves the bottleneck to governance — deciding what good looks like, writing it precisely enough to enforce, and building loops that catch drift. On our build the biggest time losses were not slow generation but requirement churn and over-verification: rebuilding an app to check a single field, or theorising about behaviour instead of running the test that answers it.

What should you never delegate to an agent?+

Product judgement, anything touching money, anything touching user data, and the final read of a data migration. Everything on that list shares a property: the cost of being wrong is not a bug report, it is a user harmed or a launch blocked.

Sources

  1. AI Baby Growth Tracker on the App StoreThe shipped app this post draws on, live since 16 August 2026
  2. Apple — App Store Review GuidelinesThe rules a generated codebase still has to satisfy at submission
  3. Apple — Human Interface Guidelines: AccessibilityLabel, contrast and touch-target requirements that generated UI code routinely omits
  4. Swift.org — Swift 6 concurrencyStrict concurrency rules that shaped several of our banned patterns
  5. Android Developers — Guide to app architectureLayer boundaries the Android audit prompt checks against
  6. Android Developers — Target API level requirementsThe release gate referenced in dimension 10 of the audit prompt
  7. Apple — App privacy details on the App StoreDeclaration requirements that every added dependency can change

About the author

Amol Pomane Founder, Vmobify

Amol leads Vmobify, a mobile app growth agency that has driven 30M+ downloads and ranked 54K+ keywords across 300+ apps since 2013. He writes about ASO, paid user acquisition, retention, and the operational reality of scaling mobile apps in India and global markets.

Related Articles

Publishing Your First App: App Store Connect and Play Console
How-To

Publishing Your First App: App Store Connect and Play Console

Read →
App Store Rejected? The Guidelines That Actually Block Launches
How-To

App Store Rejected? The Guidelines That Actually Block Launches

Read →
Google Play Closed Testing: How New Developers Reach Production
How-To

Google Play Closed Testing: How New Developers Reach Production

Read →