Skip to content

[Metrics] Measure end-to-end time from expense creation submit to destination page render #83634

@mountiny

Description

@mountiny

Overview

We need a new end-to-end performance metric that measures the time from when the user taps the Create Expense button (the submit/confirm action on the confirmation step) until the destination page is fully rendered and visible to the user.

This metric should cover all expense types and all possible navigation destinations after expense creation.

Why this matters

After a user confirms an expense, they experience a transition period: the confirmation modal dismisses, navigation occurs, and a destination page renders. This is a critical moment in the user experience — the user is waiting to see the result of their action. Any delay here feels like lag or broken behavior. We currently lack visibility into this end-to-end duration across all creation flows.

Current telemetry coverage

We already have several spans measuring parts of the expense creation flow:

Span What it measures Gap
ManualOpenCreateExpense Opening the creation UI (from startMoneyRequest() to confirmation page layout) None — this is a separate flow
ManualShutterToConfirmation Camera shutter press to confirmation screen layout (scan only) None — this is a separate flow
ManualCreateExpenseSubmit Submit button click to API dispatch completion Ends at API dispatch, not at destination render
ManualCreateExpenseNavigation Post-submit navigation to destination Only covers the Search page destination — all other destinations are unmeasured

The ManualCreateExpenseNavigation span currently only ends when the Search list component lays out (markNavigateAfterExpenseCreateEnd() in src/components/Search/index.tsx). This means we have no measurement for the majority of post-creation navigation scenarios.

What to measure

Start point: The moment the user taps the Create/Submit/Confirm button on the expense confirmation step (in IOURequestStepConfirmation.tsx).

End point: The moment the destination page is fully rendered and visible to the user (e.g., onLayout of the main content list/view on the destination screen).

Duration: The entire end-to-end time including optimistic data application, modal dismissal animation, navigation, and destination page render.

Expense types to cover

All expense creation request types:

  • Manual expense
  • Scan (receipt) expense
  • Distance expense (manual, GPS, odometer variants)
  • Per diem expense
  • Time expense

All IOU types:

  • Submit (request money from someone)
  • Track (track expense for self)
  • Split (split bill among participants)
  • Invoice (send invoice)
  • Pay / Send money

Navigation destinations to cover

After creating an expense, the user can land on many different pages depending on the context (which tab they were on, whether they used global create, whether a report was already open, etc.). All of these destinations need to be measured:

  1. Report chat (Inbox tab) — the most common path; the user is taken to the chat report containing the expense (via dismissModalWithReport)
  2. Search expenses/reports page — when creating from global create while not on the Inbox tab, the user lands on the Search page with the expense type filter (via Navigation.navigate(ROUTES.SEARCH_ROOT))
  3. Money request report in Search RHP — when adding an expense to a report that already has multiple transactions and is open in the wide RHP (via Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT))
  4. RHP pop/dismiss — when a report is already open in the RHP and the user adds an expense to it, the RHP is popped or dismissed to show the updated report (via Navigation.pop() or dismissToPreviousRHP())
  5. Modal dismiss only — when search is the topmost route or there is no reportID, the modal is simply dismissed (via Navigation.dismissModal())
  6. Split bill chat report — after splitting a bill, the user lands on the split chat report
  7. Invoice report — after sending an invoice
  8. Self DM — for per diem expenses submitted to self

Useful attributes / dimensions

To segment and analyze the metric effectively, the span(s) should capture attributes such as:

  • Expense type (manual, scan, distance, per diem, time)
  • IOU type (submit, track, split, invoice, pay)
  • Destination type (report chat, search page, RHP, modal dismiss, etc.)
  • Whether created from global create vs. from within an existing report
  • Whether a receipt is attached
  • Platform (iOS, Android, web — available via Sentry device context)
  • Whether the destination was already mounted (warm) vs. freshly navigated to (cold)

Related code

  • src/pages/iou/request/step/IOURequestStepConfirmation.tsx — confirmation step where submit is triggered
  • src/libs/actions/IOU/index.tshandleNavigateAfterExpenseCreate(), dismissModalAndOpenReportInInboxTab(), and all creation functions (requestMoney, trackExpense, createDistanceRequest, submitPerDiemExpense, etc.)
  • src/libs/actions/IOU/Split.tssplitBill, splitBillAndOpenReport, startSplitBill
  • src/libs/actions/IOU/SendInvoice.tssendInvoice
  • src/libs/actions/IOU/SendMoney.tssendMoneyElsewhere, sendMoneyWithWallet
  • src/libs/telemetry/activeSpans.ts — span management utilities
  • src/libs/telemetry/markSubmitExpenseEnd.ts — existing submit span end marker
  • src/libs/telemetry/markNavigateAfterExpenseCreateEnd.ts — existing navigation span end marker (Search page only)
  • src/components/Search/index.tsx — where the current navigation end marker fires
  • src/CONST/index.ts — telemetry span constants (lines ~1786-1791)

Metadata

Metadata

Labels

Type

No type

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions