Community코딩 & 개발github.com

관찰 가능성 및 계측

프로덕션 동작을 시각화하고 진단할 수 있도록 코드를 계측합니다. 로깅, 메트릭, 트레이싱 또는 알림을 추가할 때 사용하세요. 프로덕션에서 실행되는 기능을 출시하고 작동 여부를 증명해야 할 때 사용하세요. 프로덕션 문제가 보고되었지만 사용 가능한 데이터로 무슨 일이 일어났는지 알 수 없을 때 사용하세요.

관찰 가능성 및 계측란 무엇인가요?

이 기술은 관찰 가능성과 계측에 대한 포괄적인 가이드를 제공하여, 개발자가 프로덕션 시스템을 효과적으로 이해하고 진단할 수 있도록 합니다. 코드를 작성할 때 계측을 기능과 함께 설계하고, 구조화된 로깅, 메트릭, 분산 트레이싱을 사용하여 시스템 동작에 대한 명확한 질문에 답하는 방법을 강조합니다. Claude Code, Cursor, Codex 등의 AI 에이전트와 함께 사용하여 코드 생성 및 검토 중에 자동으로 관찰 가능성 모범 사례를 적용할 수 있습니다. 이 기술은 RED(속도, 오류, 지속 시간) 및 USE(사용률, 포화도, 오류) 메트릭 프레임워크, 증상 기반 알림, 카디널리티 관리, 상관관계 ID 전파와 같은 주요 개념을 다루며, 모든 프로덕션 기능이 출시와 함께 적절한 텔레메트리를 갖추도록 보장합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/addyosmani/agent-skills/tree/main/skills/observability-and-instrumentation

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

관찰 가능성 및 계측은(는) 무엇을 하나요?

개요

관찰할 수 없는 코드는 운영할 수 없는 코드입니다. 관찰 가능성(Observability)은 코드가 내보내는 텔레메트리를 사용하여 외부에서 "시스템이 무엇을 하고 있으며 왜 그런가?"라는 질문에 답할 수 있는 능력입니다. 계측(Instrumentation)은 출시 후에 추가하는 기능이 아니라, 테스트와 마찬가지로 기능과 함께 작성됩니다. 텔레메트리 없이 기능이 출시되면, 첫 번째 사용자 신고 버그는 질의(Query)가 아닌 고고학(Archaeology)이 됩니다.

사용 시기

  • 프로덕션에서 실행될 모든 기능 구축
  • 새 서비스, 엔드포인트, 백그라운드 작업 또는 외부 통합 추가
  • 프로덕션 장애 진단에 너무 오랜 시간이 걸린 경우 ("무슨 일이 일어났는지 알 수 없었습니다")
  • 알림 규칙 설정 또는 검토
  • I/O, 재시도, 큐 또는 서비스 간 호출을 추가하는 PR 검토

사용하지 말아야 할 경우:

  • 지금 당장 발생하는 장애 진단 — 디버깅-및-오류-복구 기술 사용 (관찰 가능성은 다음 번에 해당 기술을 빠르게 만드는 요소입니다)
  • 측정된 느린 속도 프로파일링 및 최적화 — 성능-최적화 기술 사용
  • 출시일 모니터링 체크리스트 및 롤백 트리거 — 출시-및-런칭 기술 참조; 이 기술은 이를 지원하는 계측을 다룹니다

프로세스

1. 계측하기 전에 "정상 작동"을 정의하세요

질문 없는 텔레메트리는 잡음입니다. 계측을 추가하기 전에, 온콜 엔지니어가 이 기능에 대해 물어볼 2~4가지 질문을 적어보세요:

기능: 결제 재시도
온콜이 물어볼 질문:
1. 첫 번째 시도 대 재시도 후 결제 성공 비율은 얼마인가?
2. 결제가 영구적으로 실패할 때, 이유는 무엇인가? (공급자 오류? 시간 초과? 유효성 검사?)
3. 결제 공급자가 평소보다 느린가?
→ 아래 모든 신호는 이 중 하나에 답하는 데 도움이 되어야 합니다.

질문을 명명할 수 없다면, 계측할 준비가 되지 않은 것입니다 — 모든 것을 로깅하고 아무것도 배우지 못할 것입니다.

2. 각 질문에 적합한 신호를 선택하세요

신호답변비용 프로필예시
구조화된 로그"이 특정 경우에 무슨 일이 일어났는가?"이벤트 당; 트래픽에 따라 증가payment_failed (공급자 오류 코드 포함)
메트릭"전체적으로 얼마나 자주 / 얼마나 빠른가?"시리즈 당 고정; 쿼리 비용 저렴공급자 호출의 p99 지연 시간
트레이스"서비스 간에 시간은 어디서 소비되었는가?"요청 당; 일반적으로 샘플링됨각 홉별로 분석된 하나의 느린 체크아웃

경험 법칙: 메트릭은 무언가 잘못되었음을 알려주고, 트레이스는 어디서 잘못되었는지 알려주며, 로그는 왜 잘못되었는지 알려줍니다.

3. 구조화된 로깅

산문이 아닌 이벤트를 로깅하세요. 모든 로그 라인은 안정적인 이벤트 이름과 기계가 읽을 수 있는 필드를 가진 JSON 객체입니다:

// 나쁨: 문자열 보간 — 쿼리 불가능, 일관성 없음
logger.info(`결제 ${id}가 사용자 ${userId}에 대해 ${n}회 재시도 후 실패했습니다`);

// 좋음: 안정적인 이벤트 이름 + 구조화된 필드
logger.warn({
  event: 'payment_failed',
  paymentId: id,
  provider: 'stripe',
  errorCode: err.code,
  attempt: n,
}, '결제 실패');

로그 레벨 — 일관되게 사용하세요:

레벨의미온콜 조치
error불변 조건 위반; 누군가 조치를 취해야 할 수 있음조사
warn저하되었지만 처리됨 (재시도 성공, 대체 수단 사용됨)추세 관찰
info중요한 비즈니스 이벤트 (주문 접수, 작업 완료)없음
debug진단 세부 정보프로덕션에서는 기본적으로 꺼짐

상관관계 ID는 필수입니다. 시스템 경계에서 요청 ID를 생성(또는 수락)하고 모든 로그 라인, 스팬 및 외부 호출에 첨부하세요. 이것이 없으면, 뒤섞인 로그에서 단일 요청을 재구성할 수 없습니다:

// Express: 요청 당 하위 로거, ID가 다운스트림으로 전파됨
app.use((req, res, next) => {
  req.id = req.headers['x-request-id'] ?? crypto.randomUUID();
  req.log = logger.child({ requestId: req.id });
  res.setHeader('x-request-id', req.id);
  next();
});

여러 진입점이 하나의 로그에 기록하는 경우, 진입점 이름을 지정하세요. 상관관계 ID는 실행(run)을 식별합니다. 어떤 코드 경로가 시작했는지는 알려주지 않습니다. 스케줄러, 재생 엔드포인트 및 수동 CLI 실행에 의해 도달된 동일한 작업은 하나의 싱크에서 교환 가능한 라인을 생성하므로, 라인을 속성화하는 것은 제거(elimination)에 의존합니다 — 스케줄러 기록, 프로세스 테이블, 배포 로그를 교차 참조 — 그리고 그 주장은 해당 외부 레코드가 우연히 여전히 존재하는 한에만 유효합니다. 실행이 시작되는 곳에 진입점을 상관관계 ID 옆에 스탬프하고, 둘 다 동일한 방식으로 전파하세요:

// 모든 진입점에 대한 하나의 헬퍼: 실행 자체 로거가 두 필드를 모두 전달합니다.
// `source`가 아닌 `entryPoint` — ECS는 `source.*`를 네트워크 필드용으로 예약합니다.
export const runLog = (entryPoint: 'scheduler' | 'replay_endpoint' | 'cli', runId: string) =>
  logger.child({ entryPoint, requestId: runId });

// scheduler 틱        -> runLog('scheduler', crypto.randomUUID())
// POST /jobs/:id/replay -> runLog('replay_endpoint', req.id)
// CLI 호출            -> runLog('cli', process.env.RUN_ID ?? crypto.randomUUID())

두 필드 모두 상관관계 ID와 동일한 경계(큐 메타데이터, HTTP 헤더)를 넘어야 합니다. 그렇지 않으면 작업자가 진입점을 다시 유추하여 추측하게 됩니다. 진입점과 단지 상관관계가 있는 필드는 속성이 아닌 힌트에 불과합니다. 작업을 호출할 수 있는 모든 것은 이를 재현할 수 있습니다.

비밀, 토큰, 비밀번호 또는 전체 PII를 절대 로깅하지 마세요. 이는 보안-및-강화 기술의 엄격한 규칙입니다 — 텔레메트리 파이프라인은 고전적인 데이터 유출 경로입니다. 필드를 허용 목록(allowlist)에 추가하고, 전체 요청 본문을 로깅하지 마세요.

4. 메트릭

요청 기반 서비스의 경우, 모든 엔드포인트와 모든 외부 종속성에 대해 RED를 계측하세요: 속도(Rate) (요청/초), 오류(Errors) (실패율), 지속 시간(Duration) (지연 시간 히스토그램, 평균 아님). 리소스(큐, 풀, 호스트)의 경우 USE를 사용하세요: 사용률(Utilization), 포화도(Saturation), 오류(Errors).

트레이싱과 마찬가지로, 벤더 중립적인 경로는 OpenTelemetry 메트릭 API입니다(5단계와 동일한 SDK 및 컨텍스트). 아래 예제는 Prometheus의 prom-client를 사용합니다 — 일반적인 백엔드 선택 중 하나일 뿐, 유일한 것은 아닙니다; RED/USE 및 카디널리티 규칙은 어떤 방식으로든 동일합니다.

import { Histogram } from 'prom-client';

const httpDuration = new Histogram({
  name: 'http_request_duration_seconds',
  help: 'HTTP 요청 지속 시간',
  labelNames: ['method', 'route', 'status_class'],  // '200'이 아닌 '2xx'
  buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
});

카디널리티는 실패 지점입니다. 모든 고유 레이블 조합은 별도의 시계열입니다. 레이블은 작고 고정된 집합(라우트 템플릿, 상태 클래스, 공급자 이름)에서 가져와야 합니다. 사용자 ID, 원시 URL, 오류 메시지 또는 기타 무제한 값을 레이블로 절대 사용하지 마세요 — 이는 로그와 트레이스에 속합니다.

레이블로 OK:    route="/api/tasks/:id"   status_class="5xx"   provider="stripe"
절대 레이블 불가:  user_id, email, request_id, 전체 URL, 오류 메시지 텍스트

평균을 추적하지 말고, 항상 백분위수를 추적하세요: 평균은 끔찍한 경험을 하는 1%의 사용자를 숨깁니다. 히스토그램을 사용하고 p50/p95/p99를 읽으세요.

5. 분산 트레이싱

OpenTelemetry를 사용하세요 — 벤더 중립적인 표준이며, 자동 계측은 HTTP, gRPC 및 일반적인 DB 클라이언트를 거의 코드 없이 다룹니다:

// tracing.ts — 다른 무엇보다 먼저 가져와야 함
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  serviceName: 'checkout-service',
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();

의미 있는 내부 작업 단위(예: applyDiscounts, chargeProvider) 주변에만 수동 스팬을 추가하고 온콜이 필터링할 속성을 첨부하세요. 모든 비동기 경계(HTTP 헤더, 큐 메시지 메타데이터)를 통해 컨텍스트를 전파하세요. 그렇지 않으면 트레이스가 간격에서 끊어집니다. 기본적으로 낮은 비율로 헤드 기반 샘플링을 수행하고, 백엔드가 테일 샘플링을 지원하는 경우 오류의 100%를 유지하세요.

6. 알림

사용자가 느끼는 증상에 대해 알리고, 원인에 대해 알리지 마세요:

증상 (페이지 대상):           원인 (대시보드, 페이지 아님):
오류율 > 1% (5분 동안)        CPU 85%
p99 지연 시간 > 2초           한 포드 재시작
큐 대기 시간 > 10분            디스크 70%

원인 기반 알림은 아무 문제가 없을 때 발동하고 예측하지 못한 실패를 놓칩니다. 증상 기반 알림은 원인에 관계없이 사용자가 피해를 입을 때 정확히 발동합니다.

생성하는 모든 알림에 대한 규칙:

  1. 실행 가능해야 합니다. 응답이 "무시하세요, 자체 치유됩니다"라면 알림을 삭제하세요.
  2. 런북에 연결되어야 합니다. — 3줄이라도 충분: 의미, 첫 번째로 실행할 쿼리, 에스컬레이션 경로.
  3. SLO 또는 과거 데이터로 정당화된 임계값과 기간이 있어야 하며, 추측이 아니어야 합니다.
  4. 두 가지 심각도만 사용하세요: 페이지 (사용자 대상, 지금 조치) 및 티켓 (저하, 이번 주에 조치). 세 번째 계층은 사람들이 모든 것을 무시하도록 훈련시키는 잡음이 됩니다.

런북 작성

위의 규칙 2는 모든 알림이 런북에 연결되어야 함을 요구합니다. 런북의 임무는 독자가 생각할 필요 없이 세 가지 질문에 답하는 것입니다: 무슨 일이 일어나고 있는지, 먼저 확인할 것은 무엇인지, 그리고 그것이 해결되지 않으면 누구에게 연락해야 하는지. docs/runbooks/에 알림 이름으로 저장하세요.

최소 실행 가능 런북 (세 줄):

# Runbook: /api/tasks 높은 오류율
**의미:** DB 연결 풀이 고갈되었거나, 잘못된 배포일 가능성이 높습니다.
**먼저 확인:** `SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend';`
  — 카운트가 풀 제한을 초과하면 2단계를 참조하세요. (데이터베이스에 맞는 동등한 명령어로 대체하세요.)
**에스컬레이션:** #db-oncall 또는 엔지니어링 온콜 로테이션.

세 줄을 넘어 확장해야 하는 경우: 첫 번째 확인만으로 결정을 내리기에 충분하지 않을 때만 단계를 추가하세요. 가장 일반적인 세 가지 원인을 다루는 5단계 런북이 모든 에지 케이스를 다루고 대충 훑어보게 되는 20단계 문서보다 낫습니다.

런북을 최신 상태로 유지하세요. 런북이 사용된 모든 인시던트를 종료하는 과정의 일부로 런북을 업데이트하세요 — 오래된 런북은 잘못된 확신을 심어줍니다. 단계가 잘못되었거나 누락된 경우, 인시던트를 해결됨으로 표시하기 전에 수정하세요.

7. 텔레메트리 자체를 검증하세요

계측은 코드입니다; 잘못될 수 있습니다. 작업이 완료되었다고 판단하기 전에, 경로를 트리거하고 실제 출력을 살펴보세요:

  • 스테이징에서 강제로 오류 발생 → requestId로 로그에서 찾고, 필드가 구조화되었는지 확인 ([object Object] 아님)
  • 테스트 트래픽 전송 → 예상 레이블과 정상적인 값으로 메트릭 시리즈가 나타나는지 확인
  • 트레이싱 UI에서 서비스 간 하나의 요청 추적 → 끊어진 스팬 없음
  • 각 새 알림을 한 번씩 발동 (임시로 임계값 낮춤) → 올바른 채널에 도달하고 런북 링크가 작동하는지 확인

일반적인 합리화

합리화현실
"작동한 다음에 로깅을 추가하겠습니다""다음"은 "첫 번째 인시던트 후"가 되며, 이는 자신이 눈멀었다는 것을 발견하는 가장 비용이 많이 드는 순간입니다. 구축하면서 계측하세요.
"더 많은 로그 = 더 나은 관찰 가능성"구조화되지 않은 잡음은 인시던트를 더 빠르게 만들지 않고 더 느리게 만듭니다. 질의 가능한 이벤트 3개가 300줄의 산문보다 낫습니다.
"console.log로도 충분합니다"구조화되지 않은 출력은 필터링, 상관 관계 파악 또는 알림 설정이 불가능합니다. 구조화된 로거는 한 번 5분만 더 투자하면 됩니다.
"무언가 고장 나면 대시보드만 보면 됩니다"정의된 질문 없이 구축된 대시보드는 답을 제외한 모든 것을 보여줍니다. 온콜 질문에서 시작하세요.
"중요한 모든 것에 알림을 설정하고, 나중에 조정하겠습니다"시끄러운 호출기는 사람들이 무시하도록 훈련시킵니다. 조정은 결코 일어나지 않습니다; 놓친 실제 호출기는 발생합니다.
"사용자 ID를 메트릭 레이블로 사용하면 디버깅이 더 쉬워집니다"또한 메트릭 백엔드를 다운시킵니다. 높은 카디널리티 조회는 로그와 트레이스에 속합니다.
"두 서비스에 트레이싱은 과잉입니다"두 서비스는 이미 로그가 답할 수 없는 서비스 간 지연 시간 질문을 의미합니다. 자동 계측은 비용을 사소하게 만듭니다.

위험 신호

  • 재시도, 큐 또는 외부 호출이 있는 기능 PR에 새 텔레메트리가 전혀 없는 경우
  • 문자열 보간 대신 구조화된 필드로 구축된 로그 라인
  • 상관관계/요청 ID 없음 — 각 로그 라인이 고아임
  • 스케줄러, 웹훅 및 수동 실행에 의해 공급되는 하나의 로그 스트림에 어떤 것이 라인을 생성했는지 식별하는 필드가 없는 경우
  • 사용자 ID, 원시 URL 또는 오류 메시지 텍스트로 레이블이 지정된 메트릭 (카디널리티 폭탄)
  • 백분위수 없이 평균으로 추적되는 지연 시간
  • 매일 발동되고 조치 없이 승인되는 알림
  • 사용자 대상 오류율은 모니터링되지 않으면서 사람들에게 페이지를 보내는 원인(CPU, 메모리)에 대한 알림
  • 로그에 나타나는 비밀, 토큰 또는 전체 요청 본문
  • 프로덕션 기능이 정상이라는 유일한 증거로 "제 컴퓨터에서는 잘 됩니다"

검증

기능을 계측한 후 다음을 확인하세요:

  • 이 기능에 대한 온콜 질문이 기록되었고, 각 신호가 하나에 매핑됨
  • 모든 로그 출력이 구조화(JSON)되었으며, 안정적인 이벤트 이름과 모든 라인에 상관관계 ID가 있음
  • 둘 이상의 진입점에서 기록된 모든 로그 싱크에는 실행이 시작되는 곳에 설정되고 상관관계 ID와 함께 전파되며 다운스트림에서 유추되지 않은 entry-point 필드가 있음
  • 어떤 로그 라인에도 비밀, 토큰 또는 편집되지 않은 PII가 없음 (실제 출력 스팟 체크)
  • 모든 새 엔드포인트와 모든 외부 종속성에 대해 RED 메트릭이 있으며, 레이블 집합이 제한됨
  • 지연 시간이 히스토그램이며, p95/p99를 질의할 수 있음
  • 단일 요청을 트레이싱 UI에서 끊어진 스팬 없이 종단 간 추적할 수 있음
  • 모든 새 알림은 증상 기반이며, 런북 링크가 있고, 한 번 테스트 발동됨
  • 스테이징에서 유도된 실패가 소스 코드를 읽지 않고 텔레메트리만으로 찾을 수 있음

이 목록의 한눈에 보기 버전(출시 전 계측 게이트 포함)은 ../../references/observability-checklist.md를 참조하세요.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

addyosmani/agent-skills

Builds production-quality, accessible, responsive user-facing UIs. Use when building or modifying interfaces and pages, creating components, implementing layouts, meeting WCAG accessibility requirements, managing state, or when the output needs to look and feel production-quality rather than AI-generated.

addyosmani/agent-skills

Prepares production launches. Use when preparing to deploy to production, or when asking what needs to be in place before shipping. Use when you need a pre-launch checklist, when setting up monitoring, when planning a staged rollout, or when you need a rollback strategy.

addyosmani/agent-skills

Guides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints, defining type contracts between modules, or establishing boundaries between frontend and backend.

addyosmani/browser-testing-with-devtools

Tests in real browsers via Chrome DevTools MCP. Use when building or debugging anything that runs in a browser. Use when you need to inspect the DOM, capture console errors, analyze network requests, profile performance, or verify visual output with real runtime data. Requires the chrome-devtools MCP server to be configured.

addyosmani/ci-cd-and-automation

Automates CI/CD pipeline setup. Use when setting up or modifying build and deployment pipelines. Use when you need to automate quality gates, configure test runners in CI, or establish deployment strategies.

addyosmani/code-review-and-quality

Conducts multi-axis code review. Use before merging any change. Use when reviewing code written by yourself, another agent, or a human. Use when you need to assess code quality across multiple dimensions before it enters the main branch.

addyosmani/code-simplification

Simplifies code for clarity. Use when refactoring code for clarity without changing behavior. Use when code works but is harder to read, maintain, or extend than it should be. Use when reviewing code that has accumulated unnecessary complexity.

addyosmani/constraint-driven-development

Establishes a project's quality bar as a written contract and stops agents quietly lowering it. Interviews the user on which dimensions matter, supplies sane default thresholds when they have no number in mind, records everything in CONSTRAINTS.md, and watches the diff for a weakened bar — new @ts-ignore or eslint-disable suppressions, skipped or deleted tests, assertions stripped out, unimplemented stubs, thresholds edited down. Use when no quality bar is written down, when the user says "set up constraints" or "define our standards", when the user wants dimensions they care about — accessibility, web performance, coverage — set up as enforced constraints, when an agent keeps silencing checks or skipping tests to get to green, when you need a coverage or performance threshold and don't know what number to pick, or when an agent writes more code than anyone will read.

addyosmani/debugging-and-error-recovery

Guides systematic root-cause debugging. Use when tests fail, builds break, behavior doesn't match expectations, or you encounter any unexpected error. Use when you need a systematic approach to finding and fixing the root cause rather than guessing.

addyosmani/deprecation-and-migration

Manages deprecation and migration. Use when removing old systems, APIs, or features. Use when migrating users from one implementation to another. Use when deciding whether to maintain or sunset existing code.

addyosmani/documentation-and-adrs

Records decisions and documentation. Use when making architectural decisions, changing public APIs, shipping features, or when you need to record context that future engineers and agents will need to understand the codebase.

addyosmani/doubt-driven-development

Subjects every non-trivial decision to a fresh-context adversarial review before it stands. Use when correctness matters more than speed, when working in unfamiliar code, when stakes are high (production, security-sensitive logic, irreversible operations), or any time a confident output would be cheaper to verify now than to debug later.

addyosmani/git-workflow-and-versioning

Structures git workflow practices. Use when making any code change. Use when committing, branching, resolving conflicts, or when you need to organize work across multiple parallel streams.

addyosmani/idea-refine

Refines raw ideas into sharp, actionable concepts through structured divergent and convergent thinking. Use when an idea is still vague, when you need to stress-test assumptions before committing to a plan, or when you want to expand options before converging on one. Triggers on "ideate", "refine this idea", or "stress-test my plan".

addyosmani/incremental-implementation

Delivers changes incrementally. Use when implementing any feature or change that touches more than one file. Use when you're about to write a large amount of code at once, or when a task feels too big to land in one step.

addyosmani/interview-me

Extracts what the user actually wants instead of what they think they should want. Achieves this through one-question-at-a-time interview until ~95% confidence about the underlying intent. Use when an ask is underspecified ("build me X" without "for whom" or "why now"), when the user explicitly invokes ("interview me", "grill me", "are we sure?", "stress-test my thinking"), or when you catch yourself silently filling in ambiguous requirements before any plan, spec, or code exists.

addyosmani/performance-optimization

Optimizes application performance. Use when performance requirements exist, when you suspect performance regressions, or when Core Web Vitals or load times need improvement. Use when profiling reveals bottlenecks that need fixing.

addyosmani/planning-and-task-breakdown

Breaks work into ordered tasks. Use when you have a spec or clear requirements and need to break work into implementable tasks. Use when a task feels too large to start, when you need to estimate scope, or when parallel work is possible.

addyosmani/security-and-hardening

Hardens code against vulnerabilities. Use when handling user input, authentication, data storage, or external integrations. Use when building any feature that accepts untrusted data, manages user sessions, or interacts with third-party services.

addyosmani/source-driven-development

Grounds every implementation decision in official documentation. Use when you want authoritative, source-cited code free from outdated patterns. Use when building with any framework or library where correctness matters.

관련 스킬