I’ve shipped 3 apps on Google Play. Here’s every place I got stuck along the way, laid out in the order you’ll run into them during release.

No generic advice. Every item here is something I actually stepped on and that actually stopped me. Each one links to a post with the full details.

0. The baseline: you can’t compress the time to launch

First, the biggest constraint.

If you’re an indie developer on a new developer account, you can’t go to production until you’ve run closed testing “with at least 12 testers, for 14 consecutive days.”

Working backwards, it looks like this:

Recruit 12 testers              a few days or more (the least predictable part)
Opt-ins complete = D0
14 consecutive days of testing  D0 + 14
Production access review        a few days
────────────────────────────────
About 29 days at the very least

If you’re planning to “release next month,” count from today and check that it actually adds up. Whether or not the code is finished, the calendar days are required. (details)

1. Build

It only crashes in release builds

Three times I had an app that ran with flutter run, passed flutter analyze, and still crashed only in the release build. The causes were mostly around R8’s obfuscation and shrinking. Everything working in debug guarantees absolutely nothing about release. (details)

Lose your signing key and you can never update that app again

If you lose the upload key, you can apply to recover it. But if you manage the app signing key yourself and lose that, updates under that package name are over. Keep a backup somewhere other than your dev machine. I keep an external backup.

Don’t put in AdMob production IDs until right before applying for production

I develop with test IDs. If you click your own ads during development with production IDs in place, that’s grounds for AdMob account suspension.

Treat this as a separate matter from the testing period. During closed testing I update the build every time something’s ready, but I make one exception: the AdMob IDs get swapped in only right before I apply for production. I leave the old test IDs in the code as comments so I can roll back.

2. Store listing assets

Screenshots: “long side ≤ short side × 2”

What this means is that screenshots taken on a real device won’t pass as-is.

ImageRatioResult
Raw screenshot from a moto g24, 720×16122.239Rejected
iPhone 15 Pro equivalent, 1179×25562.168Rejected
1080×19201.778OK

The requirements: JPEG or 24-bit PNG (no alpha), 320–3840px per side, and the long side must not exceed twice the short side.

Screenshots from today’s tall phones violate the spec straight out of the device. I handle it with a script that crops off the navigation bar, fits the image into 1080×1920, and fills the left and right with the app’s background color. (details)

By the way, the “1179×2556” written in our internal instructions turned out to be a mix-up with the App Store requirements. Check the official help pages, not whatever docs you have lying around.

A listing video is worth preparing

You can add one video of about 30 seconds. You don’t need editing software; a screen recording from a real device plus ffmpeg is enough. (how I made mine)

The requirements are “a Public or Unlisted YouTube video / ads turned off / no age restriction.” If it’s Private, Play Console rejects it.

3. App content (this is where it gets real)

Data safety is checked for “three-way consistency”

The declaration form, the privacy policy, and the actual implementation. If those three disagree, you fail.

For example, if you include ads, you need the full set of three: the “Contains ads” declaration, the advertising ID declaration, and a mention in your privacy policy. Missing any one of them is an inconsistency.

The moment a policy URL breaks, review stops

This blocked submission for 2 of my apps.

When I renamed my GitHub account, every URL for the policy pages I’d published on GitHub Pages changed, and the “data deletion URL” registered in Play Console was now a 404. The moment you hit the send button, you get this:

データの削除ページで「ページが見つかりません」エラーが発生しました

(Roughly: “A ‘page not found’ error occurred on your data deletion page.”)

Fixing the files and fixing the settings fields in external services are two completely different things. If you rename your account or a repository, go through Play Console for every one of your apps. (details)

Your countries may be left at the default: Japan only

If you want people outside Japan to try your app, always check the “Countries / regions” tab. With Japan-only distribution, someone abroad who opens your store URL gets “This item isn’t available in your country” and can’t install it. (details)

4. Monetization

Free trials aren’t inside the base plan

You’ll hunt all over Play Console for this. It’s the “Offers” tab.

Open the subscription product → "Add offer" on the base plan row
  → Offer ID → free trial length in days → Activate

No matter how long you stare at the base plan’s edit screen, it’s not there.

If the “Offers” column in the product list shows –, that product has no trial. It’s worth remembering as the one place where you can check whether you only think you set it up.

Pick the wrong promo code type and nobody can redeem it

If you don’t know this, you will get burned.

TypeWhere it can be redeemedQuantity
One-time codesCan be redeemed from the Play StoreSubscriptions: 10,000 per product per quarter
Custom codesOnly from a redemption UI inside your own app—

If you haven’t built a code redemption UI into your app, nobody can redeem custom codes. If you’re handing codes out to, say, contest judges, choose one-time codes.

I had originally made custom codes my main option and only caught it at the last minute.

Does the expiry date overlap with when the recipient will use it?

My issued codes had an end date of September 30, and the recipients would be using the app from October 1 to October 13. Not a single day overlapped. I was so relieved the codes existed that I never looked at the dates.

Is a canceled purchase shown as a “failure”?

My app still had a bug where simply canceling a purchase showed “Purchase failed.” The SDK threw a different exception type than I expected, so it slipped right past my catch.

Canceling is a normal path. If you show it as an error, a first-time user sees “a broken app.” (the time a billing failure went unnoticed for 11 days)

5. Submission and release

Saving alone doesn’t send anything to review

Play Console works in 2 stages.

① Save your changes on each screen
② Go to "Publishing overview" and send all the saved changes for review together

Forget step ② and everything looks correct on screen but never, ever reaches production. I hit this once with a change to countries and only noticed a few days later.

Whenever you change anything in Play Console, finish by opening “Publishing overview.” Make that a fixed step in your process.

Don’t consolidate into one build during closed testing

I upload each build as soon as it’s ready. It serves as evidence that testing is ongoing. In my experience, uploading multiple times to the same track has never broken the 14-day count.

As a rule of thumb, I bump the version and re-upload 2–3 times during the testing period.

Before submitting, open your store URL in an incognito window

On my own device I’m signed in with my own account, I’m registered as a tester, and of course everything works. What’s broken is always outside your own environment.

  • Does the install button show up? (countries, publishing status)
  • Are the terms and privacy policy links not 404s?
  • Does the video play? (it isn’t set to Private?)

It takes 3 minutes. I skipped it and hit several of the problems above in production.

Checklist

Copy it and use it.

[ ] Worked backwards from closed testing: 12 testers × 14 days
[ ] Launched the release build on a real device and ran the main flows
[ ] Backed up the signing key somewhere else
[ ] AdMob still on test IDs (production IDs right before applying for production)
[ ] Screenshots: long side ≤ short side × 2 (1080×1920 recommended)
[ ] Listing video is Public/Unlisted, ads off, no age restriction
[ ] Data safety = declaration / policy / implementation all match
[ ] Terms and deletion page URLs are live (including the fields in the Console)
[ ] Checked countries
[ ] Free trial activated in the "Offers" tab
[ ] Promo codes are "one-time codes" / expiry overlaps the usage period
[ ] Canceling a purchase isn't shown as "failed"
[ ] Sent for review from "Publishing overview"
[ ] Opened the store URL in an incognito window and checked it

I’ll keep adding to this list every time I step on something new. The details for each item are in the individual posts. For how the dev setup itself works, see this post; for what it all actually cost, see this one.