I was writing an update note for one of my wallpaper apps when I stalled on the date field. The work itself was two days from done. Even so, I couldn't make myself type "next week."
I saved the note with the date left blank. That was the night I realized the waiting had never been part of my estimate, because none of it was in my hands.
Once a Rork app takes shape, it's easy to feel that the only thing left is to ship. For a first release, though, the waiting often takes longer than the building.
The thing I'd like to say up front is that "release in a week" holds only when several conditions line up. Below, I break the waiting into pieces so you can work out your own date.
Four kinds of waiting
Saying "review takes a while" hides where the days actually go. I count four separate waits.
| Wait | Whose hands | How I estimate it |
|---|---|---|
| Apple Developer enrollment approval | Apple (varies for individual vs. organization) | A range of a few days; longer for organizations |
| Google Play closed testing | Yours (gathering testers) and the calendar (consecutive days) | Personal accounts have a required tester count and number of consecutive days in Google's help pages |
| First App Store review | Apple | Often short, but I budget a rejection separately |
| First Google Play review | For a new account, I assume it runs longer than usual |
These numbers shift with the season and the state of your account. That's why the table names whose hands the wait is in rather than promising days. Please confirm the actual figures in the official help pages on the day you plan.
The 14 days people miss
If you're building as an individual, this is where I think estimates go wrong most often. Before production access, a personal developer account has to run a closed test with a required number of testers for a required number of consecutive days. As far as I know that's 12 testers for 14 consecutive days, but please check the current figures.
The clock starts only after all the testers are in. So the estimate shouldn't be 14 days. It should include:
- the days it takes to gather testers (the most variable part)
- the consecutive days after that
- some slack for someone dropping out
How fast people sign up is not something I control. I write that step as a range — "three days to three weeks."
A script that works the date backward
Adding these up by hand each time is how I ended up frozen at the date field. Put the minimum and maximum of each wait into the script below, and it lays out both paths, iOS and Android, side by side.
// release-eta.ts
// Purpose: work backward from wait ranges to an earliest date and a late-case date
// Run: npx tsx release-eta.ts
// Every number is my own assumption, not an official guarantee. Check and overwrite them.
type Wait = { label: string; min: number; max: number }; // calendar days, not business days
const DAY = 86_400_000;
const addDays = (d: Date, n: number) => new Date(d.getTime() + n * DAY);
const fmt = (d: Date) => d.toISOString().slice(0, 10); // UTC, so it may be off by a day
function eta(start: Date, steps: Wait[], rejectBuffer = 0) {
const sum = (k: "min" | "max") => steps.reduce((a, s) => a + s[k], 0);
return {
earliest: addDays(start, sum("min")),
latest: addDays(start, sum("max") + rejectBuffer),
};
}
const start = new Date("2026-10-13"); // the day you begin (today is fine)
// iOS path: enrollment approval -> review
const ios = eta(start, [
{ label: "Apple Developer enrollment", min: 1, max: 7 },
{ label: "First App Store review", min: 1, max: 4 },
], 3); // resubmission time if rejected once
// Android path: gather testers -> consecutive days -> review
const android = eta(start, [
{ label: "Gather testers", min: 3, max: 21 },
{ label: "Closed-test consecutive days", min: 14, max: 14 },
{ label: "First Google Play review", min: 2, max: 7 },
], 3);
console.log("iOS earliest:", fmt(ios.earliest), " late case:", fmt(ios.latest));
console.log("Android earliest:", fmt(android.earliest), " late case:", fmt(android.latest));
// If you ship both, the slower path decides the date
const both = new Date(Math.max(ios.latest.getTime(), android.latest.getTime()));
console.log("Both ready (late case):", fmt(both));Starting on October 13, 2026 with these assumptions, the Android path becomes the heavy one: 19 days at the earliest, and 45 days — more than six weeks — in the late case.
What helps more than the numbers is seeing which path is setting the date. Before you write "a week," look at that output once.
Phrasing that makes the promise lighter
A longer estimate isn't something to be discouraged by. Changing how you say it makes the promise much lighter.
- Say "rolling out from the second week of the month," not a single date
- Give iOS and Android separate dates instead of insisting on both at once
- Use the waiting days to move forward on what's in your own hands, such as screenshots and store text
How to control the moment of release itself is covered in another piece (If you can't predict when review ends, decide the release date outside the review queue). That one is about choosing the moment; this one is about knowing when you can get there.
Some values can't be changed later, and the bundle ID is one of them — best settled before the waiting starts. The post on deciding your app ID before the first release pairs well with this one.
Put the date after the other side's waiting
Since the night I stalled at the date field, one rule has changed.
Put the promised date after the other side's waiting has been added, not after my own working days.
These days I pick the date in announcements from the late-case column. If I ship earlier, I get to be pleasantly surprised.
For your first task tomorrow, put your start date and the figures you confirmed in the help pages into the script and run it once. Notice how far the result lands from your current "one week." Starting from that gap is enough.