Designing a ‘Try Before You Buy’ System for Apparel Marketplaces

Designing a 'Try Before You Buy' System for Apparel Marketplaces

Designing a Try Before You Buy System for Apparel Marketplaces

A complete system design walkthrough covering trial orders, payment holds, inventory reservation, and fully automated return/exchange reverse logistics — built for interview prep and real production thinking.

01

Introduction & History

Imagine you want to buy a shirt online, but you are not sure if the size will fit or if the colour will look good on you. In a normal online store, you pay first, wait for delivery, and only then find out if you made the right choice. If it does not fit, you start a return process, wait for a refund, and feel annoyed. “Try Before You Buy” — often shortened to TBYB — flips this order around. The marketplace ships you the items first, gives you a trial window (commonly three to fourteen days), and only charges your card for the items you decide to keep. Anything you do not want gets picked up automatically, with no awkward manual return process for the customer to manage.

This idea is not brand new. Mail-order catalog companies in the early twentieth century offered “satisfaction guaranteed or your money back” promises, and door-to-door salespeople let customers try products at home. What changed with the internet era is the scale and the automation. When Amazon launched Prime Wardrobe in 2017, and when Stitch Fix built its entire business model around curated trial boxes, the challenge stopped being “should we let people try clothes at home” and became “how do we build software that can run this for millions of customers without losing money, losing inventory, or losing customer trust.”

That software problem — the plumbing behind the simple customer promise of “try it, keep what you like, send back the rest for free” — is what this tutorial is about. We will design the complete backend system: the services, the databases, the event flows, and most importantly the automated return and exchange logistics that make the whole model financially viable.

It is worth being clear from the start about why this specific topic is such a popular system design interview question. It is not just another e-commerce checkout clone — it forces a candidate to reason about a two-phase financial transaction (hold now, capture later), inventory that exists in a genuinely ambiguous “maybe coming back” state that does not fit neatly into a simple sold/unsold model, and an entire physical reverse-supply-chain that has to be automated end to end. A candidate who can walk through all three of these dimensions clearly, with the right trade-offs at each step, demonstrates a much deeper systems intuition than one who can only describe a straightforward buy-now checkout flow. Keep that lens in mind as we move through the rest of this tutorial: every design decision below is being made with these three dimensions — delayed payment, ambiguous inventory state, and automated reverse logistics — in mind.

Simple analogy — think of a library. You do not pay to “own” a book. You borrow it, keep it for a while, and if you love it you can buy your own copy later; if not, you return it and the library re-shelves it for the next reader. A TBYB apparel system is a marketplace acting like a very fast, very organised library for clothes — except when you “buy” a book here, your card gets charged automatically, and the “re-shelving” is done by a warehouse quality-check team instead of a librarian.
02

Problem & Motivation

Why would a marketplace take on the extra cost and complexity of TBYB instead of just running a normal buy-first store? The motivation comes from a very real, very expensive problem in apparel e-commerce: size and fit uncertainty. Clothing sizing is inconsistent across brands — a “medium” in one brand can fit like a “small” in another. Because of this, apparel has one of the highest return rates of any e-commerce category, often quoted between twenty and forty percent industry-wide. Customers already order multiple sizes and return the ones that do not fit; TBYB simply makes this behaviour official, controlled, and free of guesswork about “will I get my money back.”

From the customer’s side, the motivation is simple: lower risk, less regret, more confidence in a purchase. From the business’s side, the motivation is more strategic:

  • Higher conversion. Customers who are unsure whether to buy are far more likely to add an item to a trial box than to complete an upfront purchase.
  • Fewer return-driven refund disputes. Since money is only captured after the customer confirms they are keeping the item, there is no “charge now, argue about refund later” friction.
  • Data on real preferences. Every keep/return decision is a rich, labeled signal for recommendation engines — far more valuable than a simple “add to cart.”
  • Competitive necessity. Once one major apparel marketplace offers TBYB, competitors must offer something comparable or lose customers to the lower-risk option.

But this comes with real engineering problems that this tutorial will solve one by one:

  • How do you reserve inventory for a trial without letting other customers buy the same item, while also not “over-locking” stock that might come back unsold?
  • How do you charge a customer fairly — capturing money only for what they keep — without giving the business exposure to fraud (customers who “trial” everything and never pay or never return anything)?
  • How do you get the item back from the customer’s home, through a courier, through a warehouse quality check, and back into sellable inventory — automatically, at a scale of potentially millions of packages a month?
  • How do you keep this whole flow reliable when payment gateways, courier partner APIs, and warehouse systems are all separate, unreliable, third-party systems?
i
What an Interviewer May Ask

“Why is TBYB harder to design than a normal e-commerce checkout system?” A strong answer highlights that TBYB adds a second, delayed financial transaction (the final capture), a completely new reverse logistics subsystem, inventory that is in a “maybe coming back” state instead of a simple sold/unsold state, and a fraud surface that does not exist in buy-first commerce.

03

Core Concepts

Before we draw any boxes and arrows, let’s define the vocabulary we will use throughout this tutorial. If any of these terms are new to you, do not worry — we explain each one with a simple description, why it exists, and a small example.

3.1 Trial Order

What it is: A special type of order where the customer receives items before paying the full amount. Why it exists: It is the core unit that tracks “what did the customer request to try, and what state is that request in right now.” Example: Priya selects three t-shirts of different sizes to try; the system creates one trial order containing three trial items.

3.2 Card Authorisation Hold (Pre-Auth)

What it is: A temporary reservation of funds on a customer’s card, without actually taking the money yet. Why it exists: It proves the customer has a valid, funded payment method and protects the business from non-payment, without forcing an upfront charge that would defeat the purpose of “try before you buy.” Simple analogy: It is like a hotel putting a hold on your card for a possible mini-bar charge — you are not billed unless you actually use the mini-bar.

3.3 Capture

What it is: The act of actually taking money from a previously placed hold, for the items the customer decided to keep. Example: Of the three t-shirts Priya tried, she keeps one; the system captures only that one shirt’s price from the original hold and releases the rest.

3.4 Reverse Logistics

What it is: The entire process of moving a product from the customer back to the seller — pickup scheduling, courier transport, warehouse receiving, quality check, and restocking. Why it exists: Forward logistics (store to customer) is well understood in e-commerce, but TBYB depends equally on getting items back efficiently, or the business loses money on inventory sitting in customers’ homes.

3.5 Quality Check (QC)

What it is: A warehouse inspection step that verifies a returned item is undamaged, unworn (beyond trying it on), and has original tags, before it can be resold. Why it exists: Without QC, a marketplace could resell damaged goods to the next customer, hurting trust.

3.6 Trial Window

What it is: The fixed number of days a customer is allowed to keep items before a decision is required. Example: A seven-day trial window means Priya’s card hold and inventory reservation both expire or auto-resolve seven days after delivery if she takes no action.

3.7 Soft-Reserved vs Hard-Reserved Inventory

What it is: Soft-reserved stock is set aside for a trial but can potentially be reclaimed if the trial is abandoned; hard-reserved stock is fully committed once a keep decision is made. Why it exists: This distinction lets the system balance “do not oversell” against “do not lock up stock forever for indecisive shoppers.”

Beginner example — think of ordering three sizes of the same jeans through TBYB. The system creates one trial order with three trial items, places one card hold covering the full value of all three, and marks all three SKUs as soft-reserved. When you keep the medium and return the small and large, the system captures only the medium’s price, releases the rest of the hold, and starts the reverse logistics process for the two returned pairs.
04

Architecture & Components

Now let’s design the actual system. We will lay out the components from the customer’s device all the way down to the database and message bus, and we will be explicit about every infrastructure piece — including the load balancer and API gateway — because interviewers care a lot about seeing these named and placed correctly, not left implied.

Let’s walk through each layer and explain what it does and why it exists at that position in the flow.

4.1 Client Layer

The mobile app and web app are what the customer actually touches — selecting sizes, starting a trial, and later making keep/return decisions. They talk to the backend only through the API Gateway, never directly to any microservice.

4.2 CDN (Content Delivery Network)

What it is: A globally distributed network of servers that cache and serve static content — product images, CSS, JavaScript bundles — from a location physically close to the customer. Why it exists: Product photos are the heaviest part of an apparel page; without a CDN, every image request would travel back to a single origin server, adding latency for every customer far from that server. Production example: Companies like Zalando and ASOS serve product imagery through CDNs such as Akamai or CloudFront so a customer in Mumbai and a customer in Berlin both get images in under a hundred milliseconds.

4.3 Load Balancer

What it is: A component that sits in front of a group of servers and distributes incoming traffic across them, so no single server gets overwhelmed. Why it exists: A TBYB flash sale or new-brand launch can multiply traffic ten times in minutes; the load balancer is what keeps the system standing by spreading that load, and it also removes unhealthy instances from rotation using health checks. We use a Layer 7 (application-aware) load balancer here so it can also do things like route based on URL path or headers, not just spread TCP connections blindly.

4.4 API Gateway

What it is: A single front door for all client requests that handles authentication, rate limiting, request routing, and sometimes response aggregation, before forwarding a request to the correct backend microservice. Why it exists: Without a gateway, every microservice would need to reimplement authentication and rate limiting, and clients would need to know the network address of dozens of services. The gateway centralises these cross-cutting concerns.

Simple analogy — a load balancer is like the host at a restaurant entrance who looks at how full each section of the dining room is and sends you to the least crowded one. The API gateway is like the maître d’ who checks your reservation (authentication), makes sure you are not walking in every five minutes trying to grab extra bread (rate limiting), and then tells the right waiter (microservice) to come take your order.

4.5 Core Microservices

ServiceResponsibility
Auth ServiceIssues and validates OAuth2/JWT tokens, manages sessions and user identity.
TBYB Orchestrator ServiceThe brain of the flow — coordinates inventory reservation, payment hold, and order creation as one logical trial-order transaction (a saga, explained later).
Inventory ServiceOwns real-time stock counts per SKU (size + colour combination), including soft and hard reservations.
Payment and Card Hold ServiceTalks to the external payment gateway to place holds, capture partial amounts, and release remaining holds.
Order ServiceSystem of record for order and trial-item state across their full lifecycle.
Notification ServiceSends push notifications, SMS, and email — trial reminders, pickup confirmations, refund receipts.
Recommendation ServiceUses historical keep/return data to suggest sizes and items likely to be kept, reducing wasted trial shipments.

4.6 Reverse Logistics Domain

ServiceResponsibility
Return and Exchange ServiceOwns the state machine for a return once a customer indicates they do not want an item, including exchanges (swap size instead of refund).
Pickup Scheduling ServiceFinds available pickup slots and assigns a courier partner, optimising routes across many pickups in the same area.
Warehouse QC ServiceManages the physical inspection workflow once an item reaches the warehouse — pass, fail, or dispute.
Courier Partner AdapterAn anti-corruption layer that normalises different courier partners’ APIs into one internal contract, so the rest of the system does not need to know which courier is used.

4.7 Data and Messaging Layer

Postgres holds the durable system-of-record data (orders, accounts, holds). Redis caches hot-read data like current stock counts and session tokens for low-latency access. Object storage (S3-compatible) stores QC inspection photos, which are large binary files that should never sit inside a relational database. Kafka is the event backbone that lets services react to state changes asynchronously without being tightly coupled to each other.

i
What an Interviewer May Ask

“Where exactly would you place the load balancer and API gateway, and are they the same thing?” Be ready to explain that a load balancer typically operates below or alongside the gateway, distributing traffic across gateway instances (since the gateway itself is horizontally scaled), and that the gateway is a Layer 7 application concern (auth, routing, rate limiting) while the load balancer is more general traffic distribution across any pool of servers, gateway or otherwise.

05

Internal Working

Architecture diagrams show the “what.” This section explains the “how” — what actually happens inside the TBYB Orchestrator Service when a customer starts a trial.

5.1 The Saga Pattern for Trial Creation

Starting a trial order touches three different services that each own their own database: Inventory (reserve stock), Payment (place hold), and Order (create the record). We cannot wrap these in a single database transaction because they are separate services with separate databases — this is a classic distributed transaction problem. The solution is the Saga pattern: a sequence of local transactions, each with a defined compensating action if a later step fails.

trial-saga.pseudo — the three-step saga with compensations for each failure point.
Step 1: Reserve inventory (soft-reserve N SKUs)
   -> if fails: abort, tell customer item unavailable

Step 2: Place payment hold for total value
   -> if fails: compensate by releasing inventory reservation

Step 3: Create trial order record
   -> if fails: compensate by releasing hold AND releasing inventory

We use an orchestration-based saga (as opposed to a choreography-based saga using only events) because the sequence has strict ordering requirements and we want one place — the TBYB Orchestrator — that clearly owns the decision of what to do next and what to roll back. This also makes the flow far easier to debug and reason about during an interview or a production incident, compared to tracing a chain of events across five services.

5.2 Java Example: Orchestrator Saga Step

TrialOrderSagaOrchestrator.java — each catch branch performs the compensating rollback of any earlier successful step.
public class TrialOrderSagaOrchestrator {

    public TrialOrderResult startTrial(TrialRequest request) {
        InventoryReservation reservation = null;
        PaymentHold hold = null;
        try {
            reservation = inventoryClient.softReserve(request.getSkuIds());
            hold = paymentClient.placeHold(request.getCustomerId(), reservation.getTotalValue());
            TrialOrder order = orderService.createTrialOrder(request, reservation, hold);
            eventPublisher.publish(new TrialOrderCreatedEvent(order.getId()));
            return TrialOrderResult.success(order);
        } catch (InventoryUnavailableException e) {
            return TrialOrderResult.failure("Item no longer in stock");
        } catch (PaymentHoldFailedException e) {
            inventoryClient.releaseReservation(reservation.getId());
            return TrialOrderResult.failure("Payment method could not be authorized");
        } catch (OrderCreationException e) {
            paymentClient.releaseHold(hold.getId());
            inventoryClient.releaseReservation(reservation.getId());
            return TrialOrderResult.failure("Could not create order, please retry");
        }
    }
}

5.3 Idempotency

What it is: A property of an operation where performing it multiple times has the same effect as performing it once. Why it exists: Mobile networks are unreliable — a customer might tap “Start Trial” and, due to a timeout, tap it again. Without idempotency, this could create two trial orders and two card holds. How we implement it: The client generates a unique idempotency key per trial request; the orchestrator stores this key with the resulting order ID, and any retry with the same key simply returns the original result instead of creating a duplicate.

💡
Production Example

Payment gateways like Stripe and Razorpay require an idempotency key on every charge and hold request specifically because of this double-tap and network-retry problem. A well-designed TBYB orchestrator generates its own internal idempotency key and also forwards it to the payment gateway call, so retries are safe at every layer of the call chain.

06

Data Flow & Lifecycle

Let’s trace a real customer journey end to end, from picking items to trial to the final keep/return decision. This is one of the most common things interviewers ask you to draw on a whiteboard, so understanding the sequence in detail matters.

Notice the branching point in the middle: this is where the system’s behaviour genuinely diverges depending on the customer’s decision, and it is exactly the part most candidates gloss over in interviews. Handling the “keep” branch is simple — capture the money. The “return” branch is where a whole additional subsystem, reverse logistics, takes over.

6.1 Order Lifecycle as a State Machine

Because a trial order can be in so many different situations — waiting for pickup, at the warehouse, disputed, refunded — it is best modelled explicitly as a finite state machine rather than a loose set of boolean flags. This makes illegal transitions (like jumping straight from “Shipped” to “Refunded” without ever going through a return) impossible by design.

Common Mistake

A frequent design mistake is treating “AutoReturned” (trial window silently expired) the same as an explicit customer-initiated return. In practice these should be tracked separately, because auto-returns are a strong signal of a bad customer experience — perhaps the reminder notification failed, or the trial window is too short — and should feed into a different analytics and alerting pipeline than voluntary returns.

07

Data Model

Here is the core relational data model that backs the order and return lifecycle. We deliberately separate a TRIAL_ITEM from a TRIAL_ORDER because each item in a multi-item trial can be in a completely different state — one kept, one returned, one disputed — even though they arrived in the same box.

A few design decisions worth calling out:

  • TRIAL_ORDER and PAYMENT_HOLD have a one-to-one relationship because we place a single hold covering the entire order rather than one hold per item — this reduces the number of calls to the payment gateway and avoids the customer seeing many small “pending” charges on their statement.
  • RETURN_REQUEST is optional (zero-or-one) from TRIAL_ITEM because most items in a healthy TBYB system are kept, not returned.
  • Splitting QC_RECORD from PICKUP_TASK lets us cleanly represent the gap in time and physical location between “courier picked it up” and “warehouse inspected it,” which can be days apart.
08

Reverse Logistics Engine — The Heart of TBYB

This is the part of the system that makes or breaks the TBYB business model. If forward shipping is “get the product to the customer,” reverse logistics is “get the product back, verify it, and make it sellable again — fast, cheap, and automatically.” Let’s break down each stage.

8.1 Return Initiation

A return can be triggered in two ways: the customer explicitly marks an item as “return” in the app, or the trial window expires without any decision (auto-return). Both paths converge into the same RETURN_REQUEST record, but as noted earlier, they are tagged with a different reason so analytics can tell them apart.

8.2 Automated Pickup Scheduling

What it is: A service that looks at the customer’s address, available courier partner capacity, and delivery route density, then offers the earliest reasonable pickup slot — without any human coordinator involved. Why it exists: Manually coordinating pickups does not scale past a few hundred returns a day; automated slot allocation and route batching is what allows a marketplace to handle tens of thousands of daily pickups.

A simplified allocation algorithm looks like this:

PickupSlotAllocator.java — earliest-first slot search across multiple courier partners, gated by a route-density threshold.
public class PickupSlotAllocator {

    public PickupSlot findBestSlot(Address address, LocalDate earliestDate) {
        List<CourierPartner> partners = courierRegistry.getPartnersServing(address.getPincode());
        return partners.stream()
            .flatMap(partner -> partner.getAvailableSlots(address, earliestDate).stream())
            .filter(slot -> slot.getRouteDensityScore() > MIN_DENSITY_THRESHOLD)
            .min(Comparator.comparing(PickupSlot::getEarliestTime)
                           .thenComparing(PickupSlot::getCostPerPickup))
            .orElseThrow(() -> new NoSlotAvailableException(address));
    }
}

The routeDensityScore matters because a courier partner would rather batch five pickups in the same neighbourhood on one route than send a driver across town for a single package — this directly affects reverse logistics cost, which is often the single largest hidden expense of a TBYB program.

8.3 Courier Partner Adapter and the Anti-Corruption Layer

Marketplaces typically integrate with multiple courier partners for coverage and cost optimisation. Each courier has a different API shape, different status codes, and different webhook formats. The Courier Partner Adapter service exists specifically to absorb this variability, translating every partner’s events into one internal PickupStatusChanged event format that the rest of the system consumes. This is a direct application of the Adapter pattern and the Anti-Corruption Layer concept from Domain-Driven Design — external chaos stays external.

8.4 Warehouse Quality Check (QC)

When the item physically arrives at the returns warehouse, it goes through an inspection workflow before it can be marked sellable again:

  1. Intake scan — barcode scan confirms which trial item this package corresponds to.
  2. Visual inspection — a warehouse worker (or increasingly, a computer-vision-assisted station) checks for stains, tears, missing tags, or signs of extended wear.
  3. Photo capture — photos are uploaded to object storage and linked to the QC_RECORD, creating an audit trail in case of a later dispute.
  4. Result recording — pass, fail, or “needs manual review” is recorded, which drives the next step of the state machine.

8.5 Refund and Restock Decision

If QC passes, two things happen in parallel, published as one event so both can proceed independently: the remaining payment hold is released (or refund issued if capture already occurred), and the item’s stock count is incremented back in the Inventory Service so it becomes available for a new customer. If QC fails, the item goes to manual review instead of an automatic refund, and depending on policy, the marketplace might issue a partial refund, deny the return, or dispose of the item.

💡
Production Example

Stitch Fix built an entire internal logistics and warehouse operations team specifically to support its trial-box model, because the QC and restocking speed directly determines how much capital is tied up in “in transit” inventory at any moment. The faster an item can go from “customer returned it” back to “available to sell,” the less inventory a marketplace needs to hold overall for the same sales volume.

8.6 Exchanges as a First-Class Flow

An exchange (say, swap a medium for a large) is not simply “return one item, buy another.” Treating it that way creates two separate financial transactions and two separate reverse logistics chains for what the customer experiences as a single simple action. Instead, the Return and Exchange Service creates a linked pair: the returning item follows the normal reverse logistics chain, while a new trial item for the requested replacement size is created immediately and shipped without waiting for the original item’s QC to finish — because making a customer wait for their replacement until the old one is inspected would be a poor experience and is unnecessary risk-wise if the replacement SKU has available stock.

i
What an Interviewer May Ask

“How would you decide whether to ship an exchange replacement immediately or wait for the original return’s QC to pass?” A strong answer discusses the trade-off: shipping immediately improves customer experience and speed but exposes the business to inventory risk if the original item never comes back or fails QC; a mitigation is to place a second, separate authorisation hold for the replacement item’s value that is only released once the original return completes successfully.

09

Advantages, Disadvantages & Trade-offs

Advantages

  • Higher conversion rate — hesitant shoppers become buyers.
  • Reduced return-fraud friction since money is only captured for kept items.
  • Rich preference data for personalisation and sizing recommendations.
  • Builds strong customer trust and loyalty in a category plagued by fit uncertainty.

Disadvantages

  • Higher operating cost — reverse logistics, QC labour, and courier pickups are expensive.
  • Working capital gets tied up in “in transit” inventory that is neither sellable nor paid for.
  • New fraud surface — “wardrobing” (using items once, then returning them) and payment holds that are never captured.
  • Significantly more complex system than buy-first commerce, meaning more places for bugs and outages.

9.1 Key Trade-off: Trial Window Length

A longer trial window (say, fourteen days) increases customer comfort and keep-rate, but it also ties up inventory reservations longer and increases the payment gateway’s authorisation hold duration — most card networks auto-expire holds after seven to thirty days depending on the card issuer, so a long trial window risks the hold silently expiring before the customer decides, which is a real production bug many teams hit. A shorter window (three days) protects cash flow and inventory turnover but pressures customers into a decision before they may be ready, hurting satisfaction.

9.2 Key Trade-off: Soft vs Hard Inventory Reservation

Soft-reserving stock (still technically visible to other systems as “low stock” rather than fully locked) risks overselling if many trials are started simultaneously for the last unit of a popular size. Hard-reserving avoids overselling but can starve inventory availability during high-traffic periods if a large fraction of stock sits “reserved” in abandoned trials nobody ever completes.

9.3 Key Trade-off: Fraud Prevention Strictness vs Customer Experience

Every fraud-prevention mechanism described in the security section of this tutorial — rate limits on concurrent trials, address verification before pickup, QC-driven dispute holds — makes the system safer for the business but adds friction for the overwhelming majority of honest customers who are simply trying to find clothes that fit. Set fraud thresholds too aggressively and you will see legitimate customers get flagged for manual review, frustrated, and churned; set them too loosely and wardrobing losses erode the program’s unit economics. Most mature TBYB platforms resolve this not with a single global threshold but with a tiered trust model: new accounts start with tighter limits (shorter trial windows, lower maximum trial value) and graduate to looser, more generous terms as they build a track record of honest keep/return behaviour — the system trades a slightly worse experience for new customers in exchange for a much better one for its most loyal, trustworthy shoppers.

10

Performance & Scalability

TBYB traffic is naturally spiky: flash sales, new-season launches, and end-of-trial-window reminder pushes (which cause a burst of “keep or return” decisions all at once) create load patterns very different from steady, evenly spread e-commerce browsing.

10.1 Horizontal Scaling of Stateless Services

All core microservices (Auth, TBYB Orchestrator, Inventory, Order, Notification) should be stateless — any instance can handle any request — so they scale horizontally behind the load balancer simply by adding more instances. Session and reservation state lives in Redis and Postgres, not in service memory.

10.2 Caching Strategy

DataCache StrategyTTL
Product catalog and images metadataCache-aside in Redis, CDN for imagesHours
Current stock countsWrite-through cache, updated on every reservation changeSeconds
Customer session / auth tokenRedis with TTL matching token expiryMinutes
Pickup slot availabilityShort-TTL cache to reduce courier partner API callsSeconds to low minutes

10.3 Handling the Inventory Hot-Key Problem

During a flash sale, thousands of customers might try to reserve the same popular SKU simultaneously — this is a classic hot-key problem where one Redis key gets hammered with concurrent decrement operations. The fix is to use Redis’s atomic DECR operation (never read-then-write from application code) combined with sharding very high-demand SKUs across multiple Redis keys that get reconciled periodically, or using a dedicated inventory reservation service backed by a database with row-level locking and short transactions.

10.4 Asynchronous Processing via Kafka

Reverse logistics stages — pickup scheduling, QC processing, restocking — do not need to happen synchronously with the customer’s request. They are modelled as events on Kafka topics (return.requested, pickup.completed, qc.completed), letting each downstream service process at its own pace and letting the system absorb traffic spikes by simply growing the queue depth temporarily instead of failing requests.

i
What an Interviewer May Ask

“How would you prevent overselling the last unit of a size during a flash sale?” Discuss atomic decrement operations, database row locks with short transaction scope, and the trade-off between pessimistic locking (safer, slower under contention) versus optimistic locking with retry (faster, but needs idempotent retry logic).

11

High Availability & Reliability

11.1 Multi-AZ and Multi-Region Deployment

Core services and databases are deployed across multiple availability zones within a cloud region, so the failure of one data centre does not take down the whole system. For a global marketplace, active-active or active-passive multi-region deployment reduces latency for distant customers and provides disaster recovery if an entire region goes down.

11.2 Circuit Breakers for External Dependencies

The Payment Service and Courier Partner Adapter both call external third-party APIs that can be slow or fail. Wrapping these calls in a circuit breaker (using a library pattern like Resilience4j) prevents a slow external dependency from exhausting all available threads or connections in the calling service, which would otherwise cascade into an outage of the whole orchestrator.

PaymentServiceClient.java — Resilience4j @CircuitBreaker with a queue-based fallback so the caller never blocks on a failing gateway.
@CircuitBreaker(name = "paymentGateway", fallbackMethod = "holdFallback")
public PaymentHold placeHold(String customerId, BigDecimal amount) {
    return paymentGatewayClient.authorize(customerId, amount);
}

public PaymentHold holdFallback(String customerId, BigDecimal amount, Throwable t) {
    // queue for retry, return a pending status to the orchestrator
    retryQueue.enqueue(new HoldRequest(customerId, amount));
    return PaymentHold.pending();
}

11.3 Saga Compensation and Reconciliation Jobs

Even with careful compensating transactions, distributed systems can end up in inconsistent states due to crashes mid-saga. A scheduled reconciliation job periodically scans for orders “stuck” in an intermediate state (like a hold placed but no order record created) longer than an expected threshold, and either completes or rolls back the transaction — this is a critical safety net that most TBYB systems need in production even with a well-designed saga.

11.4 Trial Window Expiry as a Distributed Scheduling Problem

With millions of active trials, “check if trial window has expired” cannot be a single cron job scanning the whole orders table every minute. Instead, use a time-bucketed scheduler (writing expiry timestamps into buckets, like a wheel timer, or using a dedicated delayed-message feature in Kafka or SQS) so expiry checks scale independently of total order count.

12

Security

12.1 Payment Data — Never Touch Raw Card Numbers

The Payment Service never stores or even sees the raw card number; it uses a PCI-DSS compliant payment gateway (like Stripe or Razorpay) via tokenisation, storing only a gateway-issued token. This dramatically reduces the marketplace’s compliance burden and blast radius if a service is ever compromised.

12.2 Authentication and Authorisation

The Auth Service issues short-lived JWT access tokens plus longer-lived refresh tokens, validated at the API Gateway on every request. Internal service-to-service calls use mutual TLS (mTLS) so that even inside the private network, a compromised service cannot silently impersonate another.

12.3 Fraud Prevention Specific to TBYB

TBYB introduces fraud patterns that do not exist in buy-first commerce:

  • Wardrobing — wearing an item once (for an event, a photo) and then returning it as “unused.” QC photo evidence and wear-pattern detection heuristics help catch repeat offenders.
  • Hold abandonment — starting many trials and never responding, hoping items are “free” until the merchant chases payment. Rate-limiting the number of concurrent open trials per customer, and tightening trial windows for repeat offenders, mitigates this.
  • Return address mismatch fraud — items shipped to one address but a pickup requested from a different, unverified address. Address verification against the original shipping address before scheduling pickup is a simple, effective guard.
i
What an Interviewer May Ask

“How would you detect a customer abusing the trial system?” A good answer builds a simple fraud score from signals like return rate over time, average trial duration before deciding, QC fail rate on their returns, and account age, then applies increasingly strict policies (shorter windows, smaller hold flexibility, manual review) as the score worsens, rather than an all-or-nothing ban.

12.4 Encryption in Transit and at Rest

All external traffic terminates TLS at the load balancer, and internal service-to-service traffic uses mTLS as noted earlier, so data is encrypted end-to-end across the network. At rest, the Postgres databases holding customer addresses and order history use disk-level encryption, and specifically sensitive fields — full address, phone number — are additionally encrypted at the application layer with a key managed by a dedicated secrets and key-management service, so that even a database backup file leaked outside the organisation cannot be read without separately compromising the key management system. QC photos in object storage are similarly encrypted at rest by default, since they can incidentally capture a customer’s home background in the return packaging photos.

12.5 Least-Privilege Access and Secrets Management

Each microservice authenticates to shared infrastructure — the database, Kafka, the payment gateway — using short-lived credentials issued by a secrets manager (like HashiCorp Vault or a cloud-native equivalent) rather than long-lived static passwords baked into configuration files. The Inventory Service, for example, has no reason to ever hold credentials that can read the Payment Service’s database, and enforcing this at the infrastructure level (not just by convention) limits the blast radius if any single service is compromised.

13

Monitoring, Logging & Metrics

13.1 The Three Pillars of Observability

Metrics tell you that something is wrong (dashboards, alerts), logs tell you what happened in detail, and distributed traces tell you where in the chain of service calls the problem occurred. A TBYB system needs all three because a single trial order can touch six or seven services before it is fully closed out.

13.2 Business Metrics That Matter

metric

Keep Rate

Kept items divided by total trial items — the core health indicator of the whole program’s unit economics.

metric

Average Time-to-Decision

Signals whether trial windows are well calibrated for customer behaviour.

metric

Pickup-to-QC-Complete Latency

Directly drives how much capital is tied up in transit and idle at any moment.

metric

QC Fail Rate

Signals product quality issues or fraud spikes that need attention.

metric

Hold Expiry Before Decision

An operational bug indicator — should be near zero in a healthy program.

13.3 Distributed Tracing and Correlation IDs

Every trial order is assigned a correlation ID at creation, propagated through every subsequent service call and Kafka event. When an on-call engineer investigates “why is this customer’s refund stuck,” they search by correlation ID across all logs and traces instead of manually piecing together timestamps across seven different service dashboards.

13.4 Alerting Philosophy

Alerts should be tied to symptoms that actually require a human to act, not to every anomaly a dashboard can detect. For a TBYB system, examples of well-tuned, actionable alerts include: payment hold success rate dropping below a defined threshold over a five-minute window, saga compensation rate (the percentage of trial creations that trigger a rollback) exceeding its normal baseline, and pickup scheduling failures spiking for a specific courier partner, which usually signals that partner’s API is degraded. Alerts on things like “CPU usage is at 70%” without a clear customer-facing consequence tend to create alert fatigue and should live on dashboards, not page an on-call engineer at 3 a.m.

13.5 Structured Logging

Every service emits structured (JSON) logs rather than free-text log lines, with consistent fields — correlation ID, service name, order ID, event type — across all services. This lets the centralised logging platform (such as an ELK stack or a managed equivalent) support precise queries like “show me every log line for order X across every service,” which would be nearly impossible to build reliably on top of inconsistent free-text logs.

14

Deployment & Cloud

The recommended deployment model is containerised microservices orchestrated by Kubernetes, with each service independently deployable. This matters a lot for TBYB specifically because the reverse logistics domain (Return Service, Pickup Scheduling, QC) evolves faster and has very different scaling needs from the core commerce services — independent deployability lets teams ship changes to returns handling without redeploying the entire platform.

14.1 Blue-Green Deployment for the Payment Service

Because the Payment Service handles financial transactions, it uses blue-green deployment: a new version is deployed alongside the old one, traffic is switched over only after health checks pass, and the old version stays running briefly for instant rollback if something goes wrong. This is safer than a rolling update for a service where even a few seconds of broken behaviour means failed or duplicated financial holds.

14.2 Infrastructure as Code

All infrastructure — Kubernetes clusters, load balancer configuration, Kafka topics, database instances — is defined in code (Terraform or similar) and version controlled, so environments (staging, production) stay consistent and infrastructure changes go through the same code review process as application code.

14.3 CI/CD Pipeline

Every service has its own pipeline: run unit and contract tests, build a container image, run integration tests against a staging environment, then deploy via blue-green or canary release depending on the service’s risk profile. Contract tests are especially important here since so many services communicate through well-defined events and APIs — a broken contract between the Return Service and the Courier Partner Adapter should fail in CI, not in production.

15

Databases, Caching & Load Balancing — Deeper Look

15.1 Why Postgres for the System of Record

Orders, holds, and returns need strong consistency and ACID transaction guarantees — you cannot afford a race condition that captures payment twice or double-releases a hold. A relational database like Postgres, with row-level locking and proper transaction isolation, is the right tool here, even though other parts of the system (like product catalog browsing) might use different, more eventually-consistent stores.

15.2 Database Per Service

Each microservice owns its own database schema — the Order Service does not directly query the Inventory Service’s tables. This keeps services independently deployable and prevents a schema change in one team’s service from silently breaking another team’s queries. Cross-service data needs go through APIs or through consuming published events, not shared database access.

15.3 Read Replicas for Reporting

Business intelligence queries (keep-rate dashboards, QC fail-rate trends) should never run against the primary transactional database, since heavy analytical queries can degrade write performance for live customer transactions. Read replicas, or a separate data warehouse fed by change-data-capture from Kafka, serve this reporting load instead.

15.4 Load Balancing Algorithms

AlgorithmBest for
Round robinSimple, evenly-sized stateless service instances
Least connectionsRequests with variable processing time, like the TBYB Orchestrator’s saga calls
Weighted round robinMixed instance sizes, or canary releases getting a small traffic percentage
Consistent hashingRouting by customer ID to improve cache locality on session data

15.5 Capacity Estimation — A Back-of-Envelope Walkthrough

System design interviews almost always want you to show that your architecture is grounded in real numbers, not just boxes and arrows. Let’s estimate the scale a mid-sized apparel marketplace running TBYB might need to handle, and see how that shapes our earlier decisions.

15.5.1 Assumptions

AssumptionValue
Daily active shoppers5 million
Percentage who start a trial order per day2%, so 100,000 trial orders/day
Average items per trial order3
Average keep rate40% of items kept, 60% returned
Average trial window7 days

15.5.2 Derived Numbers

100,000 trial orders/day means roughly 300,000 trial items created per day. At 60% return rate, that is about 180,000 return requests per day, each of which needs a pickup scheduled, a courier trip, and a warehouse QC pass. Spread across a 16-hour operational day, that is roughly 187 return requests entering the pipeline every minute at peak — a number that immediately tells us the Pickup Scheduling Service and Courier Partner Adapter need to be horizontally scalable and cannot rely on any single-threaded or synchronous bottleneck.

Because the trial window is 7 days, at any given moment the system is holding roughly 100,000 × 7 = 700,000 trial orders in an “active, undecided” state (assuming a roughly steady daily rate). That is around 2.1 million trial items sitting in soft or hard inventory reservation and payment-hold states simultaneously — this number directly justifies why we chose Redis for hot inventory reservation counts rather than hitting Postgres for every stock check, since 2.1 million actively-reserved line items being queried repeatedly by a browsing storefront would create a very heavy read load on a relational primary.

15.5.3 Storage Estimation

If each trial order record, its items, hold, and eventual return/QC records average roughly 2 KB of structured data combined, then 100,000 orders/day × 2 KB ≈ 200 MB/day of new transactional data, or about 73 GB/year — very manageable for Postgres with standard read replicas. QC photos are the real storage driver: assuming 3 photos per returned item at roughly 500 KB each, 180,000 returns/day × 3 × 500 KB ≈ 270 GB/day of image data, which is why QC photos must live in object storage (S3-compatible) with lifecycle policies to move older photos to cheaper cold storage tiers after a dispute window closes (say, 90 days), rather than being kept at full cost forever.

i
What an Interviewer May Ask

“Walk me through how you’d estimate the load on the Pickup Scheduling Service.” Show the chain of reasoning: daily active users, trial-start rate, items per order, return rate, and dividing by operational hours to get a peak-per-minute figure — interviewers care more about the reasoning chain than the exact final number.

15.6 Concurrency, Locking & Algorithms

15.6.1 Optimistic Locking for Inventory Updates

Rather than locking a stock row for the full duration of a reservation check, the Inventory Service uses optimistic concurrency control: each SKU row carries a version column, and an update only succeeds if the version matches what was read moments earlier. If two concurrent requests race for the last unit, only one update succeeds; the loser retries and discovers the item is now out of stock. This keeps lock duration extremely short, which matters enormously under flash-sale-level concurrency.

reserve-sku.sql — guarded update: zero affected rows means either the version changed or stock ran out, so the caller retries or gives up.
UPDATE product_sku
SET stock_qty = stock_qty - 1, version = version + 1
WHERE sku_id = ? AND version = ? AND stock_qty > 0;
-- if affected rows = 0, retry read-modify-write or return out-of-stock

15.6.2 Priority Queue for Pickup Slot Allocation

When batching pickups by route density, the Pickup Scheduling Service internally uses a priority queue (min-heap) keyed by a combination of pickup deadline and route density score, so that time-sensitive pickups (approaching the courier’s daily cutoff) are always considered before lower-urgency ones, even as new pickup requests are continuously added throughout the day. This is a direct, practical use of a classic data structure inside a business-critical service, and it is exactly the kind of detail that separates a surface-level design from one that shows real engineering judgment.

15.6.3 Consistent Hashing for Session and Cache Locality

The API Gateway can optionally route requests using consistent hashing on customer ID, so that a given customer’s requests tend to land on the same backend instance repeatedly during a session. This improves cache hit rates for that customer’s session and cart data without requiring a fully centralised session store lookup on every single request, reducing Redis load at very high request volumes.

15.7 Disaster Recovery, Backup & Cost Optimisation

15.7.1 Backup Strategy

Postgres primaries take continuous write-ahead log (WAL) shipping to a standby in a different availability zone, plus daily full snapshots retained for 30 days and weekly snapshots retained for a year to satisfy financial audit requirements — trial orders and refunds are financial records, and many jurisdictions require multi-year retention for transaction history even after a customer account closes.

15.7.2 Recovery Point and Recovery Time Objectives

For the Order and Payment services specifically, the business typically sets an aggressive Recovery Point Objective (RPO) of under one minute of data loss and a Recovery Time Objective (RTO) of under five minutes, given that every minute of downtime here directly blocks both new trial starts and in-flight refund processing. Less critical services, like Recommendation, can tolerate a much looser RPO/RTO since a stale recommendation model for a few hours has no financial consequence.

15.7.3 Cost Optimisation Levers

  • Route batching in reverse logistics is the single biggest cost lever — a courier picking up five packages on one street in one trip is dramatically cheaper per package than five separate single-item trips.
  • Right-sizing QC photo storage tiers — moving photos older than the standard dispute window to cold storage (or deleting them after the legal retention period) cuts object storage cost substantially at scale.
  • Auto-scaling based on time-of-day traffic patterns rather than static fixed capacity — TBYB decision submissions cluster around evenings and trial-expiry reminder windows, so services should scale down during predictable low-traffic hours.
  • Spot/preemptible compute for non-critical batch jobs — the reconciliation job and analytics pipelines can run on cheaper, interruptible compute since they are not customer-facing in real time.
Common Mistake

Teams sometimes optimise the forward shipping cost aggressively while treating reverse logistics cost as a fixed, unavoidable expense. In a mature TBYB program, reverse logistics cost per order is usually the largest controllable line item, and small improvements in route batching or QC turnaround time compound into large savings at scale — it deserves the same engineering attention as the “happy path” checkout flow.

16

APIs & Microservices Design

16.1 Key External API Endpoints

tbyb-api.txt — the five customer-facing endpoints that drive the entire trial lifecycle.
POST /v1/trials                 -> start a new trial order
GET  /v1/trials/{id}            -> fetch trial order status
POST /v1/trials/{id}/decisions  -> submit keep/return decisions for items
POST /v1/returns/{id}/pickup    -> schedule or reschedule a pickup
GET  /v1/returns/{id}/status    -> track a return through its lifecycle

16.2 Synchronous vs Asynchronous Communication

Customer-facing calls (start trial, submit decision) are synchronous REST calls through the API Gateway because the customer is waiting for an immediate response. Internal service-to-service reverse logistics steps (pickup completed, QC finished, restock triggered) are asynchronous events over Kafka, because these steps do not have a human waiting in real time and benefit from the resilience of a durable queue.

16.3 API Versioning

All external APIs are versioned in the URL path (/v1/) so that breaking changes can be introduced in a new version while existing mobile app clients (which cannot be force-updated instantly) keep working against the old version until they roll off.

17

Design Patterns & Anti-Patterns

17.1 Patterns Used

pattern

Saga (Orchestration)

Coordinates the multi-service trial creation transaction with explicit compensations for each step.

pattern

Adapter / Anti-Corruption Layer

Normalises courier partner APIs so external chaos never leaks into core services.

pattern

Circuit Breaker

Protects the orchestrator against slow external payment and courier APIs.

pattern

Event Sourcing (partial)

The order state machine’s transition history doubles as an audit log.

pattern

Database per Service

Enforces service independence and deployability by removing shared schema coupling.

17.2 Anti-Patterns to Avoid

avoid

Distributed Monolith

Services that are technically separate but always deployed together and share a database, losing the benefits of microservices while keeping the complexity.

avoid

Chatty Services

The TBYB Orchestrator making many small synchronous calls per request instead of batching, which multiplies latency.

avoid

Shared Mutable Inventory Table

Multiple services writing directly to the same stock count column without going through the Inventory Service’s API, causing race conditions.

avoid

Silent Auto-Return Without Notification

Letting trial windows expire without a clear, timely reminder, which erodes customer trust.

18

Best Practices & Common Mistakes

  • Always use idempotency keys on trial creation and payment capture endpoints — mobile retries are guaranteed to happen.
  • Never let the payment hold silently expire — track card network hold expiry windows and proactively re-authorise before they lapse if a trial is still active.
  • Separate soft and hard inventory reservation so abandoned trials do not permanently starve stock availability.
  • Treat the courier partner integration as unreliable by default — build retries, circuit breakers, and a manual-intervention queue for pickups that repeatedly fail to schedule.
  • Design the state machine first, the database schema second — the states and transitions should drive what columns and statuses you need, not the other way around.
  • Common mistake: forgetting to reconcile “stuck” sagas — without a reconciliation job, a small percentage of orders will always end up in limbo after a service crash mid-transaction, and this quietly accumulates into real financial and inventory discrepancies over months.
  • Common mistake: under-investing in the QC and photo-evidence pipeline early on, then having no data to resolve refund disputes fairly once volume grows.
19

Real-World Industry Examples

case A

Amazon Prime Wardrobe

Lets Prime members try clothing before paying, with automated box-based returns and a keep-three-get-a-discount incentive structure that nudges keep rate upward.

case B

Stitch Fix

Combines algorithmic + human styling with a trial-box model; heavily invested in warehouse logistics automation to shorten the return-to-restock cycle.

case C

ASOS & Zalando

Large European apparel marketplaces with famously generous free-return policies, relying on dense courier partnerships and regional warehouses to keep reverse logistics costs manageable at scale.

case D

Warby Parker

Runs a “home try-on” program for eyewear — a smaller-scale, non-payment-hold variant of the same core reverse-logistics problem, since frames are shipped free and returned in one prepaid box.

case E

Fashion Marketplaces in India

Cash-on-delivery-heavy markets add an extra dimension: since many customers never had a card hold in the first place, “try before you buy” there often means a doorstep trial with the courier waiting while the customer decides.

19.1 A Closer Look: Why Amazon Bundles Incentives into the Keep Decision

Amazon Prime Wardrobe’s “keep three or more items and get a discount” mechanic is a clever systems-level nudge, not just a marketing gimmick. From a reverse-logistics-cost point of view, a customer who keeps three of five trial items generates far less pickup, courier, and QC load than a customer who keeps one of five — fewer physical items need to travel backward through the system. The discount is effectively a small price paid to shift customer behaviour toward outcomes that are cheaper to fulfil on the backend, and a well-designed TBYB platform should expose exactly this kind of lever (dynamic incentives tied to predicted reverse-logistics cost) to the business teams, not hardcode a single fixed discount rule.

19.2 A Closer Look: Stitch Fix’s Data Flywheel

Stitch Fix is a particularly instructive case because their styling algorithm and their reverse logistics system are deeply intertwined: every keep or return decision, tagged with a reason code (“too tight,” “not my style,” “loved it”), feeds back into the recommendation model that selects what goes into the next box. This is a direct illustration of why the Recommendation Service in our architecture consumes events from the Return and Exchange Service rather than working off a separate, disconnected dataset — the value of TBYB compounds over time only if the “why” behind every decision is captured in a structured way, not just the binary keep/return outcome.

19.3 Testing Strategy

A financial, multi-service saga like trial-order creation needs a testing pyramid that goes well beyond simple unit tests on individual methods.

19.3.1 Unit and Contract Tests

Each service is unit tested in isolation with mocked dependencies. Contract tests (using a framework like Pact) verify that the TBYB Orchestrator’s expectations of the Inventory Service’s API response shape, and the Inventory Service’s actual response shape, stay in sync — this catches breaking API changes at build time instead of at 2 a.m. in production.

19.3.2 Saga Integration Tests

A dedicated suite of integration tests runs the full saga against real (but isolated, test-environment) instances of Inventory, Payment, and Order services, specifically exercising every compensation path: what happens if the payment hold fails after inventory is reserved, what happens if order creation fails after the hold succeeds. These compensation-path tests are the ones teams most often skip under time pressure, and they are exactly the tests that catch the bugs which cause real financial discrepancies later.

19.3.3 Chaos Testing for External Dependencies

Since the Courier Partner Adapter and Payment Service depend on unreliable third parties, periodic chaos testing — deliberately injecting latency or failures into these external calls in a staging environment — validates that circuit breakers, retries, and fallback behaviours actually work as designed, rather than only being tested in the easy, everything-succeeds case.

19.4 Data Privacy Considerations

A TBYB system stores meaningful personal data: home addresses (for pickup), payment tokens, and a detailed history of what a customer tried, kept, and returned, which can reveal sensitive inferences about body size, income level, and personal habits. Under regulations like GDPR in Europe or India’s Digital Personal Data Protection Act, customers have rights to access, correct, and delete this data. Practically, this means the Order and Return services need a data-export endpoint that assembles a customer’s full trial and return history on request, and a data-deletion workflow that anonymises (rather than simply deletes) historical records needed for financial audit retention, replacing personally identifying fields with a non-reversible reference while preserving the aggregate transaction record required by law.

i
What an Interviewer May Ask

“A customer requests full account deletion under privacy law, but you’re required to retain financial transaction records for seven years. How do you reconcile that?” The expected answer is anonymisation rather than hard deletion — strip personally identifying fields (name, address, contact info) from the historical record while retaining the transaction facts (amounts, dates, SKU references) needed for audit and tax compliance, and delete or tokenise anything not legally required to be kept.

20

Frequently Asked Questions

The most common questions that come up when engineers first design a TBYB flow, answered directly.

Q1Why not just charge the full amount upfront and refund what is returned?

This is the traditional buy-first model, and it works, but it creates worse cash-flow perception for the customer (their money is gone until a refund clears, which can take days) and typically produces lower conversion for uncertain shoppers, since the psychological barrier of “spend money now” is higher than “reserve a hold that may never be charged.”

Q2How do you stop customers from trialling dozens of items with no intention of buying any?

Rate-limit the number of concurrent open trial orders and total trial value per customer, especially for new accounts, and tighten these limits dynamically based on a fraud score built from historical keep rate and return behaviour.

Q3What happens if the courier loses the returned package?

The Pickup Scheduling Service should track a proof-of-pickup event (courier scan or photo) as soon as the item leaves the customer’s hands; if a package is later lost in transit, this proof-of-pickup shifts liability to the courier partner contractually, and the customer’s hold should still be released since the loss is not their fault.

Q4Should exchanges create a new payment hold or reuse the original?

As discussed earlier, it is safer to place a new, separate hold for the replacement item’s value, only capturing it once the original returned item’s QC passes — this avoids exposing the business to double inventory risk on a single hold.

Q5How would you extend this design to support cross-border TBYB, where returns must cross international borders?

Cross-border reverse logistics adds customs documentation, longer transit times, and currency-conversion complexity to the refund step. In practice, most marketplaces route cross-border returns through regional consolidation warehouses — the customer’s return goes to a nearby domestic facility first, gets QC’d locally, and only aggregated batches move across the border periodically, rather than shipping every single returned item internationally, which would be prohibitively slow and expensive.

Q6How do you decide which items are even eligible for TBYB versus buy-first only?

Eligibility is usually a rules-based decision made at the product-catalog level, factoring in the item’s historical return rate, its resale value after a QC cycle (very cheap items may not be worth the reverse logistics cost), and category — intimates and swimwear, for hygiene reasons, are commonly excluded from TBYB across most real marketplaces regardless of return rate.

Q7What is the difference between a refund and releasing a hold, from a systems perspective?

If the payment was only ever authorised (held) and never captured, “returning” it is simply a hold release — no money ever actually moved, so there is nothing to refund. If the payment had already been captured (for example, in a hybrid model where the full amount is charged upfront and refunded on return), then a genuine refund transaction must be issued through the payment gateway, which is a slower, separate financial operation with its own settlement timeline. This is one more reason the hold-then-capture-on-keep model is usually preferred for TBYB specifically — it avoids the refund step entirely for the common case.

Q8If you had to cut scope for a first version (v1) of this system, what would you defer?

A reasonable v1 could launch with a single courier partner (deferring the multi-partner adapter abstraction), a fixed trial window with no per-customer dynamic adjustment, and manual QC-dispute resolution instead of an automated fraud-scoring pipeline. The core saga for trial creation, the state machine for order lifecycle, and the basic pickup-to-refund flow are not safely deferrable, since they define the fundamental customer promise; everything else — sophisticated fraud scoring, multi-courier routing optimisation, dynamic incentive tuning — can reasonably be layered on once the core loop is proven and generating real usage data to inform those later investments.

21

Summary & Key Takeaways

Designing a Try Before You Buy system for apparel is where distributed transactions, physical logistics, and payment flows all meet at once. Here are the ideas worth carrying forward from this whole tutorial.

Key Takeaways

  • TBYB for apparel solves the fit-uncertainty problem that drives high return rates in online clothing sales, by deferring payment until after a trial period.
  • The architecture layers client apps behind a CDN, Load Balancer, and API Gateway, then fans out to core commerce microservices and a dedicated reverse logistics domain.
  • The Saga pattern coordinates the multi-service trial-creation transaction (inventory reservation, payment hold, order creation) with explicit compensating actions.
  • The order/trial-item lifecycle is best modelled as an explicit state machine, which cleanly separates keep, return, exchange, and dispute paths.
  • Reverse logistics — automated pickup scheduling, courier integration through an anti-corruption layer, warehouse QC, and restocking — is the true differentiator and the biggest cost centre of a TBYB system.
  • Reliability patterns like circuit breakers, saga reconciliation jobs, and idempotency keys are not optional extras; they are core to keeping a financial, multi-service flow correct in production.
  • Fraud prevention must be designed specifically for TBYB’s unique risk surface: wardrobing, hold abandonment, and address-mismatch fraud, none of which exist in buy-first commerce.
💡
Final Thought

TBYB looks, from the outside, like a friendly customer feature. From the inside it is a small distributed systems textbook — sagas, state machines, idempotency, event-driven reverse logistics, and financial correctness all rolled into one. A team that can build this well can build almost any e-commerce backend, because every hard problem in modern commerce — delayed money movement, ambiguous inventory state, and automated physical logistics — shows up here at the same time.