Skip to main content

Integration into large projects

It is usually not possible to modularize an existing codebase at once. Instead, we have to integrate Sheriff incrementally.

Next to automatic tagging, we introduce modules step by step.

With barrel-less modules

The recommended approach is start with only one module. For example holidays/feature. Encapsulated files of that modules need to be moved to the internals folder. If holidays/feature is barrel-less, it can access root, given the dependency rules allow access to tag root.

By default, barrel-less modules are disabled. They have to be enabled in sheriff.config.ts via enableBarrelLess: true.

Without barrel-less modules

If Sheriff only supports barrel modules, then the integration would still progress module by module. holidays/feature gets an index.ts and defines its exposed files. Since root would be barrel-less, holidays/feature cannot access it.

There is a special property for this use case: excludeRoot. Once set to true, all modules can access all files in the root module.

export const config: SheriffConfig = {
excludeRoot: true, // <-- set this
modules: {
'src/shared': 'shared',
},
depRules: {
root: 'noTag',
noTag: ['noTag', 'root'],
shared: anyTag,
},
};

Please note that the excludeRoot property only makes sense with enableBarrelLess: false.

ESLint daemon bridge (experimental, opt-in)

The ESLint plugin can route its checks through a running Sheriff daemon. Start the daemon and opt in when invoking ESLint:

sheriff daemon start
SHERIFF_DAEMON=1 eslint .

The bridge uses two distinct timeouts:

  • A short connection timeout of approximately 200 ms decides whether a daemon is available. If none can be reached, the plugin permanently falls back to its in-process checks for the rest of that ESLint process and does not retry on later files. This keeps CI behavior deterministic when no daemon is available.
  • A separate, larger per-call timeout (default 5000 ms, configurable via SHERIFF_DAEMON_TIMEOUT_MS) bounds each individual lint round-trip, which includes the daemon's cold initialization on the first request. A single slow call falls back in-process for that file only; the bridge stays enabled so later files still use the daemon. It is disabled permanently only after several consecutive per-call failures, which indicates a genuinely broken daemon rather than one slow file.