System Design
Part 3 of 5Amazon Frontend System Architecture: Engineering E-Commerce at 300M Customers
Amazon Frontend System Architecture: Engineering E-Commerce at 300M Customers
1. Product Overview
Amazon operates the world's largest e-commerce platform, serving 300M+ active customers across 20+ marketplaces with 350M+ products. But the frontend challenge isn't just displaying products—it's orchestrating real-time inventory from millions of sellers, personalized recommendations across billions of user-item pairs, sub-second search across hundreds of millions of products, and checkout flows that process $4,000+ per second during peak events like Prime Day.
Scale assumptions:
- 300M+ active customer accounts
- 350M+ products across all marketplaces
- 2M+ active third-party sellers
- 2.5B+ daily page views
- $4,000+ in sales per second (peak: $12,000+/sec on Prime Day)
- 20+ country-specific marketplaces
- 100+ currencies and payment methods
- 50,000+ A/B tests running concurrently
- 200,000+ deploys per year (every 11.7 seconds average)
Frontend complexity drivers:
- Product detail pages: 100+ data sources (inventory, pricing, reviews, variants, seller info)
- Real-time inventory: Stock levels change every second; must show accurate availability
- Buy Box: Complex algorithm determining which seller "wins" the primary purchase button
- Personalization: Every page element potentially user-specific (recommendations, pricing, ads)
- Multi-marketplace: Same product, different UI/UX per country (tax display, delivery options)
- Performance constraints: Serve users on 2G networks in emerging markets
- Checkout complexity: Gift cards, promotions, Subscribe & Save, payment methods, delivery scheduling
- Peak traffic: 10-100x normal traffic during Prime Day, Black Friday
The frontend is not a monolithic application—it's an orchestrated symphony of 100+ independently deployable services, each owned by autonomous teams operating at Amazon's legendary pace.
2. Frontend Challenges
2.1 Product Detail Page (PDP) Complexity
The Amazon product page is one of the most complex pages on the internet:
Data sources (100+):
- Product catalog (title, description, bullet points, brand)
- Images and videos (up to 10 images, 360° views, product videos)
- Pricing (list price, sale price, Subscribe & Save, used prices)
- Inventory (in stock, low stock, out of stock, delivery date)
- Seller information (Buy Box winner, all offers, seller ratings)
- Reviews (rating, count, review highlights, Q&A)
- Variants (size, color, style—can be 100+ combinations)
- Recommendations (frequently bought together, similar items, sponsored)
- A+ Content (enhanced brand content with rich media)
- Deals (Lightning Deals, coupons, Prime Day specials)
- Delivery options (Prime, same-day, scheduled delivery)
- Financing (monthly payments, BNPL options)
Challenge: Aggregate 100+ data sources into a single page that loads in <2 seconds.
Product Page Request
│
├─> Service 1: Catalog (title, description) ─┐
├─> Service 2: Pricing (list, sale, Subscribe) │
├─> Service 3: Inventory (stock, delivery) │
├─> Service 4: Seller Info (Buy Box, offers) ├─> Orchestration layer
├─> Service 5: Reviews (rating, count) │ aggregates all data
├─> Service 6: Images (URLs, zoom data) │ (parallel calls)
├─> Service 7: Recommendations │
└─> Service 8-100+: ... ─┘
│
▼
Render PDP
2.2 Real-Time Inventory and Pricing
Problem: Inventory changes constantly. User sees "In Stock," adds to cart, but item sold out 5 seconds ago.
Constraints:
- 350M+ products with dynamic inventory
- Millions of price changes per day
- Different prices per Prime/non-Prime
- Regional inventory (warehouse-specific availability)
- Third-party seller inventory (2M+ sellers)
Challenge: Show accurate inventory without making a backend call on every page view (would cost billions per day in compute).
Solution: Optimistic display with just-in-time validation:
Page Load: Show cached inventory (TTL: 5 minutes)
Add to Cart: Real-time inventory check (authoritative)
│
├─> Stock available → Add to cart ✓
└─> Stock depleted → Show "Sorry, this item is no longer available"
2.3 Buy Box Algorithm Visualization
Challenge: Multiple sellers offer the same product. Which seller's price/offer to show prominently?
Buy Box factors (simplified):
- Price (landed price including shipping)
- Seller metrics (feedback score, order defect rate)
- Fulfillment method (FBA vs FBM)
- Shipping speed (Prime eligibility)
- Inventory depth
Frontend challenge: Display the winning offer prominently, but allow access to all offers:
function BuyBox({ offers }: { offers: Offer[] }) {
const [winner, ...otherOffers] = sortByBuyBoxScore(offers);
return (
<div className="buy-box">
{/* Primary Buy Box - winning offer */}
<div className="buy-box-primary">
<Price amount={winner.price} />
<DeliveryPromise date={winner.deliveryDate} isPrime={winner.isPrime} />
<AddToCartButton offer={winner} />
{winner.seller.id !== 'AMAZON' && (
<SellerInfo seller={winner.seller} />
)}
</div>
{/* Other offers link */}
{otherOffers.length > 0 && (
<Link href={`/gp/offer-listing/${productId}`}>
See all {otherOffers.length + 1} offers from ${Math.min(...offers.map(o => o.price))}
</Link>
)}
</div>
);
}
2.4 Variant Selection (Size/Color Matrix)
Problem: A t-shirt has 10 sizes × 15 colors = 150 variants. Each variant has different inventory, price, and images.
Challenges:
- Strike-through unavailable combinations (size M + Red is out of stock)
- Update price dynamically when variant changes
- Update images when color changes
- Preserve URL state for sharing
State machine:
interface VariantState {
selectedOptions: Record<string, string>; // { "Size": "M", "Color": "Red" }
availableCombinations: Set<string>; // Valid option combinations
currentVariant: Variant | null;
price: Price;
inventory: InventoryStatus;
images: Image[];
}
function reduceVariantState(state: VariantState, action: VariantAction): VariantState {
switch (action.type) {
case 'SELECT_OPTION': {
const newOptions = { ...state.selectedOptions, [action.dimension]: action.value };
// Find variant matching all selected options
const matchingVariant = findVariant(newOptions, state.availableCombinations);
if (!matchingVariant) {
// Invalid combination - reset conflicting options
return resetConflictingOptions(state, action.dimension, action.value);
}
return {
...state,
selectedOptions: newOptions,
currentVariant: matchingVariant,
price: matchingVariant.price,
inventory: matchingVariant.inventory,
images: action.dimension === 'Color' ? matchingVariant.images : state.images,
};
}
// ...
}
}
2.5 Search with Millions of Results
Challenge: User searches "laptop" → 500,000+ results. How to render efficiently?
Constraints:
- Must show relevant results instantly (<200ms perceived)
- Must support filters (brand, price, rating, Prime eligible)
- Must support sorting (price, rating, newest)
- Must support infinite scroll or pagination
- Must show sponsored results (ads) interspersed
Architecture:
User types "laptop"
│
├─> Autocomplete (as-you-type suggestions)
│ └─> Show dropdown in <100ms
│
├─> User presses Enter
│
├─> Search API (first 48 results + facets)
│ └─> Return in <200ms
│
├─> Render results (SSR or CSR depending on entry point)
│
└─> User scrolls → Fetch next page (infinite scroll)
2.6 Cart Persistence & Sync
Scenario: User adds item on mobile, opens laptop, cart should be synced.
Challenges:
- Cart state must be authoritative (server-side)
- Optimistic UI updates for perceived speed
- Handle conflicts (item added on two devices simultaneously)
- Handle inventory changes (item went out of stock while in cart)
- Handle price changes (price dropped, show "Price decreased!")
class CartManager {
private localCart: CartItem[] = [];
private serverCart: CartItem[] = [];
async addToCart(item: CartItem) {
// 1. Optimistic local update
this.localCart.push(item);
this.notifyListeners();
try {
// 2. Server update
const response = await api.addToCart(item);
this.serverCart = response.cart;
// 3. Reconcile (server is authoritative)
this.localCart = this.serverCart;
this.notifyListeners();
} catch (error) {
// 4. Rollback on failure
this.localCart = this.localCart.filter((i) => i.id !== item.id);
this.notifyListeners();
throw error;
}
}
private reconcile(serverCart: CartItem[], localCart: CartItem[]) {
// Server cart is authoritative
// But preserve local-only items not yet synced
const pendingItems = localCart.filter(
(local) => !serverCart.some((server) => server.id === local.id) && local.pending
);
return [...serverCart, ...pendingItems];
}
}
2.7 Multi-Marketplace Architecture
Amazon operates 20+ marketplaces: amazon.com, amazon.co.uk, amazon.de, amazon.co.jp, etc.
Differences per marketplace:
- Language and translations
- Currency and number formatting
- Tax display (inclusive vs exclusive)
- Payment methods (credit card, iDEAL, UPI, PIX)
- Delivery options (Amazon Locker, pickup points)
- Legal requirements (GDPR, cookie consent)
- Date/time formats
Challenge: Single codebase, localized experience.
// Marketplace configuration
interface MarketplaceConfig {
id: string; // 'ATVPDKIKX0DER' (US), 'A1PA6795UKMFR9' (DE), etc.
countryCode: string;
language: string;
currency: string;
taxDisplay: 'inclusive' | 'exclusive';
dateFormat: string;
primeAvailable: boolean;
paymentMethods: PaymentMethod[];
legalRequirements: LegalRequirement[];
}
// Runtime marketplace detection
function getMarketplace(): MarketplaceConfig {
// Determined by domain (amazon.de), cookie, or explicit selection
const domain = window.location.hostname;
if (domain.endsWith('.de')) return MARKETPLACE_DE;
if (domain.endsWith('.co.uk')) return MARKETPLACE_UK;
if (domain.endsWith('.co.jp')) return MARKETPLACE_JP;
return MARKETPLACE_US; // Default
}
// Localized price display
function formatPrice(amount: number, currency: string, marketplace: MarketplaceConfig): string {
const formatter = new Intl.NumberFormat(marketplace.language, {
style: 'currency',
currency,
});
const formatted = formatter.format(amount);
if (marketplace.taxDisplay === 'inclusive') {
return `${formatted} inkl. MwSt.`; // German: "including VAT"
}
return formatted;
}
3. High-Level Frontend Architecture
3.1 Service-Oriented Frontend (Micro-Frontends at Scale)
Amazon pioneered service-oriented architecture—and the frontend is no different. Each page is composed of independently deployable widgets owned by different teams.
┌─────────────────────────────────────────────────────────────────┐
│ Product Detail Page │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Header Widget (Search, Cart, Account) - Team: NavBar ││
│ └─────────────────────────────────────────────────────────────┘│
│ ┌───────────────────┐ ┌───────────────────────────────────────┐│
│ │ │ │ Title Widget - Team: Catalog ││
│ │ Image Gallery │ ├───────────────────────────────────────┤│
│ │ Widget │ │ Price Widget - Team: Pricing ││
│ │ Team: Media │ ├───────────────────────────────────────┤│
│ │ │ │ Buy Box Widget - Team: BuyBox ││
│ │ │ ├───────────────────────────────────────┤│
│ │ │ │ Delivery Widget - Team: Shipping ││
│ └───────────────────┘ └───────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Recommendations Widget - Team: Personalization ││
│ └─────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Reviews Widget - Team: Reviews ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
Widget architecture:
// Each widget is a self-contained module
interface Widget {
name: string;
version: string;
owner: string; // Team name
render: (container: HTMLElement, props: WidgetProps) => void;
prefetch?: () => Promise<void>;
cleanup?: () => void;
}
// Widget registry
class WidgetRegistry {
private widgets: Map<string, Widget> = new Map();
register(widget: Widget) {
this.widgets.set(widget.name, widget);
}
async render(name: string, container: HTMLElement, props: WidgetProps) {
const widget = this.widgets.get(name);
if (!widget) {
console.error(`Widget ${name} not found`);
return;
}
// Prefetch data if available
await widget.prefetch?.();
// Render widget
widget.render(container, props);
}
}
// Usage
const buyBoxWidget: Widget = {
name: 'BuyBox',
version: '2.3.4',
owner: 'buy-box-team',
prefetch: async () => {
// Prefetch seller offers
await queryClient.prefetchQuery(['offers', productId]);
},
render: (container, props) => {
const root = createRoot(container);
root.render(<BuyBoxComponent productId={props.productId} />);
},
cleanup: () => {
// Cleanup subscriptions, timers, etc.
},
};
3.2 Rendering Strategy: Hybrid SSR + CSR
Amazon's approach:
Entry Point Rendering Strategy
──────────────────────────────────────────────
Google search result → SSR (SEO critical)
Direct URL → SSR (fast FCP)
In-app navigation → CSR (SPA-like)
Mobile app webview → CSR (optimized)
Search results page → SSR + progressive hydration
Product page → SSR + islands hydration
Streaming SSR for product pages:
// Server-side rendering with streaming
async function renderProductPage(productId: string, response: Response) {
// 1. Send HTML shell immediately
response.write(`
<!DOCTYPE html>
<html>
<head>
<title>Loading...</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<div id="header">${renderHeader()}</div>
<div id="content">
`);
// 2. Fetch critical data (title, price, image)
const criticalData = await fetchCriticalProductData(productId);
// 3. Stream critical content
response.write(`
<div id="product-critical">
${renderProductCritical(criticalData)}
</div>
`);
// 4. Start streaming non-critical content
response.write(`<div id="product-secondary">`);
// 5. Stream reviews (can be slow)
const reviewsPromise = fetchReviews(productId);
const recommendationsPromise = fetchRecommendations(productId);
const reviews = await reviewsPromise;
response.write(`
<div id="reviews">${renderReviews(reviews)}</div>
`);
const recommendations = await recommendationsPromise;
response.write(`
<div id="recommendations">${renderRecommendations(recommendations)}</div>
`);
// 6. Close document
response.write(`
</div>
<script src="/app.js"></script>
</body>
</html>
`);
response.end();
}
3.3 CDN Architecture: CloudFront at Global Scale
Amazon uses CloudFront (their own CDN) with 400+ edge locations.
Caching strategy:
Request for amazon.com/dp/B08N5WRWNW
│
├─> CloudFront Edge (nearest to user)
│ │
│ ├─> Cache HIT: Return cached HTML (TTL: 5-60 seconds)
│ │
│ └─> Cache MISS:
│ │
│ ├─> Regional Edge Cache (if available)
│ │
│ └─> Origin (Amazon servers)
│
└─> Browser receives response
Cache invalidation:
- Price changes → Invalidate affected product pages
- Inventory changes → Invalidate or use Edge Side Includes (ESI)
- Personalization → Bypass cache, use ?ref= parameter
Edge Side Includes (ESI) for dynamic content:
<!-- Cached HTML with ESI placeholders -->
<div class="product-page">
<h1>Apple AirPods Pro</h1>
<!-- Price is dynamic, fetched at edge -->
<esi:include src="/fragments/price?productId=B08N5WRWNW" />
<!-- Inventory is dynamic -->
<esi:include src="/fragments/inventory?productId=B08N5WRWNW" />
<!-- Recommendations are personalized -->
<esi:include src="/fragments/recommendations?productId=B08N5WRWNW&userId=$USER_ID" />
</div>
3.4 State Management: Distributed by Design
Amazon doesn't use a single global state store like Redux. Instead, each widget manages its own state with server synchronization.
// Each widget has its own state
function BuyBoxWidget({ productId }: { productId: string }) {
// Local state for this widget
const [quantity, setQuantity] = useState(1);
const [selectedOffer, setSelectedOffer] = useState<Offer | null>(null);
// Server state via React Query
const { data: offers } = useQuery({
queryKey: ['offers', productId],
queryFn: () => fetchOffers(productId),
});
// Cart state via global cart service
const cart = useCart();
return (
<div>
<OfferSelector offers={offers} onSelect={setSelectedOffer} />
<QuantitySelector value={quantity} onChange={setQuantity} />
<AddToCartButton
onClick={() => cart.add(selectedOffer, quantity)}
/>
</div>
);
}
// Cart service (singleton, shared across widgets)
class CartService {
private listeners: Set<() => void> = new Set();
private cart: CartItem[] = [];
async add(offer: Offer, quantity: number) {
// Optimistic update
this.cart.push({ offer, quantity, pending: true });
this.notify();
// Server sync
const response = await api.addToCart(offer.id, quantity);
this.cart = response.cart;
this.notify();
}
subscribe(listener: () => void) {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
private notify() {
this.listeners.forEach((l) => l());
}
}
// React hook for cart
function useCart() {
const [, forceUpdate] = useReducer((x) => x + 1, 0);
useEffect(() => {
return cartService.subscribe(forceUpdate);
}, []);
return cartService;
}
4. Search Architecture
4.1 Autocomplete (As-You-Type)
Requirement: Show suggestions within 100ms of keystroke.
Architecture:
User types "lap"
│
├─> Debounce (50ms)
│
├─> Local cache check (recent searches, trending)
│ └─> If HIT: Show immediately
│
├─> API call to autocomplete service
│ └─> Returns in <100ms (edge-cached)
│
└─> Render dropdown
├─> Search suggestions ("laptop", "laptop stand", "laptop bag")
├─> Category suggestions ("Laptops", "Laptop Accessories")
└─> Product suggestions (top 3 products with images)
Implementation:
function SearchAutocomplete() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebouncedValue(query, 50);
const { data: suggestions } = useQuery({
queryKey: ['autocomplete', debouncedQuery],
queryFn: () => fetchAutocomplete(debouncedQuery),
enabled: debouncedQuery.length >= 2,
staleTime: 60_000, // Cache for 1 minute
});
return (
<div className="search-container">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search Amazon"
/>
{suggestions && (
<div className="autocomplete-dropdown">
{/* Search suggestions */}
{suggestions.queries.map((q) => (
<SearchSuggestion key={q} query={q} highlight={query} />
))}
{/* Category suggestions */}
{suggestions.categories.map((cat) => (
<CategorySuggestion key={cat.id} category={cat} />
))}
{/* Product suggestions */}
{suggestions.products.map((product) => (
<ProductSuggestion key={product.id} product={product} />
))}
</div>
)}
</div>
);
}
4.2 Search Results Page (SERP)
Challenge: Render 48+ products with images, prices, ratings, sponsored labels—fast.
Optimization: Above-the-fold priority
function SearchResults({ query }: { query: string }) {
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['search', query],
queryFn: ({ pageParam = 1 }) => searchProducts(query, pageParam),
getNextPageParam: (lastPage) => lastPage.nextPage,
});
const allProducts = data?.pages.flatMap((page) => page.products) ?? [];
return (
<div className="search-results">
{/* Filters sidebar */}
<SearchFilters query={query} />
{/* Results grid */}
<div className="results-grid">
{allProducts.map((product, index) => (
<ProductCard
key={product.id}
product={product}
priority={index < 8 ? 'high' : 'low'} // First 8 are above-the-fold
loading={index < 8 ? 'eager' : 'lazy'}
/>
))}
{/* Infinite scroll trigger */}
<InfiniteScrollTrigger
hasMore={hasNextPage}
onLoadMore={fetchNextPage}
/>
</div>
</div>
);
}
function ProductCard({
product,
priority,
loading,
}: {
product: Product;
priority: 'high' | 'low';
loading: 'eager' | 'lazy';
}) {
return (
<article className="product-card">
<img
src={product.imageUrl}
alt={product.title}
loading={loading}
fetchPriority={priority}
/>
<h3>{product.title}</h3>
<StarRating rating={product.rating} count={product.reviewCount} />
<Price
current={product.price}
original={product.listPrice}
isPrime={product.isPrimeEligible}
/>
{product.isSponsored && <SponsoredLabel />}
</article>
);
}
4.3 Faceted Navigation (Filters)
Challenge: Update results when filter changes without full page reload.
interface SearchFilters {
priceRange: [number, number] | null;
brands: string[];
rating: number | null;
primeOnly: boolean;
freeShipping: boolean;
category: string | null;
}
function useSearchFilters(initialFilters: SearchFilters) {
const [filters, setFilters] = useState(initialFilters);
const router = useRouter();
// Sync filters to URL
useEffect(() => {
const params = new URLSearchParams();
if (filters.priceRange) {
params.set('price', `${filters.priceRange[0]}-${filters.priceRange[1]}`);
}
if (filters.brands.length > 0) {
params.set('brands', filters.brands.join(','));
}
if (filters.rating) {
params.set('rating', filters.rating.toString());
}
if (filters.primeOnly) {
params.set('prime', 'true');
}
router.push(`/s?${params.toString()}`, { scroll: false });
}, [filters]);
const updateFilter = <K extends keyof SearchFilters>(
key: K,
value: SearchFilters[K]
) => {
setFilters((prev) => ({ ...prev, [key]: value }));
};
return { filters, updateFilter };
}
function FiltersSidebar({ facets }: { facets: SearchFacets }) {
const { filters, updateFilter } = useSearchFilters(defaultFilters);
return (
<aside className="filters-sidebar">
{/* Prime filter */}
<FilterSection title="Delivery">
<Checkbox
checked={filters.primeOnly}
onChange={(checked) => updateFilter('primeOnly', checked)}
label="Get It by Tomorrow"
/>
</FilterSection>
{/* Brand filter */}
<FilterSection title="Brand">
{facets.brands.map((brand) => (
<Checkbox
key={brand.name}
checked={filters.brands.includes(brand.name)}
onChange={(checked) => {
const newBrands = checked
? [...filters.brands, brand.name]
: filters.brands.filter((b) => b !== brand.name);
updateFilter('brands', newBrands);
}}
label={`${brand.name} (${brand.count})`}
/>
))}
</FilterSection>
{/* Price filter */}
<FilterSection title="Price">
<PriceRangeSlider
min={facets.priceRange.min}
max={facets.priceRange.max}
value={filters.priceRange}
onChange={(range) => updateFilter('priceRange', range)}
/>
</FilterSection>
{/* Rating filter */}
<FilterSection title="Customer Reviews">
{[4, 3, 2, 1].map((rating) => (
<RadioButton
key={rating}
checked={filters.rating === rating}
onChange={() => updateFilter('rating', rating)}
label={<StarRating rating={rating} suffix="& Up" />}
/>
))}
</FilterSection>
</aside>
);
}
5. Checkout Architecture
5.1 Single-Page Checkout Flow
Amazon's checkout is optimized for conversion:
Cart → Checkout
│
├─> Step 1: Shipping Address
│ └─> Select saved address or add new
│
├─> Step 2: Payment Method
│ └─> Select saved card, add new, or use gift card
│
├─> Step 3: Review Order
│ └─> Verify items, quantities, totals
│
└─> Place Order
└─> One-click (if enabled) or confirm
Checkout state machine:
type CheckoutStep = 'shipping' | 'payment' | 'review' | 'confirmation';
interface CheckoutState {
step: CheckoutStep;
shippingAddress: Address | null;
paymentMethod: PaymentMethod | null;
items: CartItem[];
totals: OrderTotals;
error: string | null;
}
type CheckoutAction =
| { type: 'SELECT_ADDRESS'; address: Address }
| { type: 'SELECT_PAYMENT'; payment: PaymentMethod }
| { type: 'APPLY_PROMO'; code: string }
| { type: 'PLACE_ORDER' }
| { type: 'ORDER_SUCCESS'; orderId: string }
| { type: 'ORDER_FAILURE'; error: string };
function checkoutReducer(state: CheckoutState, action: CheckoutAction): CheckoutState {
switch (action.type) {
case 'SELECT_ADDRESS':
return {
...state,
shippingAddress: action.address,
step: 'payment',
};
case 'SELECT_PAYMENT':
return {
...state,
paymentMethod: action.payment,
step: 'review',
};
case 'PLACE_ORDER':
return {
...state,
step: 'confirmation',
};
case 'ORDER_SUCCESS':
return {
...state,
orderId: action.orderId,
};
case 'ORDER_FAILURE':
return {
...state,
error: action.error,
step: 'review', // Return to review step
};
default:
return state;
}
}
5.2 One-Click Ordering
Patent-famous feature: Purchase with a single click (no checkout flow).
interface OneClickConfig {
enabled: boolean;
defaultAddress: Address;
defaultPayment: PaymentMethod;
defaultShippingSpeed: 'standard' | 'expedited' | 'priority';
}
async function oneClickPurchase(productId: string, offerId: string) {
const config = await getOneClickConfig();
if (!config.enabled) {
// Redirect to normal checkout
return router.push(`/checkout?productId=${productId}`);
}
// Show confirmation modal (brief)
const confirmed = await showOneClickModal({
address: config.defaultAddress,
payment: config.defaultPayment,
shippingSpeed: config.defaultShippingSpeed,
});
if (!confirmed) return;
// Place order immediately
const order = await api.placeOrder({
items: [{ productId, offerId, quantity: 1 }],
address: config.defaultAddress,
payment: config.defaultPayment,
shippingSpeed: config.defaultShippingSpeed,
});
// Show success
showToast(`Order placed! Arriving ${order.estimatedDelivery}`);
// Navigate to order confirmation
router.push(`/orders/${order.id}`);
}
5.3 Subscribe & Save
Feature: Recurring delivery with discount.
function SubscribeAndSave({ productId, basePrice }: {
productId: string;
basePrice: number;
}) {
const [frequency, setFrequency] = useState<SubscriptionFrequency>('monthly');
const [quantity, setQuantity] = useState(1);
const discount = calculateSubscribeDiscount(frequency); // 5-15% off
const finalPrice = basePrice * (1 - discount);
return (
<div className="subscribe-save">
<h3>Subscribe & Save</h3>
<div className="savings">
<span className="original-price">${basePrice.toFixed(2)}</span>
<span className="subscribe-price">${finalPrice.toFixed(2)}</span>
<span className="discount">Save {(discount * 100).toFixed(0)}%</span>
</div>
<FrequencySelector
value={frequency}
onChange={setFrequency}
options={['weekly', 'biweekly', 'monthly', 'bimonthly']}
/>
<QuantitySelector value={quantity} onChange={setQuantity} />
<button onClick={() => subscribeToProduct(productId, frequency, quantity)}>
Subscribe Now
</button>
<p className="terms">
Cancel anytime. No commitment. Delivery schedule can be changed.
</p>
</div>
);
}
6. Personalization Engine (Frontend Integration)
6.1 Recommendation Widgets
Amazon shows personalized recommendations everywhere:
- "Customers who bought this item also bought"
- "Frequently bought together"
- "Inspired by your browsing history"
- "Based on items in your cart"
Widget implementation:
interface RecommendationWidget {
type: 'similar_items' | 'bought_together' | 'browsing_history' | 'cart_based';
title: string;
products: Product[];
algorithm: string; // For A/B testing attribution
}
function RecommendationsCarousel({ widget }: { widget: RecommendationWidget }) {
const carouselRef = useRef<HTMLDivElement>(null);
const [visibleStartIndex, setVisibleStartIndex] = useState(0);
// Track impressions for ML feedback
useEffect(() => {
trackRecommendationImpression({
widgetType: widget.type,
algorithm: widget.algorithm,
productIds: widget.products.map((p) => p.id),
});
}, [widget]);
// Track clicks for ML feedback
const handleProductClick = (product: Product, index: number) => {
trackRecommendationClick({
widgetType: widget.type,
algorithm: widget.algorithm,
productId: product.id,
position: index,
});
router.push(`/dp/${product.id}`);
};
return (
<section className="recommendations-widget">
<h2>{widget.title}</h2>
<div className="carousel-container">
<button
className="carousel-prev"
onClick={() => setVisibleStartIndex((i) => Math.max(0, i - 4))}
disabled={visibleStartIndex === 0}
>
←
</button>
<div className="carousel-track" ref={carouselRef}>
{widget.products.slice(visibleStartIndex, visibleStartIndex + 6).map((product, index) => (
<ProductCard
key={product.id}
product={product}
onClick={() => handleProductClick(product, visibleStartIndex + index)}
/>
))}
</div>
<button
className="carousel-next"
onClick={() => setVisibleStartIndex((i) => Math.min(widget.products.length - 6, i + 4))}
disabled={visibleStartIndex >= widget.products.length - 6}
>
→
</button>
</div>
</section>
);
}
6.2 "Frequently Bought Together" Bundle
function FrequentlyBoughtTogether({
mainProduct,
bundleProducts,
}: {
mainProduct: Product;
bundleProducts: Product[];
}) {
const [selectedProducts, setSelectedProducts] = useState<Set<string>>(
new Set([mainProduct.id, ...bundleProducts.map((p) => p.id)])
);
const totalPrice = [mainProduct, ...bundleProducts]
.filter((p) => selectedProducts.has(p.id))
.reduce((sum, p) => sum + p.price, 0);
const handleAddAllToCart = () => {
const productsToAdd = [mainProduct, ...bundleProducts].filter((p) =>
selectedProducts.has(p.id)
);
cart.addMultiple(productsToAdd);
};
return (
<div className="frequently-bought-together">
<h2>Frequently bought together</h2>
<div className="bundle-products">
<ProductCheckbox
product={mainProduct}
checked={selectedProducts.has(mainProduct.id)}
onChange={(checked) => toggleProduct(mainProduct.id, checked)}
disabled // Main product always included
/>
{bundleProducts.map((product, index) => (
<Fragment key={product.id}>
<span className="plus-sign">+</span>
<ProductCheckbox
product={product}
checked={selectedProducts.has(product.id)}
onChange={(checked) => toggleProduct(product.id, checked)}
/>
</Fragment>
))}
</div>
<div className="bundle-total">
<span>Total price:</span>
<span className="price">${totalPrice.toFixed(2)}</span>
</div>
<button onClick={handleAddAllToCart}>
Add all {selectedProducts.size} to Cart
</button>
</div>
);
}
6.3 Personalized Home Feed
Amazon's home page is entirely personalized:
interface HomePageSection {
id: string;
type: 'carousel' | 'grid' | 'deals' | 'categories' | 'editorial';
title: string;
items: (Product | Deal | Category)[];
personalizationSignal: string; // "based on your browsing history"
}
async function fetchPersonalizedHomeFeed(userId: string): Promise<HomePageSection[]> {
const response = await fetch('/api/home/personalized', {
headers: {
'X-User-Id': userId,
'X-Session-Id': sessionStorage.getItem('sessionId'),
},
});
return response.json();
}
// Home page renders personalized sections
function HomePage() {
const { data: sections } = useQuery({
queryKey: ['home', 'personalized'],
queryFn: () => fetchPersonalizedHomeFeed(userId),
staleTime: 5 * 60 * 1000, // 5 minutes
});
return (
<div className="home-page">
{sections?.map((section) => (
<HomeSection key={section.id} section={section} />
))}
</div>
);
}
function HomeSection({ section }: { section: HomePageSection }) {
switch (section.type) {
case 'carousel':
return (
<ProductCarousel
title={section.title}
products={section.items as Product[]}
subtitle={section.personalizationSignal}
/>
);
case 'deals':
return <DealsGrid deals={section.items as Deal[]} />;
case 'categories':
return <CategoryGrid categories={section.items as Category[]} />;
default:
return null;
}
}
7. A/B Testing at Scale (Weblab)
7.1 Amazon's Experimentation Platform
Amazon runs 50,000+ concurrent A/B tests. Their internal system is called Weblab.
Architecture:
User requests page
│
├─> Weblab service (edge)
│ └─> Compute experiment assignments
│ └─> Return treatment IDs
│
├─> Frontend receives treatments
│ └─> Render appropriate variant
│
└─> Track metrics
└─> Conversion, clicks, revenue
Frontend integration:
interface Treatment {
experimentId: string;
treatmentId: string;
parameters: Record<string, unknown>;
}
// Fetch treatments for current user
async function fetchTreatments(userId: string): Promise<Treatment[]> {
const response = await fetch('/api/weblab/treatments', {
headers: { 'X-User-Id': userId },
});
return response.json();
}
// React context for experiments
const WeblabContext = createContext<Treatment[]>([]);
function useExperiment(experimentId: string): Treatment | null {
const treatments = useContext(WeblabContext);
return treatments.find((t) => t.experimentId === experimentId) ?? null;
}
// Usage in component
function BuyButton({ productId }: { productId: string }) {
const experiment = useExperiment('BUY_BUTTON_COLOR_2024');
const buttonColor = experiment?.parameters.color ?? 'orange'; // Control: orange
return (
<button
style={{ backgroundColor: buttonColor }}
onClick={() => handleBuyClick(productId, experiment)}
>
Add to Cart
</button>
);
}
// Track experiment exposure and conversion
function handleBuyClick(productId: string, experiment: Treatment | null) {
// Track conversion
analytics.track('add_to_cart', {
productId,
experimentId: experiment?.experimentId,
treatmentId: experiment?.treatmentId,
});
// Add to cart
cart.add(productId);
}
7.2 Feature Flags with Gradual Rollout
interface FeatureFlag {
name: string;
enabled: boolean;
rolloutPercentage: number;
targetedUsers: string[]; // Specific user IDs for beta
parameters: Record<string, unknown>;
}
class FeatureFlagService {
private flags: Map<string, FeatureFlag> = new Map();
async initialize(userId: string) {
const response = await fetch('/api/features', {
headers: { 'X-User-Id': userId },
});
const flags: FeatureFlag[] = await response.json();
flags.forEach((flag) => this.flags.set(flag.name, flag));
}
isEnabled(flagName: string, userId: string): boolean {
const flag = this.flags.get(flagName);
if (!flag) return false;
// Check if user is explicitly targeted
if (flag.targetedUsers.includes(userId)) return true;
// Check rollout percentage
const hash = murmurhash3(`${userId}:${flagName}`);
const bucket = hash % 100;
return bucket < flag.rolloutPercentage;
}
getParameter<T>(flagName: string, paramName: string, defaultValue: T): T {
const flag = this.flags.get(flagName);
if (!flag?.enabled) return defaultValue;
return (flag.parameters[paramName] as T) ?? defaultValue;
}
}
// Usage
function NewCheckoutFlow() {
const isNewCheckout = featureFlags.isEnabled('NEW_CHECKOUT_2024', userId);
if (isNewCheckout) {
return <NewCheckout />;
}
return <LegacyCheckout />;
}
8. Frontend Performance Engineering
8.1 Core Web Vitals Optimization
Amazon's targets:
- LCP: <2.5s (product image or title)
- INP: <200ms (add to cart, filter clicks)
- CLS: <0.1 (no layout shifts from prices loading)
LCP optimization: Critical image preloading
<!-- In <head>, preload hero product image -->
<link
rel="preload"
as="image"
href="https://images-na.ssl-images-amazon.com/images/I/product-image.jpg"
fetchpriority="high"
/>
INP optimization: Optimistic updates
// Before: Wait for server response
async function handleAddToCart(productId: string) {
setLoading(true);
await api.addToCart(productId); // 200-500ms
setLoading(false);
setCartCount((c) => c + 1);
}
// After: Optimistic update
function handleAddToCart(productId: string) {
// Immediately update UI
setCartCount((c) => c + 1);
showToast('Added to cart');
// Sync with server in background
api.addToCart(productId).catch(() => {
// Rollback on failure
setCartCount((c) => c - 1);
showToast('Failed to add to cart', { type: 'error' });
});
}
CLS prevention: Reserved space for dynamic content
/* Reserve space for price before it loads */
.price-container {
min-height: 24px; /* Prevents shift when price loads */
}
/* Reserve space for product images */
.product-image-container {
aspect-ratio: 1 / 1;
background-color: #f7f7f7;
}
/* Reserve space for reviews */
.reviews-summary {
min-height: 20px;
}
8.2 Performance for Emerging Markets
Challenge: Serve users on 2G/3G networks in India, Brazil, Indonesia.
Optimizations:
- Lite mode: Reduced images, no videos, minimal JS
function useLiteMode(): boolean {
// Detect slow network
const connection = navigator.connection;
if (connection?.effectiveType === '2g' || connection?.effectiveType === 'slow-2g') {
return true;
}
if (connection?.saveData) {
return true;
}
return false;
}
function ProductImage({ product }: { product: Product }) {
const liteMode = useLiteMode();
const imageUrl = liteMode
? product.images.thumbnail // 50KB
: product.images.large; // 200KB
return <img src={imageUrl} alt={product.title} loading="lazy" />;
}
- Aggressive caching: Service worker caches critical assets
// service-worker.ts
const CACHE_NAME = 'amazon-v1';
const CRITICAL_ASSETS = [
'/',
'/styles/critical.css',
'/scripts/core.js',
'/images/logo.png',
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(CRITICAL_ASSETS))
);
});
self.addEventListener('fetch', (event) => {
// Serve from cache, update in background
event.respondWith(
caches.match(event.request).then((cached) => {
const fetchPromise = fetch(event.request).then((response) => {
const responseClone = response.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseClone);
});
return response;
});
return cached || fetchPromise;
})
);
});
- Lazy loading everything below the fold
function ProductPage({ product }: { product: Product }) {
return (
<>
{/* Critical content loads immediately */}
<ProductTitle title={product.title} />
<ProductImage image={product.images[0]} priority="high" />
<BuyBox offers={product.offers} />
{/* Everything else lazy loads */}
<Suspense fallback={<Skeleton />}>
<LazyReviews productId={product.id} />
</Suspense>
<Suspense fallback={<Skeleton />}>
<LazyRecommendations productId={product.id} />
</Suspense>
<Suspense fallback={<Skeleton />}>
<LazyAPlusContent productId={product.id} />
</Suspense>
</>
);
}
8.3 Image Optimization
Amazon serves billions of images per day. Optimization is critical.
interface ImageConfig {
format: 'webp' | 'avif' | 'jpeg';
quality: number;
width: number;
height: number;
}
function getOptimizedImageUrl(baseUrl: string, config: ImageConfig): string {
// Amazon's image service supports URL parameters
const params = new URLSearchParams({
_SX: config.width.toString(),
_SY: config.height.toString(),
_QL: config.quality.toString(),
_FMT: config.format,
});
return `${baseUrl}?${params}`;
}
// Usage
function ProductImage({ product }: { product: Product }) {
const supportsAvif = checkAvifSupport();
const supportsWebp = checkWebpSupport();
const format = supportsAvif ? 'avif' : supportsWebp ? 'webp' : 'jpeg';
return (
<picture>
<source
srcSet={getOptimizedImageUrl(product.imageUrl, { format: 'avif', quality: 80, width: 500, height: 500 })}
type="image/avif"
/>
<source
srcSet={getOptimizedImageUrl(product.imageUrl, { format: 'webp', quality: 80, width: 500, height: 500 })}
type="image/webp"
/>
<img
src={getOptimizedImageUrl(product.imageUrl, { format: 'jpeg', quality: 85, width: 500, height: 500 })}
alt={product.title}
loading="lazy"
/>
</picture>
);
}
9. Reviews System
9.1 Review Display with Infinite Scroll
function ProductReviews({ productId }: { productId: string }) {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['reviews', productId],
queryFn: ({ pageParam = 1 }) => fetchReviews(productId, pageParam),
getNextPageParam: (lastPage) => lastPage.nextPage,
});
const reviews = data?.pages.flatMap((page) => page.reviews) ?? [];
return (
<section className="reviews-section">
<ReviewsSummary productId={productId} />
<ReviewFilters productId={productId} />
<div className="reviews-list">
{reviews.map((review) => (
<ReviewCard key={review.id} review={review} />
))}
<InfiniteScrollTrigger
hasMore={hasNextPage}
isLoading={isFetchingNextPage}
onLoadMore={fetchNextPage}
/>
</div>
</section>
);
}
function ReviewCard({ review }: { review: Review }) {
const [helpfulVoted, setHelpfulVoted] = useState(false);
return (
<article className="review-card">
<header>
<StarRating rating={review.rating} />
<h4>{review.title}</h4>
</header>
<div className="review-meta">
<span className="reviewer">{review.authorName}</span>
<span className="verified">{review.verifiedPurchase && 'Verified Purchase'}</span>
<span className="date">{formatDate(review.date)}</span>
</div>
<div className="review-body">
{review.body.length > 500 ? (
<TruncatedText text={review.body} maxLength={500} />
) : (
<p>{review.body}</p>
)}
</div>
{review.images.length > 0 && (
<ReviewImages images={review.images} />
)}
<footer>
<button
onClick={() => voteHelpful(review.id)}
disabled={helpfulVoted}
>
Helpful ({review.helpfulVotes + (helpfulVoted ? 1 : 0)})
</button>
<button>Report</button>
</footer>
</article>
);
}
9.2 Review Highlights (AI-Generated)
interface ReviewHighlights {
positives: string[];
negatives: string[];
frequentTopics: Topic[];
}
async function fetchReviewHighlights(productId: string): Promise<ReviewHighlights> {
// AI-generated summary of reviews
const response = await fetch(`/api/products/${productId}/review-highlights`);
return response.json();
}
function ReviewHighlightsCard({ productId }: { productId: string }) {
const { data: highlights } = useQuery({
queryKey: ['review-highlights', productId],
queryFn: () => fetchReviewHighlights(productId),
});
if (!highlights) return null;
return (
<div className="review-highlights">
<h3>Customers say</h3>
<div className="highlight-section positive">
<h4>Customers like</h4>
<ul>
{highlights.positives.map((point, i) => (
<li key={i}>{point}</li>
))}
</ul>
</div>
<div className="highlight-section negative">
<h4>Customers don't like</h4>
<ul>
{highlights.negatives.map((point, i) => (
<li key={i}>{point}</li>
))}
</ul>
</div>
<div className="frequent-topics">
{highlights.frequentTopics.map((topic) => (
<TopicChip key={topic.name} topic={topic} />
))}
</div>
</div>
);
}
10. AI in Frontend Architecture
10.1 Rufus: AI Shopping Assistant
Feature: Conversational AI assistant for product discovery.
function RufusChat() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState('');
const [isStreaming, setIsStreaming] = useState(false);
const sendMessage = async () => {
const userMessage = { role: 'user', content: input };
setMessages((prev) => [...prev, userMessage]);
setInput('');
setIsStreaming(true);
// Stream AI response
const response = await fetch('/api/rufus/chat', {
method: 'POST',
body: JSON.stringify({
messages: [...messages, userMessage],
context: { currentProduct: productId },
}),
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
let assistantMessage = '';
while (true) {
const { done, value } = await reader!.read();
if (done) break;
assistantMessage += decoder.decode(value, { stream: true });
setMessages((prev) => {
const withoutLast = prev.slice(0, -1);
return [...withoutLast, { role: 'assistant', content: assistantMessage }];
});
}
setIsStreaming(false);
};
return (
<div className="rufus-chat">
<div className="chat-messages">
{messages.map((message, i) => (
<ChatMessage key={i} message={message} />
))}
{isStreaming && <TypingIndicator />}
</div>
<div className="chat-input">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Ask Rufus anything about this product..."
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
/>
<button onClick={sendMessage}>Send</button>
</div>
</div>
);
}
10.2 AI-Powered Search
async function aiEnhancedSearch(query: string): Promise<SearchResults> {
// Detect query intent
const intent = await detectSearchIntent(query);
switch (intent.type) {
case 'product_specific':
// "iPhone 15 Pro Max 256GB"
return traditionalSearch(query);
case 'need_based':
// "gift for 10 year old boy who likes science"
return aiRecommendationSearch(query, intent);
case 'comparison':
// "best laptop under $1000"
return comparisonSearch(query, intent);
case 'question':
// "what size TV for 10 foot room"
return questionAnswerSearch(query, intent);
default:
return traditionalSearch(query);
}
}
// AI recommendation for need-based queries
async function aiRecommendationSearch(query: string, intent: SearchIntent): Promise<SearchResults> {
const response = await fetch('/api/ai/recommend', {
method: 'POST',
body: JSON.stringify({ query, intent }),
});
const { products, reasoning } = await response.json();
return {
products,
aiReasoning: reasoning, // "Based on your search for a gift, here are science kits popular with 10-year-olds..."
isAiEnhanced: true,
};
}
10.3 AI Product Summaries
function AIProductSummary({ productId }: { productId: string }) {
const { data: summary } = useQuery({
queryKey: ['ai-summary', productId],
queryFn: () => fetchAISummary(productId),
staleTime: 24 * 60 * 60 * 1000, // Cache for 24 hours
});
if (!summary) return null;
return (
<div className="ai-summary">
<div className="summary-header">
<SparkleIcon />
<span>AI-generated summary from customer reviews</span>
</div>
<p className="summary-text">{summary.text}</p>
<div className="summary-highlights">
<div className="pros">
<h4>What customers love</h4>
<ul>
{summary.pros.map((pro, i) => (
<li key={i}>{pro}</li>
))}
</ul>
</div>
<div className="cons">
<h4>Things to consider</h4>
<ul>
{summary.cons.map((con, i) => (
<li key={i}>{con}</li>
))}
</ul>
</div>
</div>
<p className="disclaimer">
This summary was generated by AI based on customer reviews.
</p>
</div>
);
}
11. Security Architecture
11.1 CSRF Protection
// CSRF token embedded in page
function CheckoutForm() {
const csrfToken = useCSRFToken(); // From server-rendered meta tag
const handleSubmit = async (formData: FormData) => {
const response = await fetch('/api/checkout', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken,
},
body: formData,
credentials: 'include',
});
if (response.status === 403) {
// CSRF token invalid, refresh page
window.location.reload();
return;
}
// Process response
};
return (
<form onSubmit={handleSubmit}>
<input type="hidden" name="_csrf" value={csrfToken} />
{/* Form fields */}
</form>
);
}
11.2 Payment Security (PCI DSS Compliance)
// Hosted payment fields (iframe-based)
function PaymentForm() {
const [cardElement, setCardElement] = useState<HTMLIFrameElement | null>(null);
useEffect(() => {
// Load Amazon Pay hosted fields
const iframe = document.createElement('iframe');
iframe.src = 'https://payments.amazon.com/hosted-fields';
iframe.sandbox.add('allow-scripts', 'allow-same-origin');
cardElement?.appendChild(iframe);
// Listen for tokenized card data
window.addEventListener('message', (event) => {
if (event.origin !== 'https://payments.amazon.com') return;
if (event.data.type === 'card_tokenized') {
// Token is safe to send to our server
handleTokenizedCard(event.data.token);
}
});
}, []);
return (
<div className="payment-form">
<div ref={setCardElement} className="card-element" />
{/* Card data never touches our domain - PCI compliant */}
</div>
);
}
11.3 Bot Protection
// CAPTCHA for suspicious activity
function AddToCartButton({ productId }: { productId: string }) {
const [showCaptcha, setShowCaptcha] = useState(false);
const handleAddToCart = async () => {
try {
await api.addToCart(productId);
} catch (error) {
if (error.code === 'BOT_DETECTED') {
setShowCaptcha(true);
}
}
};
return (
<>
<button onClick={handleAddToCart}>Add to Cart</button>
{showCaptcha && (
<CaptchaModal
onVerify={async (token) => {
await api.addToCart(productId, { captchaToken: token });
setShowCaptcha(false);
}}
/>
)}
</>
);
}
12. Observability & Monitoring
12.1 Real User Monitoring (RUM)
// Performance tracking
function trackPagePerformance() {
// Wait for page to fully load
window.addEventListener('load', () => {
const performance = window.performance;
const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
const paint = performance.getEntriesByType('paint');
const metrics = {
// Navigation timing
dns: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp: navigation.connectEnd - navigation.connectStart,
ttfb: navigation.responseStart - navigation.requestStart,
domContentLoaded: navigation.domContentLoadedEventEnd - navigation.startTime,
load: navigation.loadEventEnd - navigation.startTime,
// Paint timing
fcp: paint.find((p) => p.name === 'first-contentful-paint')?.startTime,
// Page context
page: window.location.pathname,
marketplace: getMarketplace(),
deviceType: getDeviceType(),
};
// Send to analytics
navigator.sendBeacon('/api/rum', JSON.stringify(metrics));
});
// Track LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
navigator.sendBeacon('/api/rum/lcp', JSON.stringify({
lcp: lastEntry.startTime,
element: lastEntry.element?.tagName,
}));
}).observe({ type: 'largest-contentful-paint', buffered: true });
// Track INP
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.interactionId) {
navigator.sendBeacon('/api/rum/inp', JSON.stringify({
inp: entry.duration,
type: entry.name,
}));
}
}
}).observe({ type: 'event', buffered: true, durationThreshold: 16 });
}
12.2 Error Tracking
// Global error handler
window.addEventListener('error', (event) => {
sendError({
type: 'uncaught_error',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack,
page: window.location.pathname,
userAgent: navigator.userAgent,
});
});
// Promise rejection handler
window.addEventListener('unhandledrejection', (event) => {
sendError({
type: 'unhandled_rejection',
message: event.reason?.message || String(event.reason),
stack: event.reason?.stack,
page: window.location.pathname,
});
});
// React error boundary
class ErrorBoundary extends React.Component {
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
sendError({
type: 'react_error',
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
page: window.location.pathname,
});
}
render() {
if (this.state.hasError) {
return <ErrorFallback />;
}
return this.props.children;
}
}
13. Architecture Evolution
13.1 Phase 1: Perl/Mason Templates (1995-2005)
Architecture:
- Server-rendered HTML
- Perl CGI scripts
- Mason templates
- Monolithic application
Pain points:
- Slow page loads
- No interactivity
- Difficult to scale teams
13.2 Phase 2: Service-Oriented Architecture (2005-2012)
Changes:
- Decomposed into microservices
- Each team owns their service
- Widget-based page composition
- Early AJAX adoption
Improvements:
- Teams can deploy independently
- Better fault isolation
- Scalable organization
New challenges:
- Widget coordination complexity
- Inconsistent UX across widgets
13.3 Phase 3: React Adoption (2015-2020)
Changes:
- React for interactive widgets
- GraphQL for data aggregation
- Design system standardization
- Mobile-first responsive design
Improvements:
- Better developer experience
- Consistent UX
- Faster iterations
Challenges:
- Legacy migration (some widgets still Mason)
- Performance on low-end devices
13.4 Phase 4: AI-Powered Commerce (2020-Present)
Changes:
- AI product summaries
- Rufus shopping assistant
- Personalization at scale
- Edge computing
Improvements:
- Better product discovery
- Conversational commerce
- Faster global performance
Current challenges:
- AI latency
- Managing 50,000+ concurrent experiments
- Multi-marketplace complexity
14. Future Architecture
14.1 Server Components for E-Commerce
// Server Component (no client-side JS)
async function ProductDetails({ productId }: { productId: string }) {
// Fetch data on server
const product = await db.products.findUnique({ where: { id: productId } });
const reviews = await db.reviews.findMany({ where: { productId } });
return (
<div>
<h1>{product.title}</h1>
<p>{product.description}</p>
{/* Client component for interactivity */}
<BuyBox productId={productId} />
{/* Server component for static content */}
<ReviewsList reviews={reviews} />
</div>
);
}
14.2 Edge-Native Commerce
// Cloudflare Worker for product page
export default {
async fetch(request: Request) {
const url = new URL(request.url);
const productId = url.pathname.split('/dp/')[1];
// Fetch product from edge KV
const product = await PRODUCTS_KV.get(productId, 'json');
// Personalize at edge
const userId = request.headers.get('X-User-Id');
const recommendations = await getRecommendationsAtEdge(userId, productId);
// Render HTML at edge
const html = renderProductPage(product, recommendations);
return new Response(html, {
headers: { 'Content-Type': 'text/html' },
});
},
};
14.3 Voice Commerce UI
// Alexa-like voice interface in browser
class VoiceCommerce {
private recognition: SpeechRecognition;
start() {
this.recognition = new webkitSpeechRecognition();
this.recognition.continuous = true;
this.recognition.interimResults = true;
this.recognition.onresult = (event) => {
const transcript = event.results[event.results.length - 1][0].transcript;
this.processVoiceCommand(transcript);
};
this.recognition.start();
}
private async processVoiceCommand(command: string) {
// "Add AirPods to my cart"
if (command.includes('add') && command.includes('cart')) {
const product = await extractProductFromCommand(command);
await cart.add(product.id);
speak(`Added ${product.name} to your cart`);
}
// "Search for wireless headphones"
if (command.includes('search')) {
const query = command.replace('search for', '').trim();
router.push(`/s?k=${encodeURIComponent(query)}`);
}
// "What's the price of this?"
if (command.includes('price')) {
const currentProduct = getCurrentProduct();
speak(`The price is $${currentProduct.price}`);
}
}
}
14.4 AR Product Visualization
// WebXR for product visualization
async function startARVisualization(product: Product) {
if (!navigator.xr) {
showToast('AR not supported on this device');
return;
}
const session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['hit-test', 'dom-overlay'],
domOverlay: { root: document.getElementById('ar-overlay') },
});
// Load 3D model
const model = await loadGLTF(product.arModelUrl);
// Render in AR
session.requestAnimationFrame(function render(time, frame) {
const hitTestSource = frame.getHitTestResults(hitTestSourceRequest);
if (hitTestSource.length > 0) {
const pose = hitTestSource[0].getPose(referenceSpace);
model.position.set(pose.transform.position);
renderer.render(scene, camera);
}
session.requestAnimationFrame(render);
});
}
Conclusion
Amazon's frontend architecture is a testament to service-oriented design at unprecedented scale. The system serves 2.5B+ daily page views while maintaining:
- Widget-based composition: 100+ independent widgets per page, owned by different teams
- Real-time accuracy: Inventory and pricing updates within seconds
- Hyper-personalization: Every element potentially user-specific
- Multi-marketplace support: 20+ countries with localized UX
- Performance at scale: <2s page loads on constrained networks
- Experimentation velocity: 50,000+ concurrent A/B tests
- AI integration: Rufus assistant, product summaries, smart search
- Continuous deployment: 200,000+ deploys per year
Key engineering principles:
- Two-pizza teams: Small teams own end-to-end features
- Service ownership: You build it, you run it
- Customer obsession: Every millisecond matters for conversion
- Bias for action: Deploy fast, measure everything, iterate
- Data-driven decisions: A/B test every change
The future points toward server components, edge-native rendering, voice commerce, and AR visualization—but the foundational principle remains: reduce friction between customer intent and purchase completion.
Amazon's frontend isn't just displaying products. It's engineering a frictionless path from discovery to delivery—and that's the result of 30 years of relentless customer-focused optimization.
Engineering is about removing friction. Amazon's frontend architecture is a masterclass in making buying feel effortless—even when orchestrating 100+ services behind every click.
What did you think?