shipcheck

Guideline 2.3.1: hidden features, and what counts as one

Updated September 7, 2026 · published by Baker Ventures LLC · sources cited inline

2.3.1 prohibits hidden or undocumented features, and it explicitly reaches functionality switched on after review.

That last clause is the one that catches modern release processes. Remote config, feature flags, server-driven UI and staged rollouts are all ordinary engineering practice and none of them is banned. What the guideline targets is using them so that the app users get is materially different from the app that was reviewed.

The practical test: if the code is in the binary and can be enabled without a new submission, a reviewer should have been told it exists. Disclose it in the review notes and be able to demonstrate it on request.

The most common concrete finding is not strategic at all. It is a debug menu shipped in a production build: undocumented functionality that often exposes internal state, test accounts or sandbox payments. The fix is to strip it from release builds, not to hide it better.

What counts, in practice

Counts as a problem:

Does not, on its own:

The distinguishing question is whether a reviewer could have understood what the app does. Not whether every state was demonstrated.

The React Native shape of this

Two mechanisms make it more likely here than in a native codebase.

Over-the-air updates. Expo Updates, CodePush and similar ship new JavaScript without a new binary. This is supported and legitimate, and its limit is that the update must not change what the app fundamentally does. Shipping a bug fix is the intended use; shipping a feature that would not have been approved is the case the guideline addresses.

Dev menus and debug surfaces. RN ships developer tooling by default and it is easy to leave a debug route, a hidden gesture or a settings screen in a release build. Check the built binary rather than the source, because the difference is exactly what a conditional was supposed to remove and sometimes did not.

# debug surfaces that survived into a release build
grep -rniE '__DEV__|showDevMenu|devMenu|debugScreen|/debug' src app --include=*.ts --include=*.tsx | head

# remote-config keys that change behaviour rather than content
grep -rniE 'remoteConfig|featureFlag|flags\.|unleash|launchdarkly|optimizely' src app | head

# OTA channel configuration
grep -rn 'expo-updates\|codepush\|updates' app.json app.config.js package.json 2>/dev/null | head

Why RN and Expo apps fail differently.

What to put in the review notes

Disclosure is cheap and it is the whole remedy. Something like:

Remote configuration: this build contains a flag "compare_v2" that
changes the layout of the comparison screen only. Currently off for all
users. To enable it for review, sign in with [email protected] and the
flag is forced on for that account.

Over-the-air updates: we use Expo Updates for JavaScript fixes. No
feature not present in this build is enabled by an update.

Three lines, and it converts an undisclosed capability into a disclosed one. Review notes that work.

Where it overlaps with other guidelines

GuidelineThe related concern
2.3.1Functionality concealed from review, including post-review enablement
2.3.10 Other platformsMetadata referencing Android and other platforms
3.1.2 SubscriptionsA purchase flow whose terms differ from what was reviewed
4.3 SpamNear-duplicate submissions, often with flags distinguishing them
2.1 CompletenessWhere an undemonstrable feature is usually reported

A hidden paywall variant can be evaluated under 2.3.1 and 3.1.2 at once, which is why pricing experiments deserve more caution than layout experiments.

The pre-submission decision

Ask one question about your release process: could a user end up with functionality that App Review never saw, without a new submission?

If yes, disclose it. If it is a debug surface, remove it.

shipcheck reports debug surfaces reachable in release configuration and remote-config and OTA mechanisms present in the project, so they can be disclosed deliberately rather than discovered. What it checks. · After a rejection.

About shipcheck

shipcheck is a pre-submission checker from Baker Ventures LLC that runs inside Claude Code. It reads your React Native or Expo project, finds the things that get apps rejected or blocked at upload, cites the exact App Store Review Guideline or Google Play policy clause, and names the file and line to fix.

It exists because React Native and Expo apps fail for reasons that are invisible in the code you wrote: capability arriving through a dependency, purpose strings added by a config plugin, a privacy manifest missing from an SDK you never chose directly, and a generated ios/ directory that discards your edits on the next prebuild. shipcheck checks what the binary and the config actually declare, not what you intended. The rejection references on this site are free, need no account, and link to the primary Apple or Google document for every claim.

Questions and answers

What does App Store Guideline 2.3.1 prohibit?

Hidden or undocumented features. Apps must not contain functionality that is concealed from App Review, and the guideline explicitly extends to features enabled after review through remote configuration or similar mechanisms.

Are feature flags against App Store rules?

Feature flags are not banned. What the guideline is aimed at is using them to conceal functionality from review, or to enable behaviour after approval that would not have been approved. A flag that toggles a documented feature you disclosed is a different thing from one that unlocks an undisclosed one.

Do I have to disclose a feature that is turned off?

If the code to run it is in the binary and it can be switched on remotely, disclose it in the review notes and be ready to demonstrate it. Reviewers cannot evaluate what they cannot see, and the guideline puts the burden of visibility on the developer.

Is a debug menu a Guideline 2.3.1 problem?

A debug or developer menu shipped in a production build is a common finding. It is undocumented functionality, it frequently exposes internal state or test payments, and the fix is to strip it from release builds rather than to hide it better.

Does a staged rollout violate 2.3.1?

Rolling a disclosed feature out gradually is normal. The problem is a rollout that reveals functionality App Review never saw, because the version reviewed and the version users get are then materially different apps.

What about A/B testing paywalls?

Testing variants of a disclosed subscription flow is ordinary. What draws attention is a variant with different pricing, different terms, or a different purchase mechanism from the one reviewed, because that touches the subscription disclosure rules as well.

Cite this pageshipcheck. “Guideline 2.3.1: hidden features, and what counts as one.” Baker Ventures LLC, September 7, 2026. https://shipcheck.bakerventuresstudio.com/rejections/guideline-2-3-1-hidden-features/