ScaledByDesign/Insights
ServicesPricingAboutContact
Book a Call
Scaled By Design

Fractional CTO + execution partner for revenue-critical systems.

Company

  • About
  • Services
  • Contact

Resources

  • Insights
  • Pricing
  • FAQ

Legal

  • Privacy Policy
  • Terms of Service

© 2026 ScaledByDesign. All rights reserved.

contact@scaledbydesign.com

On This Page

The Flash Sale That Broke EverythingWhat "Inventory" Actually Means (It's Not One Number)The One Thing You Cannot Get WrongMulti-Channel Sync: Where Everyone Lies to ThemselvesThe Reorder Problem Nobody Solves WellWhat Doesn't Work (And We've Tried)Cycle Counting: The Boring Thing That Saves YouThe Spreadsheet Got You Here. It Won't Get You There.
  1. Insights
  2. Growth Ops
  3. Inventory Management Systems That Scale — From Spreadsheets to Real-Time

Inventory Management Systems That Scale — From Spreadsheets to Real-Time

June 29, 2026·ScaledByDesign·
inventoryecommercewarehouseoperationsscaling

The Flash Sale That Broke Everything

A DTC brand called us on a Monday morning in full panic mode. They'd run a flash sale over the weekend — 40% off their best seller. It went great. Too great. They oversold 340 units of a product they had 200 of.

The fallout: $12,000 in refunds, 340 angry customers, a spike in 1-star reviews on Amazon, and a Shopify trust score that took three months to recover. All because their inventory system was a Google Sheet that someone updated every morning at 9 AM.

We see this exact story at least once a month. The details change — different brand, different channel, different product — but the root cause is always the same: inventory management that worked at $500K/year breaks catastrophically at $5M/year.

What "Inventory" Actually Means (It's Not One Number)

This is the first thing we fix with every client. They think inventory is a single number: "we have 200 units." It's not. It's five numbers, and confusing them is how you oversell:

On Hand:    200  ← Physically sitting in your warehouse
Reserved:    45  ← Allocated to orders not yet shipped
Committed:   12  ← Currently being picked/packed
In Transit:  80  ← On a truck from your supplier
Available:  143  ← What you can actually sell (200 - 45 - 12)

Every brand we audit is tracking On Hand and calling it Available. That's a 28% error in this example. During a sale, when Reserved spikes, that error becomes catastrophic.

The One Thing You Cannot Get Wrong

Atomic reservation. This is the single most important piece of code in your entire e-commerce stack, and most teams get it wrong. Here's why: two customers click "Buy" at the same time for the last unit. Without proper locking, both orders succeed. One customer gets a refund and a bad experience.

async function reserveInventory(
  sku: string,
  quantity: number,
  orderId: string,
  locationId: string
): Promise<ReservationResult> {
  return await db.$transaction(async (tx) => {
    // FOR UPDATE locks this row — nobody else can read it
    // until we're done. This is the whole trick.
    const inventory = await tx.$queryRaw<InventoryRecord[]>`
      SELECT * FROM inventory
      WHERE sku = ${sku} AND location_id = ${locationId}
      FOR UPDATE
    `;
 
    if (!inventory[0]) {
      return { success: false, reason: "SKU not found" };
    }
 
    const available = inventory[0].on_hand
      - inventory[0].reserved
      - inventory[0].committed;
 
    if (available < quantity) {
      return {
        success: false,
        reason: "Insufficient stock",
        available,
        requested: quantity,
      };
    }
 
    // Reserve it
    await tx.inventory.update({
      where: { sku_locationId: { sku, locationId } },
      data: { reserved: { increment: quantity } },
    });
 
    // Track the reservation (with an expiry — more on this below)
    await tx.reservations.create({
      data: {
        sku, orderId, quantity, locationId,
        status: "active",
        expiresAt: addMinutes(new Date(), 30)
      },
    });
 
    return { success: true };
  });
}

FOR UPDATE is doing all the heavy lifting here. It's a database-level lock that prevents two transactions from reading the same row simultaneously. Without it, you're relying on application-level checks that will fail under concurrent load. We've seen it happen on every platform — Shopify, custom builds, headless setups. If your reservation isn't atomic at the database level, it's broken. You just haven't hit enough traffic to notice yet.

Multi-Channel Sync: Where Everyone Lies to Themselves

Here's a conversation we have with every multi-channel brand:

Us: "How do you sync inventory between Shopify and Amazon?" Them: "We use [app name], it syncs every 15 minutes." Us: "What happens when you sell 10 units on Shopify in those 15 minutes?" Them: "..."

Fifteen-minute sync intervals are fine at low volume. At 500+ orders/day across channels, you're guaranteed to oversell. And Amazon doesn't forgive overselling — they'll suppress your listings.

Here's how we build it:

async function syncChannels(sku: string) {
  const inventory = await getAggregatedInventory(sku);
 
  // Channel-specific buffers — this is where experience matters
  const channels = [
    { name: "shopify", buffer: 0.9 },   // 10% safety buffer
    { name: "amazon", buffer: 0.85 },    // Amazon penalizes hard — be conservative
    { name: "wholesale", buffer: 0.7 },  // Reserve 30% for higher-margin DTC
  ];
 
  await Promise.all(channels.map(async (channel) => {
    const channelAvailable = Math.floor(
      inventory.available * channel.buffer
    );
    await updateChannelInventory(channel.name, sku, channelAvailable);
  }));
}
 
// Event-driven, not cron-driven. Every inventory change triggers a sync.
eventBus.on("inventory.changed", async (event) => {
  await syncChannels(event.sku);
});

Two things to notice:

  1. Event-driven sync, not scheduled. Every inventory change triggers an immediate sync. No 15-minute gaps.
  2. Channel buffers are different. Amazon gets 85% of available because the penalty for overselling is brutal. Wholesale gets 70% because you want to protect DTC margin. These numbers aren't arbitrary — we've tuned them across dozens of brands.

The Reorder Problem Nobody Solves Well

Every brand we work with has the same story: "We ran out of our best seller for 3 weeks because nobody noticed inventory was low." Then they overcorrect and tie up $200K in excess inventory that sits in a warehouse for 6 months.

Automated reordering isn't glamorous, but it's the difference between brands that scale smoothly and brands that lurch from stockout to overstock:

async function checkReorderPoints() {
  const lowStockItems = await db.inventory.findMany({
    where: {
      available: { lte: db.raw("reorder_point") },
    },
    include: { supplier: true },
  });
 
  // Group by supplier — one PO per supplier, not per SKU
  const bySupplier = groupBy(lowStockItems, "supplier.id");
 
  for (const [supplierId, items] of Object.entries(bySupplier)) {
    await createPurchaseOrder({
      supplierId,
      items: items.map(item => ({
        sku: item.sku,
        quantity: item.reorderQuantity,
        currentStock: item.available,
      })),
    });
 
    await notifications.send({
      channel: "procurement",
      message: `Auto PO for ${items.length} SKUs from ${items[0].supplier.name}`,
    });
  }
}

The reorder point calculation is where most teams phone it in. They set a static number — "reorder when we hit 50 units" — and never update it. That's fine for stable products. For anything seasonal or trending, you need dynamic reorder points based on velocity. A product selling 20/day needs a very different reorder point than one selling 2/day, especially when your supplier has a 3-week lead time.

What Doesn't Work (And We've Tried)

We've implemented inventory systems on every major platform. Here's what we've learned the hard way:

ApproachSounds GoodReality
Shopify's built-in inventory"It's already there!"✗ No reservation locking, no multi-warehouse, breaks at scale
Third-party sync apps"Just install an app!"✗ 15-min sync gaps, no atomic operations, black box when it breaks
Building everything custom"We'll own it!"✗ 6-month project that's never "done" — overkill for most brands
ERP systems (NetSuite, etc.)"Enterprise-grade!"✗ $100K+ implementation, 9-month timeline, designed for manufacturing not DTC

The sweet spot for most DTC brands doing $5M–$50M? A lightweight custom inventory service that handles reservation and sync, connected to your existing Shopify/platform via webhooks. It's a 4-6 week build, not a 6-month ERP implementation.

Cycle Counting: The Boring Thing That Saves You

Your system count will drift from physical reality. It always does. Warehouse staff makes mistakes, returns get miscounted, shrinkage happens. The question isn't whether your counts are wrong — it's how wrong and how fast you catch it.

Full physical counts are expensive and disruptive. Cycle counting is the answer:

A items (top 20% by revenue):  Count weekly
B items (next 30%):            Count monthly
C items (bottom 50%):          Count quarterly

Discrepancy > 5%? → Investigation required
Discrepancy > 10%? → Stop selling that SKU until resolved

We had a client discover through cycle counting that a warehouse employee was stealing high-value items — the system showed 50 units, the shelf had 38. Without regular counts, they'd have found out when a customer ordered something that wasn't there.

The Spreadsheet Got You Here. It Won't Get You There.

Every successful DTC brand started with a spreadsheet. There's no shame in it. But the spreadsheet that got you to $1M in revenue will actively prevent you from reaching $10M. The overselling, the stockouts, the manual sync — these aren't inconveniences at scale. They're revenue killers.

Get the reservation logic right. Make it atomic. Sync channels in real-time, not on a timer. Automate reordering before you run out, not after. And count your inventory regularly, because the system is always lying to you a little bit.

The spreadsheet got you to $1M. Proper inventory systems get you to $100M. The brands that figure this out early spend their time growing. The ones that don't spend their time apologizing to customers.

Previous
Managing Up as an Engineering Leader — What Your CEO Actually Needs from You
Insights
Inventory Management Systems That Scale — From Spreadsheets to Real-TimeSubscription Commerce Technical Architecture — Beyond Recurring BillingCheckout Funnel Optimization — The Technical Fixes That Recover RevenueLoyalty Program Technical Architecture — Points, Tiers, and the Math Behind RetentionThe Shopify Plus Migration That Saved $400K/YearProduct Page Conversion Engineering — The Technical Optimizations That Move the NeedleThe Headless Commerce Migration Playbook — From Monolith to ComposableThe Subscription Box Tech Stack That Scales Past $10M ARRThe Post-Purchase Email Sequence That Drives 40% Repeat RevenueYour Post-Purchase Experience Is Leaving $2M on the TableYour Attribution Is Lying to You — Here's How to Fix ItThe DTC Tech Stack That Actually Scales Past $10MSubscription Churn Is a Systems Problem, Not a Marketing ProblemLifecycle Automation That CompoundsThe Checkout Optimization Playbook That Added $2M in RevenueWhy Your Shopify Store Breaks During Every SaleWhy Your Loyalty Program Isn't Working (And What to Build Instead)COGS Reporting Shouldn't Take 5 DaysHeadless Commerce: When It's Worth It and When It's a TrapThe Inventory Forecasting System That Stopped Our Client From OversellingPayment Processing Architecture for High-Volume Merchants

Ready to Ship?

Let's talk about your engineering challenges and how we can help.

Book a Call