Shipping Is the Cheap Part: The Carrying Cost of an iOS App

This week Veyl, a travel-day tracker I launched in the spring, came off the App Store. It is not the first app I have retired this year. Every app that stays in the store is a standing claim on future hours, and most indie developers — me included, for longer than I would like — never write that claim down. This is the ledger I now keep for each app, and the rule that decides which ones stay.

The App Store is built to make shipping feel like a finish line. The build clears review, the listing goes live, a screenshot goes on LinkedIn. In accounting terms, what has actually happened is that you have acquired an asset with an obligation attached — and unlike most assets, this one does not depreciate quietly in a corner. It has to be maintained in order to keep existing, and the maintenance is billed in the one currency a small studio cannot borrow: the hours of the person who builds everything else.

I spend the finance half of my week telling founders to look at carrying costs — what it costs to hold inventory, to keep a market open, to keep a legacy product alive for a handful of customers. It took me an embarrassingly long time to run the same arithmetic on my own portfolio. At its widest it had twelve apps live at once. Today it has nine, and the number is smaller by decision rather than by accident.

What "alive" actually costs

Start with the cost that arrives whether or not you touch the app. Every June, Apple announces a new version of iOS; every September it ships; and the following spring, App Store Connect starts requiring that new submissions be built with the new SDK. So at least once a year, each app is rebuilt against a toolchain and an operating system that did not exist when it was written, and something always moves. A deprecated API that used to warn now fails. A permission prompt changes its wording, or its rules. A system control renders differently, and the layout that was fine on last year's screen sizes is not fine on this year's. A privacy manifest needs a new entry because a library you depend on started calling something Apple now wants declared. None of this is feature work. It is the rent.

Then there is the churn underneath. A subscription SDK ships a breaking major version. An analytics library drops the minimum iOS version you still support. Swift itself moves, and a warning you ignored for a year becomes an error. If the app has a server, the server has its own calendar: a runtime reaches end of life, a certificate expires, a hosting provider restructures its pricing. In my experience the floor for all of this — for an app that gets no new features at all — is a few days a year. That sounds small until you multiply it by twelve and notice that it is more than a month of every year spent keeping things exactly as they were.

The third line is people. Reviews need answers, because an unanswered one-star review is the first thing a prospective user reads and the second thing a prospective client reads after they look up the studio. Support email arrives at the rate of the user base, but the hard emails — a refund dispute, a data question, a user who cannot restore a purchase — take an hour each regardless of how many users the app has. The privacy policy and terms have to describe what the app does now, not what it did at launch. And there is the administrative layer nobody budgets for: agreements to re-accept in App Store Connect, tax forms that expire, certificates and provisioning profiles that lapse on a schedule with no relation to your roadmap.

The fourth line does not fit in a spreadsheet, and it is the most expensive. Attention. The app you are not thinking about is the one that fails review at eleven at night in the week you are trying to ship the app that is actually growing. A portfolio is not twelve independent products. It is one person's working memory, and every live app takes a slot in it whether or not it has earned one.

Why every indie portfolio looks profitable

Here is the arithmetic most developers never run. Take an app that makes a modest amount — a few hundred dollars a year after Apple's commission and after refunds. On a spreadsheet with revenue on one side and server costs on the other, it is profitable. It has been profitable every year. Now put a price on the developer's time. Not a consultancy day rate; any rate at all. Two days a year at a junior contractor's rate is already more than the app makes, and that is before the support, the reviews, or the attention. The app has been losing money in hours the whole time while showing a profit in dollars, because the only person's time it consumes is the one person who prices it at zero.

This is exactly the mistake founders make with the feature three customers use and the market that was opened in an ambitious year. Attention is priced at zero, so nothing ever fails the test. The moment you write a number next to your own hours — any number — the portfolio splits into two groups, and the second group is larger than you expected.

A live app is not an asset because it exists. It is an asset when what it returns — in revenue, in what it teaches you, in what it does for the apps beside it — exceeds the hours it will take from them.

The ledger I keep for each app

One page per app, updated quarterly. The revenue side is trailing twelve-month net proceeds — after Apple's commission and after refunds, since that is the cash that actually arrives — and the direction they are moving. Direction matters more than level. A small app that has grown three quarters in a row is a different object from a larger one that has declined for three.

The cost side has the four lines above: hours on the OS cycle, hours on support and reviews, direct third-party cost — servers, APIs, subscription infrastructure — and a rough attention score I would not defend in front of an auditor but which has never once been wrong about which app would interrupt a launch week.

Then two lines that are neither cost nor revenue. The first is what the app returns to the rest of the portfolio. Several apps in the studio run their analysis entirely on the phone, and each was cheaper to build than the one before because the learning carried over; I wrote about what that involves in a post on on-device AI. An app that teaches the studio a capability it then sells — to users in the next app, or to a client as forward-deployed engineering — is paying rent in a form that never appears in App Store Connect. The second line is the plan: the next specific thing to try, with a date. A paywall change, a feature, a market. If that line has been empty for two consecutive quarters, the app is not being maintained. It is being kept.

The three tests

The first test is whether the app covers its own carrying cost with something to spare, with my hours priced at a real number. An app that fails this on trailing twelve months is a candidate, not a decision. It moves to the second test.

The second is whether it has a trend or a plan. Growing apps stay, even when small; the trend is the evidence, and a small growing app is worth more than a large declining one. Flat or declining apps with a dated experiment attached stay until the experiment has run — and I have learned the hard way, mostly through paywall changes, that the experiment needs a number it has to hit and a date it has to hit it by, or it is not an experiment but a reason. Flat or declining apps with nothing attached have already told you what they are.

The third test is whether the app earns its place for a reason beyond its own numbers. It shares an audience with another app and sends users across. It demonstrates a capability the studio sells. It is an app I use every day and cannot replace — though "I use it myself" is the weakest of these, because it is the argument that most reliably keeps a losing app alive. A wide portfolio of unrelated apps is not a studio; it is a list of ideas, and users and the store both treat it accordingly.

The quarterly app review, in one paragraph

For every live app: trailing twelve-month net proceeds and their direction; hours logged against it, priced at a real rate; direct costs; what it returns to the rest of the portfolio; and the next experiment, with a number and a date. Each app leaves the review with one of three words next to it. Invest means it gets feature work. Maintain means it gets the OS cycle and nothing else. Retire means it comes off sale at the next cycle. An app that has been "maintain" for three consecutive quarters with declining proceeds and no plan is "retire" by default, and it has to argue its way back.

How to retire an app properly

Retiring is not deleting, and the difference matters. Removing an app from sale in App Store Connect takes it out of search and off the storefront, but people who already have it keep it, it keeps running on their phones, and the listing can be restored if you change your mind. Deleting the app record is a different act, and I have never done it. Retire first. Delete never, or years later.

The sequencing is where people get hurt. If the app has subscribers, stop selling new subscriptions before anything else, and decide what happens to the ones that exist: honour them to the end of their term if the app can keep running that long, and refund them if it cannot. If the app has a server, give it a shutdown date, put that date inside the app and in the listing before the listing disappears, and give users a way to take their data with them. An app that runs entirely on the device is far simpler to retire — which is one more argument for on-device that I did not anticipate when I started making that choice.

Then the pages. When Veyl came off the store this week, its landing page stayed up with a retirement notice, its App Store buttons were removed, and it was excluded from search engines — but it was not deleted, because links to it exist in places I do not control and a dead link is worse than an honest page. The privacy policy and terms stayed exactly where they were, at the same addresses registered in App Store Connect, because someone who still has the app installed is still entitled to know what it does with their data. Every app I have retired gets the same treatment. It costs almost nothing, and it is the part that is easiest to get wrong.

Keep the code and the bundle identifier. Archive the repository, tag the last release, note the last SDK it was built with; the capability may be worth more inside a different app than it was inside this one. And write down, in one paragraph, what the hypothesis was, what actually happened, and what you would reuse. For some apps that paragraph is the entire return, and it is worth more than the revenue they made.

The same problem, at company scale

I have written this about apps because that is the portfolio I own outright. The problem is the one I walk into at every company I advise, at a different scale. There is always a feature that three customers use and that every release has to be tested against. There is a market opened in an ambitious year that is now a tax filing, a localisation, and a support queue. There is a legacy plan nobody can be moved off, an integration nobody remembers building, a product line kept alive because closing it would mean saying it failed. Each of these is a live app in someone else's portfolio, and each is being kept because the attention it consumes is priced at zero.

A finance function that only reports on what is being built is doing half the job. The other half is the retirement list: the carrying cost of everything the company still holds, priced in the hours of the people who could be doing something else, reviewed on a calendar rather than when a crisis forces it. It is rarely the founder's favourite meeting. It is usually the one that moves burn the most.

Veyl was a good app. It did one thing, it did it entirely on the phone, and I am glad I built it. It is also gone, and the nine apps that remain are better for the hours it gives back to them. Shipping was the cheap part. It always is.