Sander Korf
  • Portfolio
  • Resume

Sander Korf

Dutch Full Stack AI Engineer helping businesses cut costs through intelligent automation. 13+ years turning expensive manual processes into systems that work 24/7. Based in Amsterdam, available for freelance projects.

Navigation

  • Portfolio
  • Resume

Legal

  • Cookies
  • Privacy Policy
  • Terms and Conditions

Blog

Algolia

  • Algolia InstantSearch INP and object identity
  • Algolia merch by Sanity _type, not title
  • Algolia InstantSearch search key, not admin

Vercel

  • Next.js empty Suspense fallbacks wreck CLS P95
  • Vercel Workflows: skip closed eToro legs

eToro

  • eToro deploy vs rotate: idle cash not rotate
  • eToro search 404: query fields, not ticker
  • eToro v2 opens at-most-once, settlement
  • eToro LLM overlay ±10pp: critic cannot veto

Next.js

  • next-intl useTranslate skip links above fold
  • next-intl owns Klaviyo nl-NL email copy

Firebase & Expo

  • Staging deep links must not hijack production
  • Turn off Worklets Bundle Mode for EAS SHA-1
  • Reclaim Firebase orphan without password wipe

Centra & Klaviyo

  • Centra to Klaviyo tags need a plugin contract

About

  • Full Stack AI Engineer in Amsterdam

© 2026 Sander Korf. All rights reserved

94719489

  • Applied AI
  1. Home
  2. Blog
  3. Staging deep links must not hijack production

Staging deep links must not hijack production

Staging and production must not share the same deep-link host or URL scheme. Reusing production links sends QA to the live app and makes staging look broken.

Sander KorfPublished August 20, 20262 min read
expoeasdeeplinks

This was a mobile consumer app with two binaries

Expo. EAS. A production app on the store. A staging app for QA. Deep links for reset-password, magic share, campaign landing. If both binaries claimed the same Universal Link, iOS picked one. It picked production. Staging "did not work." Production opened a path it did not know.

What they would notice if it failed

QA taps a staging email. The store app launches. Blank screen, or yesterday's production route. Or the reverse: a customer taps a marketing link and lands in an internal build that asks them to log into a fake catalog. That last one is how you get a one-star review that mentions "test."

The puzzle: one host is not two apps

I reused the production associated domain on the staging Expo profile because "it is the same path." Apple does not care that your EAS profile is named staging. The AASA file on that host lists app ids. If both bundle ids are there, or if only production is there, the wrong binary wins.

Custom schemes have the same bug. myapp://reset on both flavors means whichever app is installed handles it. On a device with both, you are playing dice.

Split the claim.

{
	"expo": {
		"scheme": "myapp-staging",
		"ios": {
			"bundleIdentifier": "com.example.app.staging",
			"associatedDomains": ["applinks:staging.example.com"]
		},
		"android": {
			"package": "com.example.app.staging",
			"intentFilters": [
				{
					"action": "VIEW",
					"autoVerify": true,
					"data": [{ "scheme": "https", "host": "staging.example.com" }]
				}
			]
		}
	}
}

Production keeps myapp, the store bundle id, and example.com. Staging emails and Klaviyo test flows must emit the staging host. AASA on each host lists only that host's app id.

I also stopped putting staging bundle ids in the production AASA "just in case." That is how a leftover QA install hijacks a customer link.

Two apps. Two hosts. Two schemes. Deep links are a namespace. Treat them like one.


Happy coding!
Sander