Static analysis for memory

Find memory risks before they find production.

MemGuard spots JavaScript and TypeScript patterns that may cost memory, handles, and headroom.

Built to report evidence, not anxiety.

express2 findings across 141 files
How it worksThe pipeline, and what it refuses to guess

A finding, with context

Potential is not a verdict.

Every report says what it saw, why scale matters, and what to consider next. It never calls a heuristic a leak.

orders-api / memguard run / 80x24
MemGuard
────────────────────────────────────────

⚠ MEM003  Potentially unbounded async concurrency

src/order.service.js:3

  2 │     const orders = await Order.find({});
> 3 │     await Promise.all(orders.map((o) => enrich(o)));
    │           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  4 │     return orders;

Problem:
Promise.all() may start one operation per element of a collection whose size
is not bounded here

Why it matters:
Every element produces a promise before any of them settle, so peak memory
and open handles scale with the collection size. A collection of 10,000 items
becomes 10,000 simultaneous operations.

Consider:
• Process the collection in fixed-size batches, awaiting each batch
• Apply a concurrency limiter such as p-limit or p-map
• Truncate the collection with .slice() before mapping when a cap is acceptable

Severity: WARNING
────────────────────────────────────────

Signal, not noise

Evidence of scale is the threshold.

A spread by itself is ordinary code. MemGuard waits for evidence: a loop, an awaited source, or a cache without eviction.

01

Trace the source

Copies tied to async or collection work deserve a closer look.

02

Read the bounds

Limits, pagination, and eviction can turn a risky pattern into a safe one.

03

Keep CI yours

Nothing defaults to error. You choose when a finding becomes a gate.

Five focused rules.

Small surface area. Specific evidence. Useful output on the first run.

memguard run order.service.js
 MEM003  Potentially unbounded async concurrency

src/order.service.js:3

  2      const orders = await Order.find({});
> 3      await Promise.all(orders.map((o) => enrich(o)));
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  4      return orders;

Why it matters:
Every element produces a promise before any of them settle, so peak memory
and open handles scale with the collection size.

Consider:
 Process the collection in fixed-size batches, awaiting each batch
 Apply a concurrency limiter such as p-limit or p-map

For CI

Clear outcomes for automation.

Machine-readable JSON and deliberate exit codes let a pipeline distinguish code findings from tool failures.

npx memguard run --json --strict
0
Clean, or info-only findings
1
Errors, or warnings under strict mode
2
MemGuard failed to run correctly

Honest limits make better tools.

Per-file analysis. An eviction in another file may not be visible to the rule.

Names are clues. MEM005 cannot know whether a query returns ten rows or ten million.

No automatic fixes. Changing aliasing or cache behavior needs an engineering decision.

The wider plan

Static analysis is layer one.

MemGuard reads your source from the command line today. What follows runs the same analysis where the code is written, in the process while it runs, and on the pull request before it merges.

  1. Static engine

    Reads JavaScript and TypeScript source and reports the patterns that may cost memory.

    Available
  2. IDE extension

    Runs the same rules in your editor as you type, so a finding arrives while you write the line.

    Planned
  3. Runtime profile

    Streams heap growth, collection pauses, retained objects, and open handles as the process runs.

    Planned
  4. CI/CD plugin

    A GitHub Action that posts findings as review comments on the lines a pull request changed.

    Planned

Detect, explain, recommend, then protect.

Every stage has to earn the next one. None of them rewrite your code for you, because changing how memory is held changes how an application behaves.

  1. 01

    Detect

    Find the pattern in the source.

  2. 02

    Explain

    Say why the size of the collection is what costs you.

  3. 03

    Recommend

    Offer a change a person can accept or reject.

  4. 04

    Measure

    Confirm the change in a process that is actually running.

  5. 05

    Protect

    Shed load before the process runs out of memory.

  6. 06

    Automate

    Last, and only where behavior stays predictable.

Start here

Run it against your project.

Run it oncenpx memguard setup && npx memguard run
Or install itnpm i memguard

Node 22+. Zero runtime dependencies beyond TypeScript.