One user action often triggers several of your own backend services calling each other behind the scenes.
Why it exists
Once a product is more than one service, those services must call each other in a defined way, the same discipline an external API needs, but inside your own walls. Letting services reach into each other's data directly creates a tangle nobody can change safely. Internal APIs exist so services talk through clear contracts, so teams can build and deploy their piece independently.
How it actually works
Press "Place order" and you make one visible request. But that order service can't do the job alone: it calls the inventory service (in stock?), the payment service (charge the card), the pricing service (any discount?), and hands the notification service a message (send the email). One click, one visible call, several invisible internal ones.
Understanding this explains a lot: "checkout is slow because the inventory service is slow," "we can't ship that until pricing supports it," "payments are down so orders are failing." A feature is a chain of services, and a weak link breaks the chain.
Subtopics
A senior PM walks you through it
A junior PM, stuck
Checkout latency doubled overnight and the backend lead told me in standup that "inventory is our slow downstream." I could not picture which service calls which, or why the engineers keep saying one Place order tap turns into five requests. I need to follow this well enough to report where the time is going.
One tap really does become several calls, and there is a real artifact that shows every one of them: a trace. When the order service handles a request it records each service it calls, with how long each took, in order. I pulled the trace for one Place order from Friday's lunch peak and trimmed it so we can read every hop.
Trace of one Place order tap, POST /api/orders, Friday 2026-03-06 12:41
what the customer's tap sends
112:41:07.010 order-service POST /api/orders 201 2210ms
what the order service calls behind that one request
112:41:07.020 order-service -> inventory-service GET /stock/p_1042 201 2100ms
212:41:07.030 order-service -> payment-service POST /charge 200 84ms
312:41:07.035 order-service -> pricing-service GET /price/p_1042 200 22ms
Reading "checkout is slow" as "the checkout team has a bug." The slow hop was inventory; the checkout code was fine. A trace points you at the right team before you assign the ticket.
Scoping a feature as one service's work when it touches a chain. If the change needs pricing and inventory too, "just one service" was the wrong estimate from the start.
Forgetting a shared downstream is a shared risk. If two unrelated features both call inventory, inventory going down breaks both at once; that is one incident, not two coincidences.
Assuming every internal call makes the customer wait. The notifications handoff returned in 6ms and no one waited on the email; treating fire-and-forget work as if it slows checkout sends you optimizing the wrong hop.
Reply in the thread: "The trace shows one order tap fans out to inventory, payment, pricing, and notifications; inventory took 2100ms of the 2210ms while the rest answered in under 100ms, so checkout is slow only because it waits on inventory. Can the inventory team look at that call?" You read one request as a chain of services and named the weak link precisely.
Where a PM meets this
"Why did an unrelated feature break?" often because they share a downstream service that went down.
When scoping, "which services does this touch?" predicts complexity: one service is simple, five talking to each other is not.
Hear it in a meeting
"Checkout depends on four services; the slow one is inventory."