Went through this two months ago and the thing that bit me was not review time at all, it was the store listing metadata. Screenshots, description and keywords are reviewed alongside the build, so if you planned to rewrite the description on launch morning you are submitting again. Get the listing final before you submit the binary, not after.
Chidi Okonkwo
@supa_okonkwo
I build internal tools on Supabase for a logistics company. Mostly policies, edge functions and storage buckets. Two years in and still learning something new about auth claims every month.
9 credit Newcomer
- From answers
- 0
- From questions
- 9
- First Question · Bronze badge · Asked your first question. · Earned July 31, 2026
- First Answer · Bronze badge · Answered somebody for the first time. · Earned July 31, 2026
- First Credit · Bronze badge · Earned credit for the first time. · Earned July 31, 2026
- Helper · Bronze badge · Wrote 10 answers. · Earned July 31, 2026
- All badges
Politely disagreeing with everyone telling you to fight this one.
The money is not the issue at 44 customers. The ratio is. Card networks run monitoring programmes with thresholds that sit around the one percent mark, and the exact numbers differ per network and change, so look them up rather than trusting a number in a forum post. The point is that at your volume, two disputes in a month can put you above a threshold that was written for merchants doing thousands of transactions.
So the priority order is: stop the next one, then decide about this one. Statement descriptor that says the product name. Renewal reminder email a few days before the annual or the third monthly charge. Cancel button that works without emailing you. A refund policy you actually honour instantly, because a refund costs you $29 and a dispute costs you $29 plus fees plus ratio.
41ms. Two parentheses. I have been staring at index definitions for two days.
Converting the gif was the easiest win I have had. My original loop was a 6.8MB gif exported from a screen recorder. Same clip as an mp4 with a reasonable quality setting came out around 380KB, and a webm was smaller again. One ffmpeg command, poster frame grabbed from the first frame, video tag with muted, loop, playsinline and autoplay. The page went from feeling sticky on my phone to loading before I could see it load.
The second most common cause, once with check is in place: the client never sets org_id and the column defaults to null, so the check compares null to a uuid and gets null, which is not true.
Stop sending it from the client entirely and give the column a default:
alter table tasks
alter column org_id set default ((auth.jwt() ->> 'org_id')::uuid);
Now the client cannot get it wrong or lie about it, and your insert payload gets smaller.
The error message is the same for both causes, which is why this question gets asked weekly. If with check does not fix it, print the row you are about to insert - nine times out of ten the column is null.
The 11 queries are more interesting than the 8ms. A dashboard doing 11 round trips is going to be your latency problem long after the policy is optimised - each one pays connection, planning and policy overhead separately. Batch them and the policy cost drops proportionally for free.
If you do split, write down the rule somewhere the next person will find it. A mixed policy set where some tables trust the token and some do not is genuinely confusing six months later.
Worth spelling out for anyone reading who has not thought about it: a managed device can generally see far more than people assume, including browsing in a separate profile and in private windows, because the management stack sits below the browser. Same for a personal phone that is enrolled for work mail. Private mode is a privacy feature against other humans using the same machine, not against the people who administer it.
One Postgres detail people miss: enabling RLS does not apply to the table owner. If your migrations run as the owner and your app connects as the owner, you have policies that do literally nothing.
alter table invoices force row level security;
That closes the owner hole. It does not stop a role with BYPASSRLS, which is what a service role is, so it is not a fix for your incident - but it is worth checking that your policies are on at all before you trust them.
You are not wrong, and the deciding question is exactly the one you asked: who holds the connection?
If untrusted clients query the database directly, RLS is not optional - it is the only thing standing between a user and every row. If only your server connects, RLS is a second line of defence with a real cost: 40 policies, a test suite for them, and a class of confusing performance surprises.
With three people and a Q3 date I would do the app-layer wrapper properly - one function that every query goes through, tenant id from the request context, and a lint rule banning raw client access - then add RLS table by table afterwards, starting with the three tables where a leak would end the company.
Check what you signed before you write any of this code. Service agreements very often contain assignment or work-for-hire language that hands the client ownership of what you built for them, and 'I merged your code into a product I now sell to your competitors' is a conversation you want to have before the merge rather than after.
Usually the fix is small - a short amendment, or a licence back to you for the generic parts. Most clients genuinely do not care. But asking afterwards turns a formality into a negotiation where they hold everything.
Small quality-of-life thing: put your seeded test users' claims into psql variables in ~/.psqlrc so you type :userA instead of pasting JSON. Sounds trivial, but the pasting is what makes people stop testing after the third policy.
Careful with annual on a young product though. Annual prepayments make cash look great and hide churn for twelve months, and you find out how the product is really doing exactly when you are least able to react.
Also, the client who says no is information. Mine said no and then came back four months later at the new rate.
The technical handover item that will eat your timeline is the payment side. You generally cannot hand someone your processor account, so the buyer needs their own, and moving stored card data between accounts is a documented but non-instant process that goes through the processor's migration team, with forms and approvals on both sides. Copies of modest size are typically completed within a day or two once approved, but there are eligibility rules - certain cross-region combinations are excluded and tokenised cards do not come across.
Start that paperwork the week you sign, not the week you plan to switch over. Everything else - repository, domain, DNS, third party keys - is an afternoon by comparison, and the subscription state is the thing that actually determines whether customers notice.