# Attribution window is configurable, not hard-coded

- **Pattern:** `ab-000592` (`campaign-analytics-attribution.attribution-conversion.attribution-window-configurable`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000592
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000592)

## Why it matters

Attribution windows determine how far back from a conversion you look for a causal email touchpoint. A 7-day window and a 30-day window produce materially different revenue attribution numbers for the same campaign — especially for products with long consideration cycles. If the window is a magic number hard-coded in four different query files, changing your attribution model to match your actual sales cycle requires a multi-file search-and-replace with no guarantee you caught every instance. This is an iso-25010:2011 maintainability failure: the system cannot adapt to a known parameter without engineering effort and regression risk.

## Severity rationale

Low because incorrect window values degrade attribution accuracy but don't cause data loss or security exposure — the risk is misleading reporting rather than a broken system.

## Remediation

Extract the attribution window to a single named constant sourced from an environment variable so it can be changed in one place without touching query logic:

```ts
// lib/attribution/config.ts
export const ATTRIBUTION_WINDOW_DAYS = parseInt(
  process.env.ATTRIBUTION_WINDOW_DAYS ?? '30',
  10
)

export function getAttributionWindowStart(): Date {
  return new Date(Date.now() - ATTRIBUTION_WINDOW_DAYS * 24 * 60 * 60 * 1000)
}
```

Import `getAttributionWindowStart()` in every attribution query. Set `ATTRIBUTION_WINDOW_DAYS` in your environment config (`.env.local`, Vercel env vars) so it's tunable without a deploy. Document the chosen value and rationale in a comment near the constant.

## Detection

- **ID:** `attribution-window-configurable`
- **Severity:** `low`
- **What to look for:** Look at the attribution window used when computing which touchpoints receive credit for a conversion. Check whether the attribution window (e.g., 7 days, 30 days) is a configurable constant or configuration value, versus being hard-coded as a magic number scattered across multiple queries. The ability to adjust the attribution window without code changes is important as you refine your model to match your actual sales cycle length.
- **Pass criteria:** Attribution window is defined as a named constant, environment variable, or database configuration that can be changed without modifying query logic. The value is referenced from a single definition. Count all files that reference the attribution window value — no more than 1 source definition should exist.
- **Fail criteria:** Attribution window is hard-coded as a magic number (e.g., `30` or `2592000`) directly in query logic with no named constant. Multiple places have different hard-coded values.
- **Skip (N/A) when:** Attribution windows are not used (the project does not perform attribution).
- **Detail on fail:** Example: `"Attribution window hard-coded as 30 in 4 different query files — changing the window requires editing multiple files"` or `"Magic number 2592000 (30 days in seconds) appears in queries without explanation"`
- **Remediation:** Extract to a named constant:

  ```ts
  // lib/attribution/config.ts
  export const ATTRIBUTION_WINDOW_DAYS = parseInt(
    process.env.ATTRIBUTION_WINDOW_DAYS ?? '30',
    10
  )

  // Use in queries
  import { ATTRIBUTION_WINDOW_DAYS } from '@/lib/attribution/config'

  function getWindowStart(): Date {
    return new Date(Date.now() - ATTRIBUTION_WINDOW_DAYS * 24 * 60 * 60 * 1000)
  }
  ```

## External references

- iso-25010 maintainability

Taxons: code-quality

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