Overview

Error tracking captures runtime exceptions, ties each to a release and environment, and routes actionable alerts to the right person before users report the problem. Sentry is the reference implementation; Datadog, Rollbar, and Honeybadger cover the same ground.

Initialize with a DSN, environment, and release

Only dsn is required, but without environment every environment lands in one stream (Sentry JS options). Import the init file before all app code.

// src/instrument.ts
import * as Sentry from "@sentry/node"
 
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,     // production | staging | development
  release: process.env.SENTRY_RELEASE,   // git SHA or semver tag
  tracesSampleRate: process.env.NODE_ENV === "production" ? 0.1 : 1.0,
  sendDefaultPii: false,
})

Read DSN and release from the environment and keep auth tokens in CI secrets (secrets-and-env). Inject SENTRY_RELEASE at build time (ci-cd).

Upload source maps and keep them out of the deploy

Without source maps, a frame like bundle.min.js:1:34291 is useless. Prefer the Sentry bundler plugin for webpack, Vite, Rollup, and esbuild (docs), placed after all other plugins, with source maps generated and uploaded then deleted:

// vite.config.ts
import { sentryVitePlugin } from "@sentry/vite-plugin"
export default {
  build: { sourcemap: "hidden" },
  plugins: [
    sentryVitePlugin({
      org: process.env.SENTRY_ORG,
      project: process.env.SENTRY_PROJECT,
      authToken: process.env.SENTRY_AUTH_TOKEN,
      sourcemaps: { filesToDeleteAfterUpload: ["./dist/**/*.map"] },
    }),
  ],
}

Without a plugin, inject debug IDs and then upload; the inject step is required (CLI docs):

sentry-cli sourcemaps inject ./dist
sentry-cli sourcemaps upload --release="$SENTRY_RELEASE" ./dist   # org and project come from env or config

"hidden" source maps omit the sourceMappingURL comment but still write .map files, and a served .map file exposes your original source. Delete them from the artifact after upload.

Tag every deploy with a release and its commits

A release enables regression detection (“first seen in v2.4.1”) and suspect commits (releases).

sentry-cli releases new "$SENTRY_RELEASE"
sentry-cli releases set-commits "$SENTRY_RELEASE" --auto
sentry-cli releases finalize "$SENTRY_RELEASE"
sentry-cli deploys new --release "$SENTRY_RELEASE" -e production

--auto associates commits since the previous release through the configured repository integration. Run it in the CI step that deploys so the release matches what is live.

Separate environments

  • Development: disable the DSN; local crashes are not actionable.
  • Staging: capture everything, alert a channel the team watches casually.
  • Production: full capture, strict routing, page for P1 issues.

Mixed environments inflate counts and teach the team to ignore alerts.

Scrub PII before the event leaves the process

sendDefaultPii defaults to false, which keeps IP addresses and cookies out of events. Strip anything else in beforeSend (custom context, breadcrumbs, request bodies); returning null drops the event (sensitive data).

beforeSend(event) {
  if (event.request?.headers) {
    delete event.request.headers["Authorization"]
    delete event.request.headers["Cookie"]
  }
  if (event.user?.email) event.user.email = "[redacted]"
  return event
},

Drop known noise (bot errors, expected 404s) with null rather than ingesting it and muting it with alert rules.

Control volume with sampling

Keep error sampleRate at 1.0 unless volume is unmanageable. Start tracesSampleRate at 0.1 in production and use tracesSampler for per-route control (health checks at 0, checkout at 1.0).

Alert on change, not on every event

Notify immediately on a new issue and on a regression (a resolved issue reappearing). Add a volume rule such as more than N events in M minutes on one issue. Route production P1 alerts to the on-call rotation (incident-response) and staging to Slack, through Sentry’s native Slack and PagerDuty integrations or webhooks. Avoid email-only routing; inbox alerts get batched and ignored.