SE

sentry-reference-architecture

Establishes best practices for structuring Sentry projects across large, multi-service organizations.

Install

mkdir -p .claude/skills/sentry-reference-architecture && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/9037" && unzip -o skill.zip -d .claude/skills/sentry-reference-architecture && rm skill.zip

Installs to .claude/skills/sentry-reference-architecture

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Design production-grade Sentry architecture for multi-service organizations.
76 charsno explicit “when” trigger
Advanced

Key capabilities

  • Implement one-project-per-service topology
  • Create centralized Sentry configuration modules
  • Configure team-based alert routing
  • Set up distributed tracing across microservices
  • Manage error middleware for Express and FastAPI

How it works

It establishes a centralized configuration module to enforce organization-wide defaults and project-specific topology for multi-service environments.

Inputs & outputs

You give it
Service configuration and DSN
You get back
Standardized Sentry project structure and error handling

When to use sentry-reference-architecture

  • Plan Sentry project rollout
  • Set up distributed tracing for microservices
  • Configure team-based alert routing
  • Implement centralized Sentry configuration

About this skill

Sentry Reference Architecture

Overview

Enterprise Sentry architecture patterns for multi-service organizations. Covers centralized configuration, project topology, team-based alert routing, distributed tracing, error middleware, source map management, and a production-ready SentryService wrapper.

Prerequisites

  • Sentry organization at sentry.io (Business plan+ for team features)
  • @sentry/node v8+ installed (npm install @sentry/node @sentry/profiling-node)
  • Service inventory and team ownership documented
  • Node.js 18+ (ESM and native fetch instrumentation)

Instructions

Step 1 — Project Structure Strategy

Pattern A: One Project Per Service (3+ services, recommended)

Organization: acme-corp
├── Team: platform-eng
│   ├── Project: api-gateway       (Node/Express)
│   ├── Project: auth-service      (Node/Fastify)
│   └── Project: user-service      (Node/Express)
├── Team: payments
│   ├── Project: payment-api       (Node/Express)
│   └── Project: billing-worker    (Node worker)
└── Team: frontend
    ├── Project: web-app           (React/Next.js)
    └── Project: mobile-app        (React Native)

Benefits: independent quotas, team-scoped alerts, per-service rate limits, isolated release tracking.

Pattern B: Shared Project (< 3 services, single team) — one project with Environment tags (production/staging/dev). Simpler setup; outgrow when alert noise exceeds one team.

Step 2 — Centralized Config Module

Create lib/sentry.ts imported by every service to enforce org-wide defaults:

// lib/sentry.ts
import * as Sentry from '@sentry/node';
import { nodeProfilingIntegration } from '@sentry/profiling-node';

export interface SentryServiceConfig {
  serviceName: string;
  dsn: string;
  environment?: string;
  version?: string;
  tracesSampleRate?: number;
  ignoredTransactions?: string[];
}

export function initSentry(config: SentryServiceConfig): void {
  const env = config.environment || process.env.NODE_ENV || 'development';
  Sentry.init({
    dsn: config.dsn,
    environment: env,
    release: `${config.serviceName}@${config.version || 'unknown'}`,
    serverName: config.serviceName,
    tracesSampleRate: config.tracesSampleRate ?? (env === 'production' ? 0.1 : 1.0),
    sendDefaultPii: false,
    maxBreadcrumbs: 50,
    integrations: [nodeProfilingIntegration()],
    ignoreErrors: [
      'ResizeObserver loop completed with undelivered notifications',
      /Loading chunk \d+ failed/,
      'AbortError',
    ],
    tracesSampler: ({ name, parentSampled }) => {
      if (parentSampled !== undefined) return parentSampled;
      const ignored = config.ignoredTransactions || [
        'GET /health', 'GET /healthz', 'GET /ready', 'GET /metrics',
      ];
      if (ignored.some(p => name.includes(p))) return 0;
      return config.tracesSampleRate ?? (env === 'production' ? 0.1 : 1.0);
    },
    beforeSend(event) {
      if (event.request?.headers) {
        delete event.request.headers['authorization'];
        delete event.request.headers['cookie'];
        delete event.request.headers['x-api-key'];
      }
      return event;
    },
    initialScope: {
      tags: { service: config.serviceName, team: process.env.TEAM_NAME || 'unassigned' },
    },
  });
}
export { Sentry };

Bootstrap in each service:

import { initSentry } from '@acme/sentry-config';
initSentry({ serviceName: 'api-gateway', dsn: process.env.SENTRY_DSN! });
// Must run BEFORE other imports that need instrumentation

Step 3 — Error Handling Middleware

Express:

// lib/sentry-middleware.ts
import * as Sentry from '@sentry/node';
import type { Request, Response, NextFunction, ErrorRequestHandler } from 'express';

export function sentryRequestHandler() {
  return (req: Request, _res: Response, next: NextFunction): void => {
    Sentry.setTag('http.route', req.route?.path || req.path);
    if (req.user) Sentry.setUser({ id: req.user.id });
    next();
  };
}

export const sentryErrorHandler: ErrorRequestHandler = (err, req, res, _next) => {
  const status = (err as any).statusCode || 500;
  Sentry.withScope((scope) => {
    scope.setLevel(status >= 500 ? 'error' : 'warning');
    scope.setTag('http.status_code', String(status));
    scope.setContext('request', { method: req.method, url: req.originalUrl });
    status >= 500 ? Sentry.captureException(err) : Sentry.captureMessage(err.message, 'warning');
  });
  res.status(status).json({ error: status >= 500 ? 'Internal server error' : err.message });
};
// Wire: app.use(sentryRequestHandler()) → routes → app.use(sentryErrorHandler)

FastAPI (Python):

import sentry_sdk, os
from sentry_sdk.integrations.fastapi import FastApiIntegration

def init_sentry(service_name: str) -> None:
    sentry_sdk.init(
        dsn=os.environ["SENTRY_DSN"],
        environment=os.getenv("ENV", "development"),
        release=f"{service_name}@{os.getenv('SERVICE_VERSION', 'unknown')}",
        traces_sample_rate=0.1,
        send_default_pii=False,
        integrations=[FastApiIntegration(transaction_style="endpoint")],
        before_send=lambda event, hint: _scrub(event),
    )

def _scrub(event):
    headers = event.get("request", {}).get("headers", {})
    for k in ["authorization", "cookie", "x-api-key"]:
        headers.pop(k, None)
    return event

Step 4 — Distributed Tracing

HTTP (automatic): SDK v8 auto-propagates sentry-trace + baggage headers on fetch/http. All services in the same org link automatically.

Message queues (manual propagation):

// lib/sentry-queue.ts
import * as Sentry from '@sentry/node';

export function publishWithTrace<T>(queue: string, payload: T, publish: Function) {
  return Sentry.startSpan({ name: `queue.publish.${queue}`, op: 'queue.publish' }, async () => {
    const headers: Record<string, string> = {};
    const span = Sentry.getActiveSpan();
    if (span) {
      headers['sentry-trace'] = Sentry.spanToTraceHeader(span);
      headers['baggage'] = Sentry.spanToBaggageHeader(span) || '';
    }
    await publish(queue, { payload, headers });
  });
}

export function consumeWithTrace<T>(queue: string, msg: { payload: T; headers: Record<string, string> }, handler: (p: T) => Promise<void>) {
  return Sentry.continueTrace(
    { sentryTrace: msg.headers['sentry-trace'], baggage: msg.headers['baggage'] },
    () => Sentry.startSpan({ name: `queue.process.${queue}`, op: 'queue.process' }, () => handler(msg.payload))
  );
}

Step 5 — Team-Based Alert Routing

Configure in Project Settings > Ownership Rules:

# .sentry/ownership-rules
path:src/payments/**     #payments-team
path:src/auth/**         #platform-team
url:*/api/v1/payments/*  #payments-team
tags.service:payment-api #payments-team
*                        #platform-team

Alert tiers:

  • P0 Critical: error rate > 50/min OR crash-free < 95% — PagerDuty on-call, 15 min SLA
  • P1 Warning: new production issue or regression — Slack #alerts-prod, same-day SLA
  • P2 Performance: P95 > 2s or Apdex < 0.7 — Slack #alerts-perf, next sprint
  • P3 Info: new staging issue — Slack #alerts-staging, backlog triage

Step 6 — Source Map Uploads (Monorepo)

#!/usr/bin/env bash
# scripts/upload-sourcemaps.sh — run in CI after build
set -euo pipefail
SERVICE="${1:?Usage: upload-sourcemaps.sh <service>}"
RELEASE="${SERVICE}@$(git rev-parse --short HEAD)"

npx @sentry/cli releases new "$RELEASE" --org "$SENTRY_ORG" --project "$SERVICE"
npx @sentry/cli sourcemaps upload --org "$SENTRY_ORG" --project "$SERVICE" \
  --release "$RELEASE" --url-prefix "~/" --validate "./services/${SERVICE}/dist/"
npx @sentry/cli releases set-commits "$RELEASE" --org "$SENTRY_ORG" --auto
npx @sentry/cli releases finalize "$RELEASE" --org "$SENTRY_ORG"

Webpack plugin alternative (auto-uploads on build):

import { sentryWebpackPlugin } from '@sentry/webpack-plugin';
export default {
  devtool: 'source-map',
  plugins: [sentryWebpackPlugin({
    org: process.env.SENTRY_ORG,
    project: 'web-app',
    authToken: process.env.SENTRY_AUTH_TOKEN,
    sourcemaps: { filesToDeleteAfterUpload: ['./dist/**/*.map'] },
  })],
};

Step 7 — Custom Integrations

Wrap internal SDK calls with spans for tracing visibility:

import * as Sentry from '@sentry/node';
export function withSpan<T>(op: string, desc: string, fn: () => Promise<T>, attrs?: Record<string, string | number>): Promise<T> {
  return Sentry.startSpan({ name: desc, op, attributes: attrs }, async (span) => {
    try { const r = await fn(); span.setStatus({ code: 1, message: 'ok' }); return r; }
    catch (e) { span.setStatus({ code: 2, message: String(e) }); throw e; }
  });
}
// Usage: await withSpan('payment.charge', 'charge $50', () => gateway.charge(5000, 'usd', custId));

Step 8 — SentryService Wrapper

Production wrapper with singleton, metrics, and graceful shutdown:

// lib/sentry-service.ts
import * as Sentry from '@sentry/node';

export class SentryService {
  private static instance: SentryService | null = null;
  private initialized = false;

  private constructor(private config: { serviceName: string; dsn: string; version?: string }) {}

  static getInstance(config?: { serviceName: string; dsn: string; version?: string }): SentryService {
    if (!SentryService.instance) {
      if (!config) throw new Error('Config required on first call');
      SentryService.instance = new SentryService(config);
    }
    return SentryService.instance;
  }

  init(): void {
    if (this.initialized) return;
    const { initSentry } = require('./sentry');
    initSentry(this.config);
    this.initialized = true;
  }

  captureError(error: Error, ctx?: { tags?: Record<string, string>; extra?: Record<string, unknown>; level?: Sentry.SeverityLevel }): string {
    return Sentry.withScope((scope) => {
      if (ctx?.tags) Object.entries(ctx.tag

---

*Content truncated.*

When not to use it

  • When managing fewer than 3 services with a single team

Prerequisites

Sentry organization at sentry.io@sentry/node v8+Service inventory and team ownership documentationNode.js 18+

Limitations

  • Alert noise exceeds one team in shared projects
  • Ownership rules mismatch can route alerts to wrong teams

How it compares

This approach uses a shared configuration module to enforce consistency across services rather than manual, per-service initialization.

Compared to similar skills

sentry-reference-architecture side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
sentry-reference-architecture (this skill)026dReviewAdvanced
software-architecture3336moNo flagsIntermediate
architect-review1094moNo flagsAdvanced
mcp-builder1363moReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jeremylongshore

View all by jeremylongshore

analyzing-logs

jeremylongshore

Analyze application logs to detect performance issues, identify error patterns, and improve stability by extracting key insights.

14123

ollama-setup

jeremylongshore

Configure auto-configure Ollama when user needs local LLM deployment, free AI alternatives, or wants to eliminate hosted API costs. Trigger phrases: "install ollama", "local AI", "free LLM", "self-hosted AI", "replace OpenAI", "no API costs". Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

1167

backtesting-trading-strategies

jeremylongshore

Backtest crypto and traditional trading strategies against historical data. Calculates performance metrics (Sharpe, Sortino, max drawdown), generates equity curves, and optimizes strategy parameters. Use when user wants to test a trading strategy, validate signals, or compare approaches. Trigger with phrases like "backtest strategy", "test trading strategy", "historical performance", "simulate trades", "optimize parameters", or "validate signals".

1071

generating-database-seed-data

jeremylongshore

Process this skill enables AI assistant to generate realistic test data and database seed scripts for development and testing environments. it uses faker libraries to create realistic data, maintains relational integrity, and allows configurable data volumes. u... Use when working with databases or data models. Trigger with phrases like 'database', 'query', or 'schema'.

1033

cursor-codebase-indexing

jeremylongshore

Execute set up and optimize Cursor codebase indexing. Triggers on "cursor index setup", "codebase indexing", "index codebase", "cursor semantic search". Use when working with cursor codebase indexing functionality. Trigger with phrases like "cursor codebase indexing", "cursor indexing", "cursor".

885

testing-mobile-apps

jeremylongshore

Execute mobile app testing on iOS and Android devices/simulators. Use when performing specialized testing. Trigger with phrases like "test mobile app", "run iOS tests", or "validate Android functionality".

810

You might also like

software-architecture

davila7

Guide for quality focused software architecture. This skill should be used when users want to write code, design architecture, analyze code, in any case that relates to software development.

333868

architect-review

sickn33

Master software architect specializing in modern architecture patterns, clean architecture, microservices, event-driven systems, and DDD. Reviews system designs and code changes for architectural integrity, scalability, and maintainability. Use PROACTIVELY for architectural decisions.

109320

mcp-builder

anthropics

Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).

136215

solid-principles

SmidigStorm

Enforce SOLID principles (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) in object-oriented design. Use when writing or reviewing classes and modules.

57236

codex

Lucklyric

Invoke Codex CLI for complex coding tasks requiring high reasoning capabilities. This skill should be invoked when users explicitly mention "Codex", request complex implementation challenges, advanced reasoning, or need high-reasoning model assistance. Automatically triggers on codex-related requests and supports session continuation for iterative development.

32238

architecture-patterns

wshobson

Implement proven backend architecture patterns including Clean Architecture, Hexagonal Architecture, and Domain-Driven Design. Use when architecting complex backend systems or refactoring existing applications for better maintainability.

55214

Search skills

Search the agent skills registry