SpikeMe
← Blog
stack

package.json as an X-Ray of the Stack

· 4 min read

In this article

Without running a single line of code, the package.json already reveals the framework, the project's era, and accumulated technical debt. How to read these signals — and what to do with them.

What Dependencies Reveal

Before opening a single code file, the package.json already tells much of a project's story. The production dependencies show the framework and the architecture decisions behind it — whether it's an SPA with a separate state management library, a full-stack app resolved within a single framework, or something assembled piecemeal, without an obvious central choice.

Versions tell the era. A major dependency several versions out of date says the project stopped keeping up with the ecosystem at some point — and usually not by accident, but because updating would have cost too much to fit what was being prioritized at the time. This isn't a judgment on whoever maintained the project; it's a data point on how much work lies ahead before touching anything that depends on that specific version.

devDependencies shows the project's discipline: is there linting, formatting, automated testing, or does the package.json only have the essentials to run? The scripts reveal the real development workflow — if build chains several tools in sequence, that's a complexity debt that isn't documented anywhere else besides this file.

None of this requires running the project. It's reading metadata, not behavior — but it already narrows the distance quite a bit between "I know nothing about this code" and "I know enough to ask the next right question."

Warning Signs

Stale majors. A central dependency of the project — the framework, the ORM, the bundler — stuck on an old major version while the rest of the ecosystem has already moved on is the most direct sign of accumulated technical debt. The bigger the gap in majors, the higher the chance the migration requires cascading changes, not just an isolated version bump.

Abandoned libraries. A dependency without a recent release, especially one that plays a structural role (authentication, routing, data fetching), is a silent risk: it keeps working until it stops working — an unpatched security issue, an incompatibility with a new version of another dependency — and at that point the decision to swap it stops being planned and becomes an emergency.

Role duplication. Two libraries solving the same problem — two HTTP clients, two state managers, two data-fetching libraries — usually isn't a deliberate choice. It's the trail of a migration that started and didn't finish, or of decisions made at different times by different people without reviewing what already existed in the project. Every duplication like this is dead weight: one more API for the team to know, with no corresponding benefit.

None of these signs, on its own, decides anything. They point to where the next investigation should begin. A hypothetical dependency snippet already makes this visible without needing to open any other file:

{
  "dependencies": {
    "react": "16.8.0",
    "redux": "^4.0.0",
    "zustand": "^4.4.0",
    "axios": "^0.21.0",
    "node-fetch": "^2.6.0"
  }
}

In this illustrative example, three signs appear together: a framework major stuck several versions behind what the ecosystem has already adopted as standard; two state management libraries coexisting in the same project, likely a migration that stopped halfway; and two different HTTP clients doing the same job. None of these points, on its own, justifies stopping everything — but together, they form a concrete list of questions to investigate, instead of a vague feeling that "the stack is outdated."

From Diagnosis to Decision

Reading the package.json is diagnosis, not decision. It points to the areas of concern; it doesn't say whether it's worth acting on them now, or what the best way to act is. That second part — deciding whether to update, swap, or live with what's there — is the kind of question best answered with a spike rather than a meeting: a short period, defined in advance, to test, in practice, what a stale major breaks when upgraded, or how a library candidate to replace the duplicate behaves once integrated into the rest of the project.

The diagnosis made from the package.json already delivers half the work of preparing that spike: it defines exactly which question to investigate — this dependency, this specific version — instead of a "let's modernize the stack" that's too generic to fit into any time-box.

This also changes the conversation within the team. "We need to modernize the stack" is a statement that's hard to agree or disagree with, because it doesn't point to anything specific enough to be tested. "The duplicated HTTP client carries unnecessary weight in the bundle, and the older of the two libraries hasn't had a release in a long time" is a verifiable statement — you can check whether it's true, decide whether it matters now or later, and turn it into a spike with a defined scope, instead of discussing the entire stack at once.

Where SpikeMe Fits In

SpikeMe starts from the same principle — the package.json as the starting point — to generate a stack's initial diagnosis. The analysis runs in the browser; the package.json isn't sent to the servers, only a summary of what was identified locally makes up the request used to generate the spike document. To try it with your project's package.json, see spikeme.io.

Share

XLinkedIn

Recommended articles

Deciding on a library or pattern for your project? Validate it with a spike.

Generate the spike for YOUR stack