Decomposition — Example: Package Delivery Logistics

The Starting Point

Goal: Build a system for a delivery company that picks up packages from senders, routes them through sorting facilities, and delivers them to recipients. Track every package at every step. Handle failed deliveries, re-routing, and returns.

This system is different from the previous examples because it spans physical space and physical time. A package moves through multiple locations over multiple days. The decomposition must account for geography, vehicle routing, and the hard reality that physical objects can be lost.


Step 1: Top-Down — First Level

Ask: "What does a package delivery system need?"

Package Delivery
├── Package Intake (getting packages into the system)
├── Routing (deciding how packages travel)
├── Sorting & Transfer (physical movement through facilities)
├── Last-Mile Delivery (getting packages to recipients)
├── Tracking (visibility into package location)
└── Exception Handling (when things go wrong)

Six branches. But already, this decomposition reveals something: "Exception Handling" is its own branch. In the bookstore and messaging app, error handling was part of each leaf. Here, exceptions are so common and varied that they form a top-level concern.

Why? Because physical systems fail differently than digital ones. A database transaction either commits or rolls back. A package can be partially delivered (wrong address, left with neighbor, returned to sender). The failure modes create their own subsystem.


Step 2: Second Level — Each Branch

Package Intake

Package Intake
├── Customer creates shipment request
│   ├── Enter sender address
│   ├── Enter recipient address
│   ├── Enter package details (weight, dimensions, contents description)
│   ├── Select service level (overnight, 2-day, ground, economy)
│   └── Validate addresses (real addresses? deliverable? restricted areas?)
├── Calculate price (based on weight, dimensions, distance, service level)
├── Generate shipping label (with barcode/QR code and tracking number)
├── Schedule pickup (or drop at a facility)
└── Record package in system (status: "label created")

Routing

Routing
├── Determine route plan
│   ├── Origin facility (nearest sorting center to sender)
│   ├── Destination facility (nearest sorting center to recipient)
│   ├── Intermediate hops (if origin and destination aren't connected directly)
│   └── Transportation mode for each leg (truck, air, rail)
├── Optimize route for service level
│   ├── Overnight → air route, priority loading
│   ├── Ground → truck route, cost-optimized
│   └── Economy → most cost-efficient, may wait for full truck
└── Update route (if re-routing is needed due to weather, capacity, etc.)

Sorting & Transfer

Sorting & Transfer
├── Package arrives at facility (scan barcode → update status: "at [facility]")
├── Sort package to outbound lane (based on route's next hop)
├── Load package onto vehicle (scan → update status: "in transit to [next facility]")
├── Transfer between vehicles (for multi-leg routes)
└── Package arrives at destination facility (scan → update status: "at destination facility")

Last-Mile Delivery

Last-Mile Delivery
├── Assign package to delivery route (group packages by neighborhood)
├── Load delivery vehicle (scan each package)
├── Attempt delivery
│   ├── Successful delivery (scan → status: "delivered", capture signature or photo)
│   ├── No one home → leave at door (if authorized) or leave notice
│   ├── Wrong address → return to facility, flag for investigation
│   ├── Refused by recipient → return to facility
│   └── Access issue (gated community, locked building) → leave notice, reschedule
└── End of day: reconcile (all packages either delivered or accounted for)

Tracking

Tracking
├── Generate tracking number (at intake)
├── Record scan event (every scan at every point creates a tracking update)
├── Calculate estimated delivery date (based on route + service level)
├── Update estimated delivery date (if delays occur)
├── Provide tracking timeline to customer (ordered list of events)
└── Send proactive notifications
    ├── "Package picked up"
    ├── "Package in transit"
    ├── "Out for delivery"
    ├── "Delivered" (with photo/signature)
    └── "Delivery attempted — notice left"

Exception Handling

Exception Handling
├── Failed delivery (no one home, refused, wrong address)
│   ├── Reschedule delivery attempt
│   ├── Hold at facility for customer pickup
│   └── Return to sender (after N failed attempts)
├── Damaged package
│   ├── Assess damage (at any scan point)
│   ├── Notify sender and recipient
│   ├── File insurance claim (if insured)
│   └── Decide: deliver damaged or return to sender
├── Lost package
│   ├── Detect: expected scan didn't happen within time window
│   ├── Investigate: check last known scan, vehicle manifest
│   ├── Notify customer
│   └── File claim / send replacement or refund
├── Address correction
│   ├── Recipient contacts company with corrected address
│   ├── Update route while package is in transit
│   └── If package already at destination facility, re-sort
└── Customer-initiated redirect
    ├── Hold at facility
    ├── Deliver to alternate address
    └── Return to sender (sender requests)

Step 3: Finding Seams

Seam: Physical Scan Points

Every barcode scan is a seam. Data literally changes at each scan point:

  • Before scan: "package was loaded onto truck" (assumed location)
  • After scan: "package is confirmed at Chicago facility" (known location)

The system transitions from assumed state to confirmed state at every scan. Between scans, the package's location is inferred, not known. This is fundamentally different from digital systems where data is always in a known state.

Seam: Service Level → Route Strategy

The same package going from New York to Los Angeles decomposes differently based on service level:

  • Overnight: NYC facility → JFK airport → LAX airport → LA facility → delivery
  • Ground: NYC facility → truck to Pittsburgh hub → truck to Denver hub → truck to LA facility → delivery
  • Economy: NYC facility → waits for full truck → Pittsburgh → waits → Denver → waits → LA → delivery

Same origin, same destination, completely different decomposition of the journey. The service level seam changes the entire routing tree.

Seam: Custody Changes

Every time the package changes hands (sender → pickup driver → facility worker → vehicle → next facility → delivery driver → recipient), there's a custody seam. At each custody transfer, responsibility shifts. If the package is damaged, the question is: during whose custody? This is a liability seam as much as a technical one.

Seam: Customer Expectation vs. Physical Reality

The customer sees: "In transit → Out for delivery → Delivered." The system sees: "Scan 47 → Sort lane B → Load truck 104 → Scan 48 → Driver route position 12/38 → Scan 49 → Delivery confirmed GPS 40.7128° N."

Same package, radically different levels of detail. The tracking system must translate between these two views — that's a seam between the physical world and the customer experience.


Step 4: Dependency Map

Package Intake ──────────── (entry point, no dependencies)
       │
       ├────────────────┐
       ▼                ▼
   Routing          Tracking (tracking number created at intake)
       │                │
       ▼                │
Sorting & Transfer ─────┘ (each scan updates tracking)
       │
       ▼
Last-Mile Delivery ──── needs sorted packages + route assignments
       │
       ├────────────────┐
       ▼                ▼
  Tracking           Exception Handling
  (delivery scan)    (failed delivery triggers exception flow)

What This Map Reveals

Tracking runs in parallel with everything. It's not a step in the chain — it's a continuous side channel that records events from every other branch. Every scan in Sorting, Routing, and Delivery feeds into Tracking.

Exception Handling is triggered from multiple points. A failed delivery triggers an exception. A damaged package at a sort facility triggers a different exception. A lost package (detected by Tracking) triggers yet another. Exception Handling isn't downstream of one branch — it's connected to everything.

The physical chain is strictly sequential. A package must be: picked up → routed → sorted → transported → sorted again → delivered. You can't build or deliver out of order. This contrasts with the messaging app where sending and receiving happen simultaneously.


Step 5: The Complete Tree (Abridged)

Package Delivery
├── Package Intake
│   ├── Create shipment request (addresses, weight, dimensions, service level)
│   ├── Validate addresses
│   ├── Calculate price
│   ├── Generate label + tracking number
│   ├── Schedule pickup
│   └── Record in system
│
├── Routing
│   ├── Determine origin/destination facilities
│   ├── Plan route (hops, transport modes)
│   ├── Optimize for service level
│   └── Re-route (on delay or capacity change)
│
├── Sorting & Transfer
│   ├── Scan at arrival (per facility)
│   ├── Sort to outbound lane
│   ├── Load onto vehicle (scan)
│   └── Track vehicle movement
│
├── Last-Mile Delivery
│   ├── Create delivery routes (cluster by geography)
│   ├── Load delivery vehicle (scan each package)
│   ├── Attempt delivery (success / fail scenarios)
│   ├── Capture proof (signature, photo)
│   └── End-of-day reconciliation
│
├── Tracking
│   ├── Record scan events (from all sources)
│   ├── Calculate estimated delivery
│   ├── Update estimated delivery on delay
│   ├── Provide customer timeline
│   └── Send proactive notifications (picked up, in transit, out for delivery, delivered)
│
└── Exception Handling
    ├── Failed delivery (reschedule, hold, return)
    ├── Damaged package (assess, notify, claim)
    ├── Lost package (detect, investigate, claim)
    ├── Address correction (update route, re-sort)
    └── Customer redirect (hold, alternate address, return)

Total leaf count: 30+ operations


Comparing All Three Systems

AspectBookstoreMessaging AppPackage Delivery
Primary domainDigital commerceDigital communicationPhysical logistics
Time horizonMinutes (browsing → purchase)Milliseconds (real-time messages)Days (pickup → delivery)
Failure recoveryRefund/retry (reversible)Retry/resend (mostly reversible)Physical recovery (often irreversible)
Fan-out pattern1 order → 1 customer1 message → N recipients1 package → many scan points
Deepest complexityCheckout (sequential chain)Notification routing (multi-channel)Exception handling (branching physical outcomes)
Seams unique to this domainFormat changes (cart → order → record)Real-time vs. stored, audience routingCustody changes, physical scan points, assumed vs. confirmed state
Exception handlingPart of individual operationsPart of individual operationsIts own top-level branch
Build orderCatalog → Cart → Checkout → OrdersUsers → Conversations → Messages → Presence → NotificationsIntake → Routing → Sort → Delivery → Tracking → Exceptions

What This Example Teaches

  1. Physical systems have assumed state — between scans, you're guessing where the package is. Digital systems always know.
  2. Exception handling can be its own subsystem — when failures are common and varied, they deserve a top-level branch, not just error cases in contracts
  3. The same entity (package) decomposes differently based on context — overnight vs. ground creates entirely different route trees
  4. Custody changes are seams — every handoff between people, vehicles, or facilities is a decomposition boundary
  5. Sequential physical chains can't be parallelized — unlike software where operations can run simultaneously, a package must physically move step by step