# Near-real-time score updates

- **Pattern:** `ab-000615` (`campaign-orchestration-sequencing.lead-scoring.realtime-updates`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000615
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000615)

## Why it matters

A nightly batch score recalculation means a contact who crosses the SQL threshold at 9am on a Tuesday does not trigger the sales notification until 2am Wednesday — a 17-hour delay on the highest-intent moment in the lead lifecycle. iso-25010:2011 performance-efficiency.time-behaviour captures the failure: the system's response time for a threshold-crossing event is measured in hours, not minutes. In B2B SaaS where intent signals decay rapidly, delayed scoring translates directly to missed pipeline: the contact who booked a competitor demo at 10am was already gone before the batch ran.

## Severity rationale

Low because batch-only scoring delays threshold actions by hours — a significant conversion impact but not a data integrity failure or compliance risk.

## Remediation

Update lead scores in the engagement event handler (webhook callback or queue job), not in a scheduled batch. Call threshold evaluation immediately after the score write.

```ts
// In the engagement event handler
async function handleEngagementEvent(event: EngagementEvent) {
  const scoreChange = SCORE_EVENTS[event.type]?.points ?? 0
  if (scoreChange === 0) return

  const updated = await db.contact.update({
    where: { id: event.contactId },
    data: { leadScore: { increment: scoreChange } },
    select: { id: true, leadScore: true }
  })

  // Evaluate thresholds in the same request context
  await evaluateScoreThresholds(updated.id, updated.leadScore)
}
```

A background job queue (BullMQ, Inngest, etc.) is acceptable if it processes events within 1–5 minutes. A nightly cron is not. Enumerate all score-update triggers in the codebase and verify that none are batch-only.

## Detection

- **ID:** `realtime-updates`
- **Severity:** `low`
- **What to look for:** Check how quickly lead scores are updated after an engagement event. Updates should happen within minutes, not hours or days. Look for: event-driven score updates (webhook handlers or job queues that update scores when events arrive), versus batch cron jobs that recalculate all scores on a daily or weekly basis. A daily batch is acceptable if near-real-time routing is not required, but a batch approach means a contact who crosses a scoring threshold at 9am may not receive the threshold action until the next batch run.
- **Pass criteria:** Lead scores are updated within no more than 5 minutes of an engagement event, either via event-driven handlers or a frequently-running queue processor. Threshold actions fire within the same time window. Count all score-update triggers and enumerate which are event-driven vs. batch.
- **Fail criteria:** Scores are only updated in a nightly or weekly batch job. A contact crossing a critical threshold (e.g., score over 100 → route to sales) may wait up to 24+ hours for the action to fire.
- **Skip (N/A) when:** The project does not implement lead scoring.
- **Detail on fail:** `"Lead scores updated in a nightly cron job — threshold actions can be delayed up to 24 hours after the triggering event"` or `"No event-driven score updates found — only a weekly batch recalculation"`
- **Remediation:** Process score updates in event-driven job handlers:

  ```ts
  // In the engagement event handler (webhook or ESP callback)
  async function handleEngagementEvent(event: EngagementEvent) {
    const scoreChange = SCORE_EVENTS[event.type]?.points ?? 0
    if (scoreChange === 0) return

    const updated = await db.contact.update({
      where: { id: event.contactId },
      data: { leadScore: { increment: scoreChange } },
      select: { id: true, leadScore: true }
    })

    // Check thresholds immediately after update
    await evaluateScoreThresholds(updated.id, updated.leadScore)
  }
  ```

## External references

- iso-25010 performance-efficiency.time-behaviour

Taxons: data-integrity

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