One trade-in flow for four systems — and the modal argument that history settled

Trade-in was the most broken part of the T-Mobile for Business e-commerce experience — and one of the most commercially exposed, because promotions depended on it. Four different surfaces needed the same capability. We had two months.
01What shipped, what changed
The end-to-end flow shipped across all four surfaces inside the two-month window, and trade-in went from the most broken part of TFB e-commerce to a working, promotable capability. Order completion per client rose roughly 10% — from ~180 to ~200 monthly orders per client at ~$50 per order — which, across roughly 2,000 business clients a month, compounded into a material revenue impact.
02The system problem
Trade-in wasn't a page; it was an obligation spread across an ecosystem. Account Hub, the Education portal, the e-commerce purchase flow, and Salesforce-based rep tooling all needed customers and reps to move devices through the same underlying trade-in machinery — legacy systems with strict data formats, built long before anyone imagined a business customer trading in hundreds of devices at once.
The core design decision was to treat trade-in as one shared flow consumed by four surfaces, not four sibling implementations. I owned the e-commerce flow — the revenue path — and supported the other three; years later, when Salesforce trade-in became its own project, I supported that too.
03Decision: meet the legacy systems where they were
Business trade-in isn't one phone — it's a fleet. Reps and account owners needed to submit dozens or hundreds of devices, each with identifiers and condition data, into systems that accepted only rigid formats. The elegant answer everyone wanted — smart, guided, per-device entry — collapsed at fleet scale, and the AI-assisted parsing you'd reach for today didn't exist in a shippable form.
I designed a spreadsheet-based bulk upload instead: a template that matched our data systems' expectations exactly, with validation that caught format errors before submission rather than after failure. Less glamorous than a wizard; dramatically more honest about what the back end could digest and what a business customer managing 200 devices actually does — they live in spreadsheets already.
04Decision: the modal argument
The sharpest disagreement was structural. For the interactive trade-in steps, the other codebases wanted modals — dialogs layered over the page. I argued for in-page micro-flows: the trade-in sequence living in the page itself, as addressable steps.
My case, then and now:
- Task shape. Modals suit short, single-decision interruptions. Trade-in is multi-step and data-heavy — device lists, condition grading, value estimates, error states. Long tasks trapped in dialogs fight the container.
- State and complexity. Every edge case inside a modal — validation failures, partial saves, nested confirmations — compounds engineering complexity. In-page steps keep state where the rest of the flow's state already lives.
- Recoverability. In-page steps are URL-addressable: browser back works, sessions can resume, funnels can be measured step by step. A modal is a dead end for all three.
- Room to grow. A page can absorb a 200-row device table. A modal cannot — it can only spawn more modals.
Engineering pushed for modals for local reasons; my design lead sided with them, and modals shipped. The system worked — until businesses needed to trade in more than 25 devices at once, exactly the scale-driven failure the in-page architecture was designed to absorb. When I left, the team was scoping a more robust Salesforce trade-in that integrated directly with client databases — solving, at much greater cost, the bulk problem the original architecture had capped.
05What I'd do differently
Escalate the architecture argument beyond my design lead. I treated the modal decision as a pattern preference and let it be settled as one; it was actually a scalability decision wearing a UI costume. Today I'd write the one-page consequence memo — "here is the entry-count ceiling this creates, here is who hits it, here is the rebuild cost" — and put it in front of the people who'd own that cost.
The full working detail
Why each rule exists, and what it costs.
EvidenceModal vs. in-page micro-flow — the full comparison
| Dimension | Modal | In-page micro-flow |
|---|---|---|
| Task length | Best under ~1 minute, single decision | Handles multi-step, multi-entity tasks |
| Bulk data (25–200+ devices) | Breaks; tables don't fit; spawns nested dialogs | Page absorbs tables, pagination, review states |
| Error recovery | Dismissal risks losing state | Steps addressable; partial progress persists |
| Deep-linking / resume / analytics | None — one opaque state | URL per step; measurable funnel |
| Accessibility | Focus-trapping and screen-reader behavior notoriously fragile | Standard document flow |
| Engineering cost of edge cases | Compounds inside dialog state | Shares the page's existing state model |
Industry guidance (Nielsen Norman Group among others) has long held that modals are for brief, self-contained interruptions and degrade as task complexity grows. The 25-entry ceiling TFB later hit is the textbook failure mode.
EvidenceThe numbers, unpacked
Scale: roughly 2,000 business clients moving through the flow monthly, averaging ~200 orders per client at ~$50 per order. After the rebuild, order completion rose approximately 10% per client — from ~180 to ~200 monthly orders. Order volumes were tracked by the analytics team while it existed; the uplift compounds across the client base into a material revenue impact I deliberately state as a range rather than a computed total, because precision I can't defend is worse than a range I can.
MethodThe bulk-upload design in detail
Template mirrored the legacy systems' required fields exactly — device identifiers, condition codes, quantities — so a successful upload was guaranteed ingestible. Validation ran client-side at upload: row-level errors surfaced with plain-language fixes before submission, replacing the old failure mode of silent downstream rejection. The trade-off accepted: initial format learning cost for the user, in exchange for reliability at fleet scale and zero mystery failures.
ConstraintWorking across four codebases
Each surface had its own stack, release cadence, and design debt. The shared-flow strategy meant negotiating a common pattern language while letting each surface keep its shell. My e-commerce flow served as the reference implementation; I reviewed the others against it. The lesson that stuck: in multi-codebase work, the design system is the contract — where we had shared patterns, surfaces converged cheaply; where we didn't, every divergence became a meeting.
LessonHow I escalate architecture decisions now
The modal decision was settled as a pattern preference when it was actually a scalability decision in a UI costume. My practice since: when a disagreement has a growth ceiling in it, I write the one-page consequence memo — the specific limit this creates, who hits it, when, and the rebuild cost — and put it in front of whoever owns that cost, not just my design lead. Losing an argument is survivable; letting it be miscategorized is what costs the company later.
Finding money in a city you just landed in →
Walk the trade-in flow as a customer ↗ Simulated product site — built as part of this case study.