Skip to content
expressjs/express (opens in a new tab)

A mature, well-tested framework with clear request handling and solid CI practices, but lacks architectural documentation and strict dependency pinning for agents working at scale.

JavaScript★ 69,217208 open issuesMIT License
Overall score: 68 out of 100
Slipping

last scored 2026-06-21 · both lenses

Readiness lenses

The same repo, two ways: how ready it is for an AI coding agent, and for a human newcomer. Switch lenses to see each one's score and checks.

How ready the repo is for an AI coding agent to read, build, and operate it.

68 / 100

21 of 39 checks passed across 6 categories.

  • Machine Orientation

    75

    README and package.json describe the framework clearly, with explicit build/test commands; entry point and library structure are obvious, but no dedicated AGENTS.md or CLAUDE.md, and architecture lacks high-level documentation of internal modules and request flow.

    The README provides a quick-start code example, explicit test command (`npm test`), installation steps, and feature summary. The package.json lists all scripts (`test`, `lint`, `lint:fix`) and pinpoints the Node 18+ requirement. The entry point (index.js → lib/express.js) is straightforward, and the lib/ structure (application.js, request.js, response.js, view.js, utils.js) is navigable. However, there is no AGENTS.md, CLAUDE.md, or architecture document explaining module boundaries, middleware flow, or how the Router integrates—an AI agent must infer these from source code inspection alone.

  • Reproducible Setup

    50

    Partial setup reproducibility: Node.js pinned to a minimum floor but no lockfile, no container, and floating caret ranges across all dependencies leave version resolution to npm negotiation.

    The repository specifies `engines.node >= 18` in package.json and includes explicit npm scripts (test, lint) documented in the README. However, all 23 dependencies use caret-range pins (^2.0.0, ^4.4.0, etc.) rather than exact versions, and .npmrc disables package-lock generation (package-lock=false), so the agent must resolve transitive dependencies at install time. No container, devcontainer, or npm-shrinkwrap.json provides a locked state, and no .env.example documents environment variables. This forces npm to pick minor/patch versions dynamically, creating reproducibility drift across runs.

  • Code Navigability

    72

    Clear module structure with explicit request/response/application boundaries, descriptive function names, and JSDoc; minor gaps in TypeScript definitions and architectural documentation prevent exemplary status.

    Express uses a conventional, well-established layout with lib/ housing core modules (application.js, request.js, response.js, utils.js, view.js), examples/ for runnable patterns, and test/ with comprehensive coverage organized by feature. Module boundaries are explicit: request and response are prototypal extensions of Node's http classes; application.js handles server setup and routing. Function signatures are descriptive (e.g., `app.use()`, `res.send()`, `req.get()`), and JSDoc comments document intent and parameters. The test suite (213 files detected, test/ organization by concern) and examples/ (20+ runnable scenarios) are extensive and navigable. ESLint configuration enforces consistent style. However, no TypeScript definitions (.d.ts) exist, no explicit architecture document explains the full composition pattern, and lib/ files lack JSDoc-style @private/@public markers in some utility functions, creating minor friction for agents unfamiliar with Node.js prototypal patterns.

  • Verification Surface

    72

    Tests run reliably via mocha with broad CI coverage; linting is strict and documented; lacks type-checking and has minor CI observability gaps.

    Express has a comprehensive test suite (100+ test files across unit, integration, and acceptance tests) invoked via `npm test` using mocha with leak detection (`--check-leaks`), concrete fixtures, and supertest for HTTP assertions. The CI pipeline (ci.yml) runs tests across 9 Node versions and 2 OSes with coverage collection via nyc and Coveralls integration. ESLint is configured with strict rules (eqeqeq, no-unused-vars, proper indentation) and runs in CI as a separate lint job. However, the repository lacks TypeScript or type-checking (no JSDoc, no tsc), and test coverage thresholds are not enforced or visible in the CI config.

  • Agent Safety

    72

    Good secret hygiene and CI oversight, but missing formal safety documentation and destructive-operation boundaries.

    The repository demonstrates solid agent-safety fundamentals: .gitignore properly excludes node_modules, package-lock.json, and .log files (no .env committed); CI pipelines run linting and tests with restricted permissions; no hardcoded secrets detected in scanned files. However, the repo lacks a SECURITY.md file to formalize vulnerability reporting, has no CODEOWNERS to gate sensitive code changes, and lacks explicit boundaries around destructive operations (e.g., app.listen, file deletions via res.download, or child process spawning).

  • Context Economy

    72

    Clear, navigable codebase with committed build and vendor artifacts properly excluded, though example and changelog bulk is untagged and adds moderate noise to the signal path.

    Express excludes node_modules, package-lock.json, and build output via .gitignore (confirmed: `node_modules`, `coverage`, `.nyc_output` explicitly listed). The core library is compact (lib/ ~1400 LOC across 6 key files), tests are well-organized (213 files total but split by concern), and CI/linting configuration is standard and minimal. However, 27 example directories with templates, CSS, and fixture files, plus a 1000+ line History.md changelog, are committed without Linguist markers, forcing agents to parse full directory trees to identify signal from noise — examples would benefit from `.gitattributes linguist-documentation=true` or a separate docs branch.

  • Machine Orientation

    5 checks passed4 problems found

    • mediumAgent instructions file

      AGENTS.md / CLAUDE.md is the direct, machine-read channel for entry points and conventions.

      Fix: Add an AGENTS.md naming the entry points, the build/test commands, and the house conventions.

    • lowllms.txt present

      An llms.txt is the emerging machine-orientation standard — a curated map for LLMs.

      Fix: Add an llms.txt pointing at the key docs and entry points.

    • lowArchitecture doc

      An ARCHITECTURE doc hands an agent the module map instead of making it infer one.

      Fix: Add an ARCHITECTURE.md describing the major components and how they fit.

    • lowContribution conventions are written down

      A contributor guide is where an agent learns the branch, commit, and review conventions it must follow.

      Fix: Add a CONTRIBUTING.md stating the branch naming, commit format, and how a change gets reviewed.

    • README present — An agent reconstructs what the repo is and how to work in it from the README first.
    • Repository description set — The one-line description is the fastest orientation signal an agent reads.
    • Topics set — GitHub topics situate the repo's domain and stack for an agent at a glance.
    • README shows usage — A usage/example section tells an agent how the project is actually invoked.
    • Examples directory — An examples/ directory gives an agent runnable, copyable references for the API.
  • Reproducible Setup

    2 checks passed5 problems found

    • highLockfile committed

      Without a lockfile two clones can resolve different dependency trees — the build isn't reproducible.

      Fix: Commit your lockfile (package-lock.json / pnpm-lock.yaml / Cargo.lock / …).

    • mediumContainer or dev-container

      A Dockerfile / compose / devcontainer removes 'works on my machine' from setup.

      Fix: Add a Dockerfile, a compose file, or a .devcontainer so the environment is declared, not assumed.

    • lowVersion pins are exact

      A floating pin (a bare major, or a range) still drifts between installs.

      Fix: Pin to an exact or minor version, not a floating range.

    • lowEnv template present

      A .env.example tells an agent which configuration keys it must supply to run.

      Fix: Ship a .env.example listing every required key with placeholder values.

    • lowDev container

      A .devcontainer gives an agent a one-command, fully-declared environment.

      Fix: Add a .devcontainer so the toolchain is declared, not assumed.

    • Runtime / toolchain pinned — An unpinned runtime makes an agent guess the version a clean environment needs.
    • README documents setup — Explicit install/setup steps are what let an agent go from clone to running.
  • Code Navigability

    2 checks passed2 problems found

    • lowFormatter configured

      A formatter config (Prettier/Biome/rustfmt/…) keeps style uniform across the tree.

      Fix: Add a formatter (Prettier, Biome, rustfmt, …) and commit its config.

    • lowTyped public API

      A declared `types` entry gives an agent typed signatures to anchor an edit on.

      Fix: Ship type declarations and point package.json `types`/`typings` at them.

    • Conventional source layout — A recognised src/ · lib/ · app/ layout lets an agent localise code without spelunking.
    • .editorconfig present — An .editorconfig keeps whitespace and encoding consistent, so diffs stay legible.
  • Verification Surface

    4 checks passed3 problems found

    • lowTask runner present

      A Makefile / Taskfile / justfile gives an agent a single index of the project's commands.

      Fix: Add a Makefile / Taskfile / justfile collecting the common commands.

    • lowTypecheck command

      A typecheck script lets an agent catch type errors before they ship.

      Fix: Add a `typecheck` script (e.g. `tsc --noEmit`).

    • lowBuild command

      A documented build command is the discoverable way for an agent to produce artifacts.

      Fix: Add a `build` script that produces the distributable.

    • Tests present — Runnable tests are the ground truth an agent checks its own edits against.
    • Test command defined — A `test` script is the documented, discoverable way to run the suite.
    • Lint command defined — A linter an agent can run catches a class of mistakes before they ship.
    • CI configured — Readable CI is the feedback loop an agent reads to know what 'green' means here.
  • Agent Safety

    5 checks passed3 problems found

    • medium.gitignore covers env files

      An ignore rule for .env is what keeps the next careless commit from leaking secrets.

      Fix: Add `.env` (and `.env.*`) to .gitignore.

    • mediumSecurity policy

      A SECURITY.md is where an autonomous security agent reads this repo's rules before acting on it.

      Fix: Add a SECURITY.md stating the disclosure channel and the repo's security rules.

    • lowCODEOWNERS present

      CODEOWNERS marks the 'don't touch without review' zones an agent should respect.

      Fix: Add a CODEOWNERS file marking the sensitive paths.

    • No committed .env — A tracked real .env hands secrets to anything — including an agent — that reads the repo.
    • No exposed secrets — Secret-shaped literals in tracked files are a live credential leak.
    • Automated dependency updates — Dependabot / Renovate keeps dependencies patched without a human in the loop.
    • .gitignore present — A .gitignore is the first guard against an agent committing secrets or build junk.
    • License makes the code reusable — Without a license an agent cannot safely copy, redistribute, or vendor any of this code.
  • Context Economy

    3 checks passed1 problem found

    • lowGenerated / vendored files marked

      A .gitattributes linguist marker tells tools (and agents) what to skip.

      Fix: Mark generated/vendored paths in .gitattributes (linguist-generated / linguist-vendored).

    • Dependencies not vendored in — A committed node_modules / vendor tree drowns the real signal an agent needs.
    • Build output not committed — Generated dist/ · build/ output is noise an agent has to read past.
    • No oversized source file — A single file in the hundreds of KB blows past an agent's effective context window.

Where the grip slips

  1. highReproducible Setup+6 pts if fixed

    No package-lock.json or npm-shrinkwrap.json

    The .npmrc explicitly sets package-lock=false, disabling lockfile generation. Without a committed lockfile, npm install resolves transitive dependencies nondeterministically, violating reproducibility for an agent.

    Fix: Remove package-lock=false from .npmrc, generate and commit package-lock.json (or use npm-shrinkwrap.json), and verify it captures all nested dependency versions.

  2. mediumVerification Surface+3 pts if fixed

    No type-checking or static type enforcement

    The codebase is plain JavaScript with no TypeScript, JSDoc with type annotations, or type-checker (tsc, flow) integrated into the verification loop. An agent cannot verify type correctness without manual inspection.

    Fix: Add TypeScript strict mode or comprehensive JSDoc with type checking via `tsc --allowJs --checkJs`, and run it in CI.

  3. highReproducible Setup+2 pts if fixed

    Floating caret-range dependencies

    All 23 dependencies use caret ranges (^X.Y.Z) allowing minor and patch upgrades. Combined with no lockfile, this permits breaking changes in minor versions to slip into installs.

    Fix: Pin all dependencies to exact versions (remove ^) in both dependencies and devDependencies, then regenerate and commit the lockfile.

  4. highMachine Orientation+2 pts if fixed

    Missing agent-specific onboarding file (AGENTS.md or CLAUDE.md)

    No file exists to concisely document entry points, key module structure, build/test/run commands, or architectural patterns for an AI agent. The README is user-focused (installation, examples, philosophy) rather than agent-focused (code navigation, internal conventions).

    Fix: Create AGENTS.md with sections: (1) Entry Point & Core Modules (index.js → lib/express.js, lib/application.js, lib/router integration); (2) Build/Test Commands (npm install, npm test, npm run lint); (3) Request Flow (app.handle → router.handle → middleware stack); (4) Key Abstractions (app, req/res prototypes, Router).

  5. mediumReproducible Setup+1 pts if fixed

    No container or devcontainer setup

    No Dockerfile, docker-compose.yml, or .devcontainer/devcontainer.json exists. An agent cannot guarantee a consistent OS, Node.js build toolchain, or system library state across different host environments.

    Fix: Add a Dockerfile specifying Node.js 18+ base image and a .devcontainer/devcontainer.json for VS Code dev environments, or provide docker-compose.yml for local dev.

  6. mediumMachine Orientation+1 pts if fixed

    No architecture or module-dependency diagram

    While lib/ modules are readable in isolation, there is no overview of how application.js, request.js, response.js, view.js, and external dependencies (Router, bodyParser) interact. An agent must reverse-engineer request lifecycle from code.

    Fix: Add a brief Architecture section to AGENTS.md: 'Express creates an app function that proxies to app.handle(). The handle method sets up req/res prototypes and delegates to this.router.handle(). Middleware and route handlers are orchestrated by the Router instance, which chains handlers via next().'

  7. mediumCode Navigability+1 pts if fixed

    No TypeScript definitions

    The codebase is pure JavaScript with no .d.ts files. Agents and downstream users must infer types from runtime JSDoc and code inspection, increasing the cognitive load for understanding what to edit where.

    Fix: Generate .d.ts type definitions using tools like tsc --declaration or JSDoc-to-TypeScript converters, or author manually for core exports (application.js, request.js, response.js). Start with the public API surface.

  8. mediumCode Navigability+1 pts if fixed

    No architecture documentation

    The README and repo contain no doc explaining how request/response prototypes are composed, how app.init() sets up the lazy router, or the inheritance chain for middleware. An agent must read and infer from source code.

    Fix: Add ARCHITECTURE.md or docs/ folder explaining: (1) how createApplication() creates an Express app, (2) the prototypal inheritance model for req/res, (3) the lazy router setup, (4) flow from app.handle() to Router.handle(). Include a simple diagram.

  9. lowVerification Surface+1 pts if fixed

    No explicit code coverage threshold in CI

    The test-ci script collects coverage reports via nyc and uploads to Coveralls, but does not enforce a minimum coverage percentage in the CI config or fail the build if coverage drops. An agent cannot rely on coverage thresholds as a verification signal.

    Fix: Add `--check-coverage --lines X --functions X` flags to the nyc invocation in package.json test-ci script, or add a post-test check in CI.

  10. mediumAgent Safety+1 pts if fixed

    No SECURITY.md or formal vulnerability disclosure policy

    The README references 'Security Policies and Procedures' via a GitHub URL, but no SECURITY.md file exists in the repo itself. An agent reading the repo cannot verify this as machine-readable content and must rely on external GitHub settings.

    Fix: Create SECURITY.md with a clear vulnerability reporting workflow, scope, and contact instructions, committed to the repository root.

  11. mediumAgent Safety+1 pts if fixed

    No CODEOWNERS file for destructive or sensitive code paths

    Express handles HTTP servers, file serving (res.sendFile, res.download), cookie/session management, and middleware composition—all agent-accessible and potentially destructive. No CODEOWNERS file exists to gate changes to critical paths like lib/application.js or lib/response.js.

    Fix: Add CODEOWNERS file assigning lib/application.js, lib/response.js, lib/request.js, and cookie/session logic to required reviewers.

  12. mediumContext Economy+1 pts if fixed

    Examples and fixtures not marked as documentation/non-source

    The `examples/` directory contains 27 sample apps with templates (EJS, HBS), static files, CSS, and fixture data, plus `test/fixtures/` with template files and test data. These are all committed at repository root depth without `.gitattributes` linguist markers, forcing an agent to treat them as primary signal when walking the tree. Similarly, History.md (1000+ lines) is unparsed changelog committed to root.

    Fix: Add `.gitattributes` file with `examples/** linguist-documentation=true`, `test/fixtures/** linguist-generated=true`, and `History.md linguist-documentation=true` to signal these are not source code.

  13. lowReproducible Setup

    No .env.example or environment documentation

    No .env.example or documented environment variables are provided. An agent does not know what configuration is required to run tests or the application.

    Fix: Create .env.example with all environment variables needed (NODE_ENV, DEBUG flags, etc.) and document them in CONTRIBUTING or a dedicated SETUP guide.

  14. lowMachine Orientation

    Test invocation details partially implicit

    README says 'npm test', and package.json shows the mocha command with test/support/env require and acceptance tests, but there is no documented convention for where to place new tests or how to run specific test suites.

    Fix: Add to AGENTS.md: 'Tests live in test/ and test/acceptance/. Run all: npm test. Test organization: test/app.*.js for app methods, test/acceptance/ for integration examples. Use mocha --grep to run subsets.'

  15. lowCode Navigability

    Sparse JSDoc in lib/utils.js utility functions

    Helper functions like compileETag, compileQueryParser, compileTrust, and createETagGenerator lack @param/@return JSDoc annotations. Agents must read the full function body to understand inputs and outputs.

    Fix: Add JSDoc headers to all exported and internal functions in lib/utils.js, e.g., '@param {string|Function} val', '@return {Function}', '@private' for internal helpers.

  16. lowCode Navigability

    Large core files approach context limits

    lib/application.js, lib/request.js, and lib/response.js are long files (>1000 lines likely for application.js and response.js); a single change site may require an agent to hold the entire file in context.

    Fix: Consider splitting lib/application.js into smaller modules (e.g., lib/app/init.js, lib/app/routing.js) if editing becomes a bottleneck. Not urgent for current size, but worth monitoring.

  17. lowVerification Surface

    No machine-readable verification summary in CI output

    CI produces TAP and coverage artifacts but does not post test results (pass/fail counts, flakiness) as a comment or artifact accessible to an agent reading the workflow.

    Fix: Add a GitHub action (e.g., dorny/test-reporter) to parse test results and post them as a workflow artifact or PR comment.

  18. mediumAgent Safety

    Destructive operations (file I/O, process termination) lack agent-readable safety boundaries

    Methods like res.sendFile, res.download, and app.listen are callable by agent without warnings. No JSDoc guards, runtime assertions, or explicit 'dangerous—requires review' markers exist to signal destructive intent to an LLM reading the code.

    Fix: Add JSDoc tags like @dangerous or @requires-manual-review to lib/response.js and lib/application.js methods that perform I/O or network binding; document these in a SAFETY.md guide for agents.

  19. lowAgent Safety

    No .env.example template or documented secret keys

    While no .env is committed, the repo provides no .env.example to guide agents on what environment variables are expected or how to safely configure sensitive settings (e.g., session secrets, proxy trust rules, view caching).

    Fix: Create .env.example documenting all environment variables used in examples/ and lib/, with safe default placeholders.

  20. lowContext Economy

    No .npmrc constraints or explicit dependency pinning strategy

    package.json uses floating semver ranges for all dependencies (e.g., `accepts@^2.0.0`, `debug@^4.4.0`), and .npmrc has no `save-exact=true` or lockfile requirement strategy. This is normal for a library, but agents cannot infer dependency stability or reproducible builds from the repo alone without a lock file (package-lock.json is .gitignored).

    Fix: Either commit package-lock.json to ensure reproducibility, or add a note in README explaining the library's semver strategy and why devs should `npm ci` instead of `npm install`. Alternatively, document in a CONTRIBUTING guide.

  21. lowContext Economy

    No explicit marker of which files are published to npm

    `package.json` has a `files` field listing `[LICENSE, Readme.md, index.js, lib/]`, which is correct and visible, but this is not documented in the README or a .npmignore. An agent must parse package.json to know examples, tests, and CI are not distributed.

    Fix: Add a line to README explaining what is published ('Only `lib/` and `index.js` are distributed via npm; examples and tests are development-only') or create .npmignore as a visual signal.

Show your score

Latch badge
[![Latch score](https://latch.fyi/api/badge/3dfd05b3-b33e-4de4-a089-e5bdeb80c279)](https://latch.fyi/expressjs/express)

Share this analysis

https://latch.fyi/expressjs/express

Fixed some issues? Re-analyze to measure your progress →

Copy the whole report

Grab the entire verdict in one go — a Markdown block for a README or an issue, plain text, or a prompt you can paste straight into an LLM.

Was this verdict fair?

One tap helps — or tell us more.