Your package.json Is a Liability Map.
Every dependency in your lockfile is a bet on someone else's uptime. Here's how to start thinking about your dependency tree as a risk surface.
By CheckUpstream Team
Your package.json Is a Liability Map
Open your package.json. Go ahead, I'll wait.
Count the dependencies. Not just the top-level ones. Run npm ls --all and watch your terminal scroll for a while. A mid-sized Next.js app typically has somewhere between 800 and 1,200 transitive dependencies. A Rails app with a dozen gems? Easily 200+. A Go service? Fewer, but each one tends to be more critical.
Now here's the question nobody asks during sprint planning: how many of those dependencies talk to an external service that can go down?
The dependency tree you don't see
Most developers think about dependencies as code. Libraries. Packages. Stuff that gets bundled into your build. And that's true. But some of those packages are also runtime service clients.
When you npm install stripe, you're not just adding a JavaScript library to your bundle. You're adding a hard runtime dependency on Stripe's API servers. If those servers go down, your code throws. Doesn't matter how good your test coverage is. Doesn't matter that your CI is green. The code is correct. The service behind it is not.
This is the gap in how we think about dependencies. We treat them as a supply chain problem (are they secure? are they maintained?) but not as an availability problem (what happens when the thing they connect to goes offline?).
Your package.json is both a bill of materials and a liability map. We're pretty good at the first part. We're terrible at the second.
A quick inventory
Let me walk through a typical Node.js SaaS app and point out the service dependencies hiding in plain sight.
"dependencies": {
"stripe": "^14.0.0", // Stripe API
"@aws-sdk/client-s3": "^3.500", // AWS S3
"openai": "^4.28.0", // OpenAI API
"@clerk/nextjs": "^4.29.0", // Clerk Auth
"@sentry/nextjs": "^7.100.0", // Sentry
"postmark": "^3.0.0", // Postmark Email
"@vercel/analytics": "^1.1.0", // Vercel Analytics
"ioredis": "^5.3.0", // Redis (wherever hosted)
"@prisma/client": "^5.9.0", // Database (wherever hosted)
"resend": "^3.0.0" // Resend Email API
}
That's ten dependencies. Nine of them are clients for external services. If any of those services have an outage, some part of your application breaks.
And here's what makes it tricky: you probably didn't think of @sentry/nextjs as a service dependency. Sentry going down won't break your checkout flow. But it will break your error monitoring, which means if something else goes wrong at the same time, you're flying blind.
Some dependencies are load-bearing walls. Others are smoke detectors. You need to know which is which.
The risk matrix nobody builds
In security, there's a concept called a threat model. You list your assets, identify threats, assess likelihood and impact, then prioritize defenses accordingly. We do this for security vulnerabilities. We almost never do it for availability.
Here's what a dependency risk matrix looks like:
Critical (causes revenue loss or data loss):
- Payment processor (Stripe, PayPal)
- Primary database (PlanetScale, Supabase, RDS)
- Auth provider (Clerk, Auth0)
- Core cloud infrastructure (AWS, GCP, Vercel)
High (causes feature degradation users notice):
- Email/SMS provider (Resend, Postmark, Twilio)
- AI/ML API (OpenAI, Anthropic)
- Search service (Algolia, Typesense)
- File storage (S3, Cloudflare R2)
Medium (causes internal tooling problems):
- Error tracking (Sentry, Datadog)
- Analytics (PostHog, Mixpanel)
- CI/CD (GitHub Actions, CircleCI)
- Logging (Datadog, Logtail)
Low (can wait until next business day):
- Documentation hosting
- Marketing tools
- Non-critical webhooks
Building this matrix takes about 30 minutes. It changes how you think about every npm install going forward.
The Python and Go version of this problem
This isn't a JavaScript problem. It's everywhere.
In Python, your requirements.txt or pyproject.toml has the same hidden service map. boto3 means AWS. openai means OpenAI. stripe means Stripe. sentry-sdk means Sentry. The package names are basically the same.
In Go, your go.mod is actually more honest about it because Go module paths include the domain: github.com/aws/aws-sdk-go-v2, github.com/stripe/stripe-go. You can literally see who you depend on by reading the import paths.
Rust's Cargo.toml, Ruby's Gemfile, Java's pom.xml, PHP's composer.json. Every ecosystem has the same pattern. The package manager tracks code dependencies. Nobody tracks the service dependencies those packages imply.
What changes when you see it
Once you start looking at your dependency files as liability maps, a few things shift in how you make decisions.
You start asking "what happens if this goes down?" during code review. Someone adds the Anthropic SDK to the project. Fine. But now you have a new external dependency. Is there a fallback? Is there a timeout? Is there a circuit breaker? These are questions you'd never ask about a pure utility library like lodash, but they matter a lot for service clients.
You notice concentration risk. If your auth, your database, and your file storage all run on AWS, you don't have three dependencies. You have one. A single AWS region outage takes out all three simultaneously. We saw exactly this during the us-east-1 outage in December 2021. Teams that spread their critical services across multiple providers fared much better than teams that went all-in on one cloud.
You realize that "it's not our fault" still requires a response. When Stripe goes down, your users don't care whose fault it is. They care that checkout is broken. A response plan for each critical dependency, even one as simple as "show a friendly error and retry in 2 minutes," is the difference between a minor blip and a support queue meltdown.
From liability map to monitoring
Here's the thing: you already have the information you need. Your dependency files contain the full list of services you depend on. The mapping from "this package" to "this service" to "this status page" is mechanical. It's just that nobody's been doing it.
We built CheckUpstream to close that gap. Point it at your repo, and it reads your package.json, requirements.txt, go.mod, Cargo.toml, Gemfile, .tf files, docker-compose.yml, and 50+ other manifest formats. Every package that maps to a known service gets connected to that service's real-time status. When something changes, you get an alert before your users file a ticket.
The liability map is already sitting in your repo. Start reading it.