# No side effects on import

- **Pattern:** `ab-002357` (`sdk-package-quality.api-surface.no-side-effects`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002357
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002357)

## Why it matters

Code that executes at import time bypasses the consumer's control plane. A top-level `fetch()` fires before the consumer has configured anything; a top-level `console.log` pollutes test output and server logs; a `globalThis` mutation can conflict with other packages or the host environment. ISO 25010 compatibility and fault-tolerance both degrade when a package modifies global state the moment it's imported. In server-side rendering, side-effecting imports can crash the render pass. In test environments, they produce flaky behavior depending on import order.

## Severity rationale

Critical because side effects on import execute before the consumer opts in, enabling silent global mutations, unsolicited network calls, and SSR crashes that are invisible until production.

## Remediation

Move all initialization into exported constructors or factory functions. Nothing should run at module evaluation time.

```ts
// Before — side effects on import:
console.log('SDK loaded')
globalThis.__SDK = true
const config = await fetch('/api/config') // this fires on every import

// After — no side effects, consumer controls initialization:
export class Client {
  constructor(private config: ClientConfig) {
    // initialization here, when the consumer asks for it
  }
}
```

Also declare in `package.json`:

```json
{ "sideEffects": false }
```

This tells webpack, Rollup, and Vite that all modules are safe to tree-shake. If any files have legitimate side effects (CSS imports, polyfills), list only those files in the `sideEffects` array.

## Detection

- **ID:** `no-side-effects`
- **Severity:** `critical`
- **What to look for:** Count all module-level statements in the SDK entry point and public modules. For each, classify whether it causes side effects on import. examine the main entry point file and any files it imports (follow the import chain). Look for code that executes at import time:
  1. Top-level `fetch()`, `axios()`, or HTTP calls
  2. Top-level `console.log()`, `console.warn()`, or other logging
  3. Top-level `addEventListener()` or DOM manipulation
  4. Top-level `setInterval()` or `setTimeout()`
  5. Top-level mutations to `global`, `window`, `process`, or `globalThis`
  6. Importing for side effects (`import './polyfill'`, `import './register'`)
  7. Module-level code that reads from the filesystem or environment
  Also check `package.json` for `"sideEffects": false` declaration.
- **Pass criteria:** Importing the package does not execute any observable side effects. All functionality is behind exported functions or class constructors that must be explicitly called. The package may declare `"sideEffects": false` in `package.json` (recommended but not required for pass) — 100% of imports must be side-effect-free unless explicitly documented. Report: "X module-level statements found, Y are side-effect-free."
- **Fail criteria:** The entry point or its immediate imports execute code at module evaluation time — logging, network requests, global mutations, polyfills, or event listeners that run when someone does `import 'your-package'`.
- **Skip (N/A) when:** The package is explicitly designed to be a polyfill, shim, or registration side-effect (e.g., `@babel/register`, CSS reset packages). The `package.json` description or README must indicate this intent.
- **Detail on fail:** `"src/index.ts has a top-level console.log('SDK initialized') and sets global.__SDK_VERSION. These execute when the package is imported, before the consumer has opted in. A fetch() call to the package telemetry endpoint also runs at import time."`
- **Remediation:** Side effects on import surprise consumers, break tree-shaking, and cause problems in test environments and server-side rendering.

  ```ts
  // Before — side effects on import:
  console.log('MySDK v1.0.0 loaded')
  globalThis.__MY_SDK = true
  const config = await fetch('/api/config')

  export class Client { /* ... */ }

  // After — no side effects, everything is explicit:
  export class Client {
    constructor(config: ClientConfig) {
      // initialization happens here, when the consumer asks for it
    }
  }

  export function getVersion() {
    return '1.0.0'
  }
  ```

  Also add to `package.json`:
  ```json
  { "sideEffects": false }
  ```

## External references

- iso-25010 compatibility.interoperability
- iso-25010 reliability.fault-tolerance

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-002357
