Skip to content

Articles

Implementation, architecture, release work, and product lessons — written from shipping, not from docs.

App Store age rating 4+ next to a not-a-diagnosis note

Article

BrainHD’s Attention Check Is Not a Diagnosis

BrainHD is built for ADHD brains. It includes an optional attention self-check. That check is not a diagnosis. I put that on the App Store listing on purpose.

1 min readShip

  • BrainHD
  • ADHD
  • App Store
A What's New field with a single clear sentence

Article

What's New Is Not My Git Log

After Hangar and Monetra shipped I still have to write What's New. Users do not want commit messages. I write the one thing that changed in the listing.

1 min readGrowth

  • ASO
  • App Store
A visit pipeline from record to saved summary

Article

The Product Was the Pipeline, Not the Record Button

The healthcare app I worked on was a visit: record, transcribe, speaker, transcript, LLM summary, save. The record button was the start of a product sequence, not the product.

1 min readAI

  • AI
  • Flutter
  • API
A green upload next to a rejected review status

Article

Transporter Was Green. Review Was Not.

Signing and upload are not approval. Speech Route left my machine and still failed 2.1, 3.1.2, and 5.1.1. I treat a successful upload as the start of review, not the end of the job.

1 min readShip

  • App Store
  • iOS
A transcript feeding an LLM summary step

Article

The LLM Summary Had to Wait for a Transcript

Health summaries ran after transcription, not instead of it. If Flutter asked for a summary too early, the visit drifted from the React app. Order was the product.

1 min readAI

  • AI
  • API
  • Flutter
Two product ID strings that differ by one character

Article

A Product ID That Is Almost Right Still Returns Nothing

On Speech Route the identifiers matched. When they do not — a typo, a prefix, a different bundle — StoreKit still succeeds and returns []. I check the string before I debug the request.

1 min readShip

  • App Store
  • StoreKit
  • Subscriptions
Sera screenshot showing a Glow Score

Article

Sera's Glow Score Has to Make Sense in a Screenshot

Sera's product is a Glow Score and a routine that changes. If the first screenshot does not show that, the listing is a skincare generic. I treat the frame like Hangar's first screenshot.

1 min readGrowth

  • Sera
  • ASO
  • App Store
A reviewer phone next to a hidden Xcode console

Article

Reviewers Do Not See Your StoreKit Logs

Guideline 2.1 is what the reviewer sees: a paywall with nothing to buy. They do not see that your fetch returned []. I stopped debugging Xcode first on empty IAP.

1 min readShip

  • App Store
  • StoreKit
  • Subscriptions
Recording states: live, interrupted, stopped, failed

Article

Stop Had to Be a State, Not a Button Label

The healthcare visit app recorded audio in Flutter. The UI could say stopped while the plugin still held the session. Interruptions made that worse. I wanted four states: recording, interrupted, stopped, failed.

1 min readBuild

  • Flutter
  • Mobile
  • AI
A paywall price with and without a billing period

Article

$9.99 Without “Per Month” Fails 3.1.2

Apple's paywall list wants length with price. $9.99 fails. $9.99 per month passes. I take both from StoreKit so the period cannot drift from the product.

1 min readShip

  • App Store
  • Paywall
  • StoreKit
An onboarding loop that returns to the paywall

Article

Leaving the Paywall Cannot Drop You Back Onto the Paywall

Guideline 3.1.2 is not only a close button. If the paywall appears in onboarding, dismiss has to land somewhere that is not the same purchase screen again. I test that path on a device.

1 min readShip

  • App Store
  • Paywall
  • Subscriptions
A single featured story on a homepage

Article

Only One Post Is Featured. The Newest Flag Wins.

Frontmatter featured: true on more than one post still resolves to a single featured story: the newest. I do not mark five posts featured and hope the homepage becomes a magazine.

1 min readBuild

  • Next.js
  • SEO
A timeline with review sitting after the feature

Article

I Quote App Review as a Separate Line Now

Speech Route's subscription feature was not the long part. Review was. I now quote App Store review as its own work before I agree a date.

1 min readShip

  • App Store
  • Subscriptions
A paywall privacy link next to a 404 page

Article

A Privacy Link That 404s Counts as Missing

Guideline 3.1.2 wants functional links. A 404, an empty in-app browser, or no URL because you still have a file — Speech Route failed that. I open the paywall links on a phone before I submit.

1 min readShip

  • App Store
  • Paywall
  • Subscriptions
Terms of Use shown on a paywall and in App Store Connect

Article

Terms of Use Have to Exist Twice

Privacy is not the only document. Terms of Use need a working paywall link and a listing link or Apple's standard EULA. Speech Route had neither a hosted policy nor terms as URLs.

1 min readShip

  • App Store
  • Paywall
  • Subscriptions
A checklist of listing items the client has to provide

Article

The Client Sent Code. Review Wanted a Listing.

Speech Route stalled on information I did not own: banking, a hosted privacy page, product metadata. I now list those as client deliverables before I quote a submit date.

1 min readShip

  • App Store
  • Subscriptions
Four matching bundle ID fields

Article

The Bundle ID Had to Be the Same in Four Places

Certificate, identifier, profile, and the App Store Connect record all have to name the same app. On a first Mac I did not know those were four names. Builds failed before review started.

1 min readShip

  • App Store
  • iOS
App Store Connect keyword field next to a demand question mark

Article

The Keyword Field Is Permission. It Is Not Demand.

Filling Hangar's keyword field made Apple willing to show it. It did not mean people search those words or download from result 21. I stopped reading the field as a traffic plan.

1 min readGrowth

  • ASO
  • App Store
  • Hangar
App description with a standard EULA link

Article

I Linked Apple's Standard EULA From the Description

You do not need a custom contract if you use Apple's standard Terms of Use and link them from the listing. Speech Route had no terms URL at all. I decide standard vs custom before I build the paywall.

1 min readShip

  • App Store
  • Paywall
  • Subscriptions
An empty App Store Connect app list

Article

App Store Connect Was Empty When I Opened It

Speech Route had no app record when I opened App Store Connect. You cannot fetch subscriptions for an app that does not exist yet. Filling the record is submission work, not leftover metadata.

1 min readShip

  • App Store
  • StoreKit
  • iOS
An integration following another system's sequence

Article

Most Integrations Failed When I Invented the Order

CAD, anomaly detection, Strapi, StoreKit — the work was wiring to a system that already had an order. Flutter versus React was the clearest example. I write the live sequence down first now.

1 min readBuild

  • API
  • Flutter
A calendar spanning a review round trip

Article

Each Rejection Was Days, Not an Afternoon

Speech Route's eight rejections were not eight hours of coding. Each one was a review queue. I now treat a missing URL or an empty paywall as days on the calendar.

1 min readShip

  • App Store
  • App Review
A phone sign-in screen labelled sandbox instead of a personal Apple ID

Article

I Was Signed Into the Simulator With the Wrong Apple ID

StoreKit returns nothing if you are signed in with your regular Apple ID. On Speech Route I checked the sandbox and still wasted time, because the empty list looks the same as a broken fetch.

2 min readShip

  • App Store
  • StoreKit
  • Subscriptions
An unsigned Paid Applications Agreement next to an empty paywall

Article

StoreKit Was Fine. The Paid Applications Agreement Wasn't.

On Speech Route, subscription products existed and still returned nothing. The Paid Applications Agreement — tax and banking included — has to be complete before StoreKit will load a single product.

2 min readShip

  • App Store
  • StoreKit
  • Subscriptions
A crossed-out hardcoded $9.99 next to a price loaded from StoreKit

Article

I Stopped Hardcoding $9.99 on the Paywall

A hardcoded paywall price is wrong in most storefronts and can fail review if it does not match what Apple charges. After Speech Route I read price and duration from StoreKit, not from a string in the UI.

2 min readShip

  • App Store
  • StoreKit
  • Subscriptions
A checked list of App Store Connect and paywall items before submit

Article

I Don't Submit Until This List Is Done

Speech Route was rejected eight times. Hangar passed on the first try. The difference was finishing App Store Connect, the paywall, and the privacy URL before I pressed submit — not a friendlier reviewer.

2 min readShip

  • App Store
  • StoreKit
  • Subscriptions
Two URLs with www and without, an arrow collapsing them into one

Article

www or Not: Picking One Host and Making Google Agree

Google ranks canonical signals by strength: redirects and rel=canonical are strong, sitemap inclusion is weak, and they stack. This site picked the bare domain, redirects www to it, sends a canonical on every page, and lists only the apex in the sitemap. Here is how that maps to Google's document, and the three things Google says not to use.

3 min readShip

  • SEO
  • Canonical
  • Vercel
Three question marks labelled who, how, and why above a byline

Article

Google's Who, How, and Why, Applied to a Blog That Only Writes What It Did

E-E-A-T is not a ranking factor. Google says so. What its systems look for are signals of experience, expertise, authority, and above all trust, and the practical test is three questions: who made this, how, and why. Running this site's one editorial rule through those questions.

3 min readGrowth

  • SEO
  • E-E-A-T
  • Content
A dashboard score of 87 next to a Search Console chart with a question mark between them

Article

Google Wrote Down What SEO Tools Cannot Know

In June Google published a page on third-party SEO tools, services, and advice. The key sentences: tools do not have Google's internal ranking data, they cannot guarantee performance, and Google does not evaluate AEO or GEO services. How I read a tool score now, and the one source Google tells you to trust.

3 min readGrowth

  • SEO
  • Search Console
  • Tools
A site split down the middle, one half marked and one half untouched

Article

Google's Site Reputation Policy Now Stops at the EEA Border

Since August 30, a site reputation manual action changes results for searchers outside the EEA and does nothing for searchers inside it. Google also wrote down the four things it looks at in third-party content. Here is what changed and why a one-author site should still read the list.

3 min readGrowth

  • SEO
  • Google Search
  • Spam Policies
Three gauges labelled LCP, INP, and CLS

Article

There Is No Page Experience Ranking Signal. There Are Several.

Google retired the idea of one page experience system. Its guidance now says core ranking looks at a variety of signals, content wins even when experience is poor, and experience decides ties. The Core Web Vitals thresholds, LCP 2.5s, INP 200ms, CLS 0.1 at the 75th percentile, and the one page on this site that has to earn them.

3 min readBuild

  • Core Web Vitals
  • Performance
  • Next.js
A search result card whose title differs from the page headline beneath it

Article

Why Every Post Here Has Two Titles

Google generates the title you see in results from the title element, the visible headline, headings, og:title, and even other sites' anchor text. This site sends a separate seoTitle for the title element and shows the headline on the page. Here is why, when Google rewrites titles, and the one way that setup can go wrong.

3 min readGrowth

  • SEO
  • Titles
  • Next.js
A tiny stack of pages next to a very tall one, with a crawler icon on the tall stack

Article

Crawl Budget Is Not Your Problem. Google Gives the Numbers.

Google's crawl budget guide names the sites it applies to: a million-plus pages changing weekly, ten thousand-plus changing daily, or a large pile of URLs stuck at Discovered, currently not indexed. This site has twenty-eight URLs. What crawl budget is, what wastes it, and what everyone else should look at instead.

3 min readGrowth

  • SEO
  • Crawling
  • Search Console
A ranking chart with two long shaded bands for the March and May core updates

Article

Two Core Updates, One Retired System, and What Google Says to Do About a Drop

The March and May 2026 core updates each took about twelve days. Google's own guidance on what to do afterward is more specific than most people think: wait a week, compare positions, treat a move from 2 to 4 differently from 4 to 29, and delete as a last resort. Plus the helpful content system officially moving to the retired list.

3 min readGrowth

  • SEO
  • Google Search
  • Core Updates
An App Store product page with a 13+ badge and a Social Media descriptor

Article

Apple Now Asks Every App Whether It Has a Social Feed

Since September the age rating questionnaire in App Store Connect requires an answer about social media capabilities. Say yes and your app gets a Social Media descriptor and a 13+ rating. Here is the definition, the under-13 exception, and the other September changes.

3 min readShip

  • App Store
  • App Review
  • Age Ratings
A wide TV frame next to a phone-sized game layout

Article

The Same Flutter Game Had to Run on Android TV

The game we built in Flutter was not only phones and web. It had to work on Android TV. Adapting that Flutter app for TV was one of the hardest parts of the project.

1 min readBuild

  • Flutter
  • Android TV
  • Mobile
Hangar marked approved on the first App Store submission

Article

Hangar Went Live on the First Submission

Hangar passed App Review on the first submission. The pre-review checks I run now: paywall, Guideline 2.1, and a hosted privacy URL.

4 min readShip

  • App Store
  • Hangar
  • iOS
MDX post files in a folder instead of a CMS

Article

Six Dependencies Instead of a CMS

I write posts as MDX files in the repo. Here's every package that setup needs, what each one is for, and why the build fails on a bad post.

3 min readBuild

  • MDX
  • Next.js
  • TypeScript

Back to home