Configuration Reference

Every WireDoctor property in one place. All properties are optional — WireDoctor works out of the box with zero configuration.

Core

Property Default Description
wiredoctor.enabled true Master switch. false completely disables the analyzer — no analysis, no reports, no bean-structure exposure. Set this in application-prod.properties if the dependency ships to production.
wiredoctor.output-path project root Directory where wiredoctor-report.json and wiredoctor-report.html are written. Set it to target (or build) if your build lints the source tree — see the note below.
wiredoctor.scan-packages (auto) Comma-separated package prefixes to analyze for orphan beans. By default, framework packages (org.springframework, java., org.apache, …) are filtered out automatically.
wiredoctor.slow-bean-threshold-ms 100 Beans taking longer than this to instantiate are flagged as slow (report + console).
wiredoctor.max-graph-nodes 2000 Above this many beans, the serialized graph (JSON + HTML view) is capped to top-N by fan-in (cycle members always kept) so the browser doesn’t freeze. Analysis itself — cycles, smells, critical path, baseline diff — always runs on the full graph. 0 = unlimited.
wiredoctor.include-framework-smells false Include framework beans in smell rankings. Off by default so every ranked bean is one you can actually refactor.
wiredoctor.scan-packages=com.yourcompany.app,io.yourteam.service
wiredoctor.output-path=target
wiredoctor.slow-bean-threshold-ms=50
wiredoctor.max-graph-nodes=2000

Set output-path if your build lints the source tree

By default the report lands in the project root, where source-tree linters will find it. The HTML embeds vis.js and egjs, whose license headers contain http:// URLs, so any project using Spring’s nohttp-checkstyle — that is, every Spring project and every build that inherited Spring’s parent — fails its next build on a file nobody wrote:

[ERROR] wiredoctor-report.html:[17,43] (extension) NoHttp: http:// URLs are not
        allowed but got 'http://almende.com'. Use https:// instead.
[ERROR] Failed to execute goal maven-checkstyle-plugin:check
        (nohttp-checkstyle-validation): You have 11 Checkstyle violations.
wiredoctor.output-path=target

target/ is already outside the lint scope and already ignored by git, so this also keeps the report out of commits and diffs.

scan-packages also cleans the smell rankings

Naming your own packages does more than filter orphan beans: without it, WireDoctor’s own beans are ranked in your architecture report (com.wiredoctor.WireDoctorAutoConfiguration as a coupling hotspot, wireDoctorAnalyzer as unstable), alongside framework beans you cannot refactor.

wiredoctor.scan-packages=com.yourcompany.app

With that set, every ranked bean is one you own. It is the single highest-leverage property for first-run signal quality.

Regression Guard & Gates (opt-in — CI only)

Property Default Description
wiredoctor.baseline (unset) Path to the committed architecture baseline. Setting it enables the diff.
wiredoctor.baseline-write false true writes/refreshes the baseline (never diffs or gates on that run).
wiredoctor.fail-on "" Comma-separated gates that fail startup. Four are diff gates and need a baseline: new-cycle, condition-changed, startup-time, slow-bean. A fifth, boundary-violation (v1.2.0), needs no baseline — it trips on any current module-boundary violation. Empty = report-only.
wiredoctor.startup-time-absolute-threshold 500 ms. Startup must regress by more than this AND the relative threshold to trip startup-time.
wiredoctor.startup-time-relative-threshold 0.20 Fraction (0.20 = 20%). The other half of the dual-threshold AND condition.
wiredoctor.slow-bean-margin-ms 20 Jitter margin for the slow-bean gate: a new slow bean must exceed threshold + margin to trip. Beans inside the margin band are reported but never fail CI. 0 = exact pre-v0.8.0 behavior.
wiredoctor.trend-history-size 30 Cap on trendHistory[] entries kept in the baseline file. Each baseline-write run appends one {timestamp, totalStartupMs, slowBeanCount} entry and trims the oldest beyond the cap. 0 = unlimited. See Startup Time Trend.
# One-time baseline capture (commit the file):
wiredoctor.baseline=wiredoctor-baseline.json
wiredoctor.baseline-write=true

# CI profile — diff and gate:
wiredoctor.baseline=wiredoctor-baseline.json
wiredoctor.baseline-write=false
wiredoctor.fail-on=new-cycle,startup-time,slow-bean

Gates write wiredoctor-gate.status (PASS/FAIL) and wiredoctor-diff.json for CI inspection. Full walkthroughs: Performance Gates · CI gating · Upgrade Guard.

The one gate that sits outside all of this is boundary-violation: it has no diff and no baseline, so it produces no wiredoctor-diff.json — but it still records its verdict in wiredoctor-gate.status (FAIL:boundary-violation on line 1, written even with no baseline). So a current violation fails the JVM with a non-zero exit and stays grep-able for CI that can’t trust the exit code. Set it up in Module Boundaries → gate it in CI.

Ghost Tracking (opt-in — dev/staging only)

Property Default Description
wiredoctor.ghost-tracking.enabled false Wraps eligible user beans in a thin first-touch counting proxy. Off by default: the tracking BeanPostProcessor is never registered at all (regression-tested passivity).
wiredoctor.ghost-tracking.exclude (unset) Comma-separated bean names to never wrap — reported as untrackable:excluded, never silently hidden.
wiredoctor.ghost-tracking.enabled=true
wiredoctor.ghost-tracking.exclude=legacySoapClient,nativeBridge

Results land in wiredoctor-ghost-report.json at shutdown, or live via /actuator/wiredoctor/ghosts. Details: Ghost Detector guide.

Module Boundaries (opt-in — multi-module architectures)

Declare your modules by package prefix and WireDoctor flags hidden coupling: an edge from one module into another module’s internal (non-API) package. It compiles and runs fine today — which is exactly why it goes unnoticed until the modules can no longer be pulled apart.

Property Default Description
wiredoctor.module-boundaries.modules (empty) Map of package-prefix → module name. Empty = feature off (zero overhead — the detector short-circuits). A bean is assigned to the module whose configured prefix is the longest match for its package, so nested modules (com.acme vs com.acme.orders) resolve correctly.
wiredoctor.module-boundaries.api-packages (empty) Glob patterns for each module’s public surface, e.g. *.api. A cross-module edge whose target package matches one of these is allowed; any other cross-module edge is a violation. * matches any characters; a matched package’s sub-packages count as public too.
wiredoctor:
  module-boundaries:
    modules:
      "[com.acme.orders]": orders
      "[com.acme.billing]": billing
    api-packages:
      - "*.api"

Violations show up in the console at startup, in wiredoctor-report.json under boundaryViolations, and in the Boundaries tab of the HTML report. All three are absent entirely when no modules are configured — the section is additive and schemaVersion stays 1. Details: Module Boundaries guide.

Gotcha: map keys with dots need brackets

modules is a Map whose keys are package names, and package names contain dots. Spring’s relaxed binding reads a dot as a nesting separator, so an unquoted com.acme.orders: key binds as nested objects (com → acme → orders), not the single string key you meant — and the module silently never matches anything. Wrap the whole key in [...]:

# ✅ correct — the dotted key is taken literally
wiredoctor.module-boundaries.modules:
  "[com.acme.orders]": orders

# ❌ wrong — binds as com/acme/orders nesting; the module never resolves
wiredoctor.module-boundaries.modules:
  com.acme.orders: orders

In a .properties file (or in --args/SpringApplicationBuilder properties) the same key uses index-style brackets, no surrounding quotes:

wiredoctor.module-boundaries.modules[com.acme.orders]=orders
wiredoctor.module-boundaries.api-packages[0]=*.api

Production Safety

WireDoctor is enabled by default. If the dependency accidentally ships to production:

# application-prod.properties
wiredoctor.enabled=false

For what the reports expose and WireDoctor’s offline-only network behavior (its JVM does zero network I/O), see the security posture guide.


v1.0.0 Stability Contract

All wiredoctor.* property names listed above are frozen as of v1.0.0:

  • A property will not be removed without being deprecated for at least one minor release first.
  • Deprecated properties log a WARN on startup; the old name remains functional until the next major.
  • The report JSON field names (schemaVersion, beanCategories, dependencies, smells, gates, etc.) are frozen at schemaVersion: 1. A field rename or removal requires a new schemaVersion value and a major version bump.
  • Default values will not change in patch or minor releases.

If you pin the dependency at 1.0.x, you are guaranteed no breaking config or schema changes until 2.0.0.