If the OS Will Not Confirm It, Your Analytics Should Not Either

Complete-sounding event names are tempting when an API returns a resolved promise.
review_prompt_shown.
review_completed.
user_rated_app.
They make dashboards wonderfully decisive. They can also describe events the application never observed.
In-app review is a sharp example. An application may request the system review flow and receive a resolved promise, while the operating system decides not to display a dialog. The application does not get a separate “shown” signal.
So the code can know that a request was accepted without knowing that a person saw anything.
That is not an analytics inconvenience to smooth over. It is the product contract. Name events after observable facts, not desired outcomes.
Resolution proves less than the UI suggests
This code is valid:
await requestReview();
track("review_request_resolved");
The event describes something the app observed: the request path resolved.
This version is not supported by the same evidence:
await requestReview();
track("review_prompt_shown");
The variable did not become less honest because the syntax changed. The system still owns prompt delivery, and the application still lacks confirmation.
The same distinction should shape the surrounding interface. A resolved request is not enough evidence to show “Thanks for rating us,” record an impression, retry because no dialog appeared, or wait for a completion signal that the platform does not expose.
await can make asynchronous code look like a receipt. Here it is closer to a delivery request. (The courier has accepted the parcel; nobody has sent us a photo of the doorstep.)
Build a vocabulary of observed transitions
A reliable event taxonomy begins with state transitions the application can actually witness.
For an in-app review flow, useful events may include:
- the app decided the user was eligible to see a review invitation;
- the user tapped an explicit review action;
- the application found the native review capability available;
- the native request resolved or rejected;
- an attempt to open a store-listing fallback resolved;
- that opening attempt rejected.
Those names are less exciting than user_left_five_stars. They are also reviewable against code.
The distinction matters downstream. If a team treats request resolution as prompt exposure, every conversion rate built on that denominator inherits an invented impression count. The dashboard can be internally consistent and externally meaningless.
Honest analytics may leave a gap:
request resolved → [unobservable system decision] → possible user action
Keep the gap. Do not bridge it with a name.
Explicit intent deserves an observable route
An operating-system-controlled prompt can be appropriate after a natural moment in the product. It is not a good sole implementation for a button that explicitly says “Rate this app,” because the user may tap and see nothing.
A store-listing fallback offers a different contract. The application can attempt a platform-native market URI and fall back to an HTTPS listing where appropriate. That still does not prove a rating was submitted, but it gives explicit intent a route whose opening attempt is observable.
The UI can then stay truthful:
implicit invitation → request system review, accept opacity
explicit “Rate app” action → open an observable store route
The two paths serve different moments. Combining them behind one success message only hides their different evidence.
Missing signals should change the design
Opaque success appears beyond reviews. A background task may be scheduled without running. A notification may be handed to a system without being seen. A share sheet may open without a share completing.
The contracts differ, so none of those examples should inherit review-prompt behavior by analogy. But the design question transfers cleanly: what did our process observe, and what did an external system keep private?
Write that answer before naming events. Then make every user message, metric, retry rule, and fallback depend only on the observable side.
The package whose contract prompted this exercise is public as react-native-in-app-review. If your dashboard contains an event ending in _shown, _delivered, or _completed, trace it back to the exact signal that proves the verb. If there is no signal, rename the event before the graph convinces everyone it happened.
Get the next post.
If you made it to the end, meet the next post in your inbox or RSS reader.