Skip to content

Dimensions

neosloc scores ten dimensions. Each one asks a single question, and the evidence that answers it depends on what kind of surface the project offers. A CLI is asked about --json output and exit statuses, not RFC 9457 errors and pagination.

The requirement tables on this page are generated from neosloc/criteria.py whenever the docs are built, so they always match the code.

How a level is decided

  1. Surfaces are detected. A project can offer several:

    Surface Detected when
    service HTTP routes are declared, a server framework is instantiated, an MCP server exists, or a standard protocol is served (RESP, MQTT, gRPC, PostgreSQL wire, Kafka, SMTP, WebSocket: a protocol or broker library plus a listening socket).
    cli A declared entry point (console script, npm bin, Go main, Cargo binary) and argument parsing.
    library A distributable package with an importable API (a Python package, an npm package with main/exports that isn't a web app, a Go module with non-main packages, a Rust lib crate). A service counts as a library only if it is published.
    desktop A desktop GUI toolkit: Electron, Tauri, Qt, GTK, Tkinter, wxWidgets, WPF/WinForms/MAUI, Avalonia, JavaFX/Swing, Compose Desktop, Rust GUI crates (egui, iced, slint), Fyne/Wails, Flutter desktop, or a macOS SwiftUI/AppKit app (SwiftUI alone may be iOS, so a macOS indicator is required).
    format A file or wire format: a specification document that describes bytes, headers, offsets and encodings (not just any spec.md), or a machine-readable definition (Kaitai Struct, ABNF/EBNF). Its reference implementations are detected as libraries, up to two directories deep (js/, python/<pkg>/, crates/<name>/).
    frontend An index.html with a package.json, and no service or desktop app (an Electron/Tauri index.html is the desktop app's UI).

    Three more properties decide whether some dimensions apply: a long-running mode (--watch, serve, daemon), owning persistent data (migrations, ORM models, database drivers, SQLite files), and using credentials (credential environment variables or secret options).

  2. Each applicable surface climbs a ladder. Every level lists requirements. A surface reaches level N only when it meets every requirement of levels 1 to N; a met requirement above an unmet one doesn't count.

  3. The dimension's level is the best surface's level. Integrators use the best way in. The other surfaces' levels and first missing requirements are still reported ([cli] L2 … in the gaps; the full ladders with -v or in the JSON).

  4. Not applicable (n/a) is a result, decided by rules, never by missing evidence. A dimension that applies to none of the project's surfaces is reported as n/a and left out of the integrability index and the retrofit estimate. The rules are given with each dimension below. Interface is never n/a: having no programmatic surface is level 0.

Level Name
0 absent
1 ad hoc
2 partial
3 solid (the target of retrofit estimates)
4 exemplary

Languages

Every language is inventoried and counted, and Python, JavaScript/TypeScript, Go, Rust, Java/Kotlin, C#, PHP, Ruby and Swift have dedicated patterns. For Swift that means Swift Package Manager (Package.swift executables, libraries and dependencies, tags as releases, Package.resolved), swift-argument-parser CLIs, Vapor/Hummingbird routes, Tests/ targets, swift-log and os_log, Error types, @available(*, deprecated…), DocC catalogs, and """ strings and NSRegularExpression patterns in the code view.

What counts as evidence

Detectors read product code only: tests, examples, docs, fixtures, vendored code (vendor/, third_party/, libs/, …) and generated or minified files are excluded. Library names count only when they appear in dependency manifests, and transitive Go dependencies (// indirect) are ignored.

Code is matched through a code view that removes text which talks about a practice instead of using it:

  • comments and docstrings;
  • strings that read like sentences (messages, help texts, prompts);
  • the contents of regex literals (Python raw strings and re.* arguments, JS /…/ and RegExp, Go regexp.MustCompile, Rust Regex::new, and their Java, C# and PHP equivalents). Route detection still sees them, because Django routes are regexes.

A file whose strings are detection vocabulary (a linter's rules, a scanner's patterns) can opt out with a neosloc: ignore comment in its first five lines.

1. Interface

Can another program use it through a documented, machine-readable surface?

Level service cli library desktop format frontend
1 A network surface exists: HTTP routes, an MCP server, or a standard protocol listener. Invocable non-interactively: no prompts, or prompts guarded by a TTY check or a --yes/--no-input option. An importable, packaged API exists. Launchable by other programs with input: command-line arguments, a URL scheme or file associations. A specification document describes the format (layout, header, encoding), or a machine-readable definition exists. A programmatic surface besides the UI.
2 A contract exists: a checked-in OpenAPI/GraphQL/protobuf/AsyncAPI spec, a framework-generated spec, MCP tool schemas, or a standard application protocol that off-the-shelf clients speak (RESP, MQTT, gRPC, PostgreSQL wire, …). --help documents every option (at least 90% of options have help text). The public API is delimited (all, an exports map, exported identifiers). An automation interface: AppleScript, App Intents/Shortcuts, D-Bus, COM, IPC commands, or a local API. The specification is versioned (versioned spec documents, or a version field in the format). —
3 A contract covers its transport: an HTTP contract covers at least 80% of declared routes (generated contracts do), or the application layer over a standard protocol (commands, topics, payloads) is specified in a protocol document or AsyncAPI. Machine-readable output (--json/--format) and documented exit statuses. The public API is statically typed (py.typed, type declarations, a typed language). Its functions run without the GUI (a headless/batch mode or a companion CLI). Reference implementations exist in at least two languages. —
4 A second surface: another transport (HTTP and a protocol, two protocols), an MCP server, a CLI with JSON output, or an SDK. The output format is specified by a JSON Schema. An API reference is generated from the code (autodoc, mkdocstrings, typedoc, …). The automation interface is specified machine-readably (an AppleScript sdef, D-Bus introspection XML, App Intents, an OpenAPI spec, MCP). Conformance material: sample files including invalid ones, or a machine-readable definition (Kaitai Struct, ABNF, JSON Schema). —

Never n/a. A frontend alone, or no surface at all, is level 0. For a file format the specification is the interface, and a format is only as usable as its implementations, so level 3 asks for reference implementations in two languages.

A standard application protocol is a contract in its own right: any off-the-shelf client can use the server. What it leaves open is the application layer on top (which commands, topics and payloads), so level 3 asks for that to be specified, in a protocol document or AsyncAPI. A bare WebSocket only carries custom messages, so it is a surface but not a contract.

2. Contract stability

Can integrators rely on it not changing under them?

Level format all
1 The format carries a version. Releases are versioned (semver tags or versioned changelog sections).
2 Changes between format versions are documented (a spec changelog or per-version specifications). A changelog exists and marks breaking changes.
3 The specification states compatibility rules (what readers and writers must do across versions, deprecations). A deprecation mechanism is used in code (warnings, @deprecated, Deprecation/Sunset headers) or the API is versioned.
4 Old versions can be migrated or converted (documented tooling). Across tagged releases, no OpenAPI operation, CLI option or all symbol was removed without first being deprecated.

Applies when there is a service, CLI or library. The history check walks tagged releases and compares OpenAPI operations, CLI long options and Python __all__ symbols. An item counts as properly removed only if the release before its removal mentioned it in a deprecation warning.

3. Events

Can others react to state changes without polling?

Level service cli
1 Internal events exist (signals, queues, an outbox, emitters). Progress is reported while running.
2 Webhooks sent by this system, or a streaming endpoint (SSE, WebSocket). A machine-readable event stream (NDJSON / JSON Lines).
3 Signed webhooks registrable through the API, or two mechanisms (webhooks, streams, a broker). Hooks run commands or URLs on events (--on-…, --exec, webhooks).
4 Events are described by AsyncAPI or CloudEvents. The event format is specified (a schema).

n/a for libraries, frontends and batch CLIs. A CLI is assessed only if it has a long-running mode. Receiving other platforms' webhooks never counts as publishing events.

4. Identity

Can a machine act with its own revocable, least-privilege identity?

Level service cli library desktop
1 Programmatic authentication (bearer tokens, API keys). Credentials can be supplied non-interactively (environment variables). Credentials can be supplied non-interactively (environment variables). Credentials are kept in the OS credential store (Keychain, Credential Manager, Secret Service), not in plain files.
2 Per-client, revocable credentials (managed API keys, service accounts) or OAuth2. Credentials never need to be on the command line, and their environment variables are documented. The credential environment variables are documented. Automation and headless runs can authenticate without the GUI (credentials from the environment).
3 Credentials carry scopes or permissions. No output or log statement includes a credential. No output or log statement includes a credential. No output or log statement includes a credential.
4 Standard provisioning or federation (OIDC client credentials, SCIM). Several profiles or scoped credentials per target. Several profiles or scoped credentials per target. Several profiles or scoped credentials per target.

n/a for frontends, and for CLIs and libraries that use no credentials.

5. Data portability

Can its data get in and out, in bulk, in open formats, with an explicit schema?

Level Requirement
1 The data schema is explicit (migrations, DDL, schema files).
2 An export exists (endpoint or command, not only the UI).
3 A bulk export in an open format, and a bulk import.
4 The export format is documented and the round trip is tested.

n/a for projects that own no persistent data, and for file formats: a format is itself the portability layer. A tool's output format belongs to Interface (cli level 4).

6. Agent ergonomics

Can an automated caller use it without guessing, and recover from failure?

Level service cli library desktop
1 Errors are machine-readable: consistent JSON over HTTP, or a standard protocol's native error replies (RESP errors, MQTT reason codes, gRPC status, SQLSTATE). Never blocks on a prompt when stdin is not a TTY. Errors are specific exception types, and library code never calls sys.exit. In headless mode, failures end with a non-zero exit status instead of a dialog.
2 Field-level validation errors, and paginated lists. Data on stdout, diagnostics on stderr, non-zero exit on failure. Inputs are validated with messages naming the bad argument. In headless mode, data goes to stdout and diagnostics to stderr.
3 Typed errors (RFC 9457 over HTTP, or a standard protocol's typed error codes), idempotent writes (Idempotency-Key), and rate-limit signals. Documented exit statuses distinguish error classes, errors are JSON in JSON mode, and color is off when not a TTY. Timeouts and retries are configurable. Documented exit statuses distinguish error classes, and headless output is machine-readable (JSON).
4 Dry-run or validate-only, conditional requests, and agent docs. --dry-run, limits for long or costly operations (timeouts, budgets), and agent docs. Agent docs, and examples that run as tests (doctests). Dry-run for side-effecting operations, and agent docs.

Applies to services, CLIs and libraries.

7. Embeddability

Can another system run and compose it without a human?

Level service cli library desktop
1 Runnable from source with documented steps (a README with commands). Runnable from source (a declared entry point). Importable from source. Buildable from source with documented steps (a README with commands).
2 Configured through environment variables, documented: a .env example, a config schema, or docs naming at least half of the variables the code reads. Installable as a package that provides the command. Installable as a package (a package manifest). Packaged as an installer (dmg/pkg, MSI/MSIX, AppImage/Flatpak/Snap, electron-builder, Tauri bundle, PyInstaller).
3 A container image. Published to a public registry and run non-interactively in CI. Published to a public registry, with at most 10 runtime dependencies. Distributed for unattended installation: a package manager (Homebrew cask, winget, Chocolatey, Flathub, Snap) or release artifacts built in CI.
4 Composition artifacts (compose, Helm, IaC). Also shipped as a container image or a single binary. Optional features behind extras/features. Settings can be managed without the GUI: documented environment variables or config file, macOS defaults/MDM profiles, or Windows policies.

Applies to services, CLIs and libraries.

8. Extensibility

Can behaviour be added without modifying the code?

Level format all
1 The specification defines free-form metadata or custom fields. Internal seams: a registry, registration function or plugin manager.
2 Readers are told how to treat unknown fields or values (forward compatibility), or values are reserved. The docs show how to add behaviour through those seams (even in-tree).
3 Enumerations (types, compressions) are extensible, with reserved or registered values. Out-of-tree extensions are discovered (entry points, a plugin directory, configuration) without editing the package.
4 A documented process or registry for adding extensions. The extension API is versioned, has lifecycle hooks, and has example extensions.

Applies to every project. "Documented" is checked by matching the docs against the project's own registration functions (for example a register decorator), not by looking for words like "plugin".

9. Observability

Can an operator, or an orchestrating agent, tell whether it works and why not?

Level service cli library desktop
1 A health or readiness endpoint, or a container healthcheck. Meaningful exit statuses, and errors on stderr. Logs through a standard logger namespace and never prints from library code. Writes logs through a standard logger (os_log/Logger, electron-log, logging, Qt categories).
2 Structured logs. Verbosity control for diagnostics (--quiet, --log-level, --debug). Log levels are used meaningfully (at least two levels). Log verbosity can be raised for diagnosis (--verbose/--debug, a log-level setting).
3 Metrics. Structured logs (e.g. --log-format json) or a machine-readable run summary. Optional metrics or tracing hooks. Crashes are reported (Sentry, Crashpad/Breakpad, Crashlytics, MetricKit).
4 Distributed tracing (OpenTelemetry). Tracing hooks (OpenTelemetry). OpenTelemetry instrumentation available. Tracing or metrics (OpenTelemetry).

Applies to services, CLIs and libraries.

10. Change legibility

Can an agent change it safely with limited context?

Level Requirement
1 A README and some tests.
2 CI runs the tests; applications pin dependencies (libraries and zero-dependency projects are exempt).
3 At least 90% of modules fit 32k tokens and no file exceeds 12k; test/source token ratio at least 0.3; at least 70% of the code is typed.
4 No import cycles, and agent docs (AGENTS.md, CLAUDE.md, llms.txt).

Applies to every project. Size is measured in tokens per module and per file, because what matters is whether a unit of change fits in an agent's working context. A module is a directory of source files.