Ask

Reyna Salcedo

@finops_reyna

I do finance and operations at a nine-person company and taught myself enough cloud pricing to challenge our bills. I can usually tell you which SKU is eating your money before the engineers can.

30 credit Contributor

From answers
0
From questions
30

Joined March 14, 2024 · 0 followers · 0 following

nightly cron stopped running 23 days ago and nothing told me

If you do not want another service in the stack: a job_runs table with one row per successful completion, and /health/jobs returning 500 when max(finished_at) < now() - interval '26 hours'.

You almost certainly already have an uptime monitor screaming at you when an endpoint returns 500. Now it screams about cron too, and you added one table and one route.

94 · in/queues-and-jobs ·

shut the api off 3 weeks ago and stripe still billed 41 accounts last night

The fee is around $15 in the US and yes, it lands on won and lost alike in most cases. The number that gets you attention is disputes over transactions on a rolling window: with 41 charges and 6 disputes you are at roughly 14%. That is not "flagged eventually", that is "someone reads your account this week". Get ahead of it and leave a note explaining the shutdown.

58 · in/sunset-or-sell ·

storekit 2 transaction updates missed a renewal, user lost pro for 3 days

It is genuinely not enough and it never was. Transaction.updates delivers while your process is alive. A renewal at 03:00 on a phone in a drawer has nowhere to be delivered to.

That listener exists for transactions that complete outside your normal purchase flow - a Family Sharing grant, an interrupted purchase resuming, a purchase that finished while you were backgrounded. It is not a renewal feed and treating it as one is the single most common subscription bug I see.

Two things are the answer:

  • App Store Server Notifications to an endpoint you own. That is your source of truth for renewals, billing retry, grace period, refunds and cancellations. It arrives whether or not the app has ever been opened again.
  • On every launch, read Transaction.currentEntitlements and reconcile. That is exactly what fixed itself while she was watching.

Server for truth, client for speed.

172 · in/app-review-and-iap ·

navigationstack path enum or a coordinator for 31 screens with deep links

Practical note on universal links: test the cold-start case specifically. Link arriving while the app is already running is easy. Link arriving as the process launches, before your root view has appeared, is where you find the ordering bug - you set the path and something resets it during initial layout. Buffer the incoming route until the root is ready.

12 · in/swiftui-compose ·

rejected 3.1.1 for missing restore purchases, the button is right there

3.1.1 is a big bucket - missing restore, purchases routed outside the store, unlocking content bought elsewhere, and yes sometimes missing links. The rejection text says which one, and theirs says restore.

Adding the links is a good idea because you need them anyway, but telling someone on their third rejection that the guideline "always" means the one thing it does not mean here is how people end up on their fourth.

16 · in/app-review-and-iap ·

one duplicate row made my consumer send the same receipt 10 times

Separate "record that it happened" from "do the thing".

Handler one writes the fact in a transaction and nothing else. A second step - an outbox row picked up by another job - sends the email. Then a mail provider outage does not re-run your accounting, and a constraint violation in accounting does not send anything at all.

It is one more moving part and it is worth it the first time your email provider has a bad afternoon.

103 · in/queues-and-jobs ·

buyer pulled out after diligence found 62% of mrr in one account

Fair, and it would have been the first thing anyone modelled.

Nobody is buying your MRR. They are buying the probability of that MRR existing next year. One customer with no contract, on a stale price, who has never been asked to re-sign anything, is a coin flip - and if they leave, the multiple you applied to the remaining $1,550 was wrong too, because the remainder has to carry all the fixed costs on its own.

Two things worth more than any listing copy: get that account onto a twelve month term before you list again, and disclose the concentration on page one next time. You burned fourteen days of somebody's diligence budget and that gets remembered in a small market.

191 · in/sunset-or-sell ·

listing at ~2.4x or handing it to my two biggest customers for free

Mechanics people forget until the week of:

  • payment processor accounts do not transfer between legal entities. New account on their side, and customers either re-enter payment methods or you go through the provider's documented migration process, which takes weeks
  • domain, DNS, and the email sending domain, which has reputation attached to it that does not move
  • every API key in a .env you have not looked at since 2022
  • any store listings, if there are apps
  • who answers support@ on day 31

55 · in/sunset-or-sell ·