<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rafa Gomes</title>
    <description>The latest articles on DEV Community by Rafa Gomes (@rafagomes).</description>
    <link>https://dev.to/rafagomes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1870200%2F2fa09815-9f93-4a4e-885f-705f9d006918.jpg</url>
      <title>DEV Community: Rafa Gomes</title>
      <link>https://dev.to/rafagomes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rafagomes"/>
    <language>en</language>
    <item>
      <title>We instrumented Claude Code for our team. Here's what the data showed</title>
      <dc:creator>Rafa Gomes</dc:creator>
      <pubDate>Thu, 05 Nov 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/rafagomes/we-instrumented-claude-code-for-our-team-heres-what-the-data-showed-mo9</link>
      <guid>https://dev.to/rafagomes/we-instrumented-claude-code-for-our-team-heres-what-the-data-showed-mo9</guid>
      <description>&lt;p&gt;AI coding only started paying off for my team when we stopped treating it as a tool everyone uses their own way and started treating it as a process we could measure.&lt;/p&gt;

&lt;p&gt;That took two changes. First, we moved from ad hoc prompting to a spec-driven workflow, where agents build from written specs and a separate agent reviews the work. Second, we instrumented every Claude Code session with OpenTelemetry and built our own dashboards for cost and usage, with no SaaS involved.&lt;/p&gt;

&lt;p&gt;This post covers both: the workflow, the telemetry stack, the design decisions that mattered, and what the data told us that we did not know before.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Generic AI usage made output depend on who was typing, and nobody could say what it cost.&lt;/li&gt;
&lt;li&gt;Spec-driven delivery gave agents real context and split the writer from the reviewer.&lt;/li&gt;
&lt;li&gt;Claude Code already emits OpenTelemetry. An OTel Collector, Prometheus, Loki and Grafana were enough to see cost per developer, team and skill.&lt;/li&gt;
&lt;li&gt;Measure cost, not tokens. Don't capture prompts.&lt;/li&gt;
&lt;li&gt;The data exposed concentrated spend, uneven adoption, and expensive skills we could fix.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Phase 1: generic AI
&lt;/h2&gt;

&lt;p&gt;Every engineer had an AI coding assistant, and every engineer used it differently.&lt;/p&gt;

&lt;p&gt;Some wrote long prompts, some pasted a ticket and hoped. Agents guessed at context the codebase never gave them. The same task could come back clean from one person and half-broken from another, and reviews caught the difference late.&lt;/p&gt;

&lt;p&gt;The bigger problem was that we were flying blind. We could not answer basic questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who is actually using this, and for what?&lt;/li&gt;
&lt;li&gt;What does it cost, per person or per feature?&lt;/li&gt;
&lt;li&gt;Is it making us faster, or just busier?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a shared process there was nothing to improve, and without data there was nothing to measure. So we fixed the process first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: spec-driven, not prompt-driven
&lt;/h2&gt;

&lt;p&gt;The core idea is simple: documents flow downstream, so agents implement with full context instead of guessing. I packaged the process we use as an open-source Claude Code plugin, &lt;a href="https://github.com/rafagomes/ai-workflow" rel="noopener noreferrer"&gt;ai-workflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A feature moves through five stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plan.&lt;/strong&gt; A PRD, the architecture, an optional threat model and ADRs, then a roadmap. Each feature becomes a spec with explicit verification criteria. Specs that grow past roughly 500 source lines get sliced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design (optional).&lt;/strong&gt; Screens are designed, and the running UI is later verified against them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build.&lt;/strong&gt; One agent implements one spec slice, with tests. Bugs get a root-cause diagnosis before a fix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship.&lt;/strong&gt; Changes are split into conventional commits and opened as a draft PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review.&lt;/strong&gt; A separate reviewer agent checks the PR before it is marked ready, and a human merges.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The reviewer is the part that changed quality the most. It runs in a fresh context, read-only, on a stronger model than the writers. It reviews the diff against the spec for compliance, correctness, boundary security, test quality and convention drift. It runs the project's checks itself and returns &lt;code&gt;PASS&lt;/code&gt; or &lt;code&gt;FIX_REQUIRED&lt;/code&gt;, with findings graded &lt;code&gt;HIGH&lt;/code&gt;, &lt;code&gt;MED&lt;/code&gt; or &lt;code&gt;LOW&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The loop has hard limits so it cannot spin forever:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HIGH&lt;/code&gt; and &lt;code&gt;MED&lt;/code&gt; findings get fixed; &lt;code&gt;LOW&lt;/code&gt; findings are listed but never block.&lt;/li&gt;
&lt;li&gt;At most two re-review cycles. If the loop does not converge, the PR stays a draft for a human.&lt;/li&gt;
&lt;li&gt;Security-sensitive diffs also get a dedicated security review first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The principle behind it: &lt;strong&gt;the writer never grades its own work.&lt;/strong&gt; An agent reviewing code it just wrote, in the same context, tends to agree with itself.&lt;/p&gt;

&lt;p&gt;A few conventions hold it together: trunk-based development with short-lived branches, one PR per concern, spec before implementation, and verification before anyone claims a task is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3: telemetry, no SaaS
&lt;/h2&gt;

&lt;p&gt;Claude Code already emits OpenTelemetry natively, so we did not need a third-party product to see how AI was being used. We needed a collector, two stores and Grafana.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdrriprw0xgc1ngzyxwj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdrriprw0xgc1ngzyxwj5.png" alt="Telemetry pipeline: Claude Code and Codex CLI send OTLP to an OTel Collector. The collector routes metrics to Prometheus and events to Loki, Grafana Alloy ships pod logs to Loki, and Grafana reads both stores." width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sessions send metrics and events over OTLP to a collector, which splits metrics into Prometheus and events into Loki; Grafana reads both.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Emitter&lt;/td&gt;
&lt;td&gt;Claude Code (native OpenTelemetry)&lt;/td&gt;
&lt;td&gt;Exports metrics and events per developer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collection&lt;/td&gt;
&lt;td&gt;OTel Collector (contrib)&lt;/td&gt;
&lt;td&gt;Receives OTLP, drops session-level labels, routes to storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics&lt;/td&gt;
&lt;td&gt;Prometheus&lt;/td&gt;
&lt;td&gt;Stores usage and cost metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs&lt;/td&gt;
&lt;td&gt;Loki&lt;/td&gt;
&lt;td&gt;Stores activity events and pod logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pod logs&lt;/td&gt;
&lt;td&gt;Grafana Alloy&lt;/td&gt;
&lt;td&gt;Ships container logs to Loki&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visualization&lt;/td&gt;
&lt;td&gt;Grafana&lt;/td&gt;
&lt;td&gt;Developer Analytics and Developer Detail dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;How it runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Activation lives in the repo.&lt;/strong&gt; &lt;code&gt;CLAUDE_CODE_ENABLE_TELEMETRY=1&lt;/code&gt; and the OTLP endpoint sit in a versioned &lt;code&gt;.claude/settings.json&lt;/code&gt;. Anyone who opens a session in the repo exports by default, with nothing to install.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private by design.&lt;/strong&gt; Access goes through a Tailscale ingress, with no public load balancer. The tailnet is the authentication boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitOps.&lt;/strong&gt; Everything deploys with Argo CD and Helm into its own namespace on our Kubernetes cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets stay out of manifests.&lt;/strong&gt; External Secrets Operator pulls them from the cloud secrets manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The running cost is a few small pods and some disk. Dashboards slice the data per developer, per team, per skill and by cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design decisions that mattered
&lt;/h2&gt;

&lt;p&gt;The stack itself is standard. These choices are what made the data trustworthy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure cost, not tokens.&lt;/strong&gt; Raw token counts look impressive and mean little. The vast majority of tokens in an agentic session are cache reads, which are cheap. A developer with a huge token count can be cheaper than one with a modest count. Cost is the metric that tracks real usage, so every dashboard leads with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estimate plan limits from cost.&lt;/strong&gt; Claude Code does not export how close a user is to their plan limit. We use cost as a proxy, calibrated against a reading from the status bar. It is an approximation, it drifts near the reset window, and the dashboard says so. A labelled estimate beats no signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control cardinality early.&lt;/strong&gt; Each session carries its own instance id. Left alone, every new session would create a new time series in Prometheus. The collector drops &lt;code&gt;service.instance.id&lt;/code&gt; before export, so series count grows with people and skills, not with sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't capture prompt content.&lt;/strong&gt; OpenTelemetry can carry prompts. We chose not to. Capturing what engineers type into an AI assistant is an HR and legal decision, not a config toggle, and we treated it that way. Metrics and activity events were enough to answer every question we had.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep it vendor-neutral.&lt;/strong&gt; Codex CLI sends to the same collector. It emits mostly events rather than metrics, so its data lands in Loki instead of Prometheus. The same pipeline can absorb other agents as the team's tooling changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the data showed
&lt;/h2&gt;

&lt;p&gt;The dashboards answered questions we had only been guessing at.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spend was concentrated.&lt;/strong&gt; A few people and a few task types drove most of the cost. That is not bad by itself, but now we knew where to look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adoption was uneven.&lt;/strong&gt; Some engineers barely used the assistant at all. Without data, we would have assumed everyone was on board.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vague tasks burn money.&lt;/strong&gt; Work that started without a spec produced retries, rework and long sessions. The cost showed up in the data before it showed up in review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specs cut the cost per task.&lt;/strong&gt; Once the spec-driven workflow was in place, the same kind of work got cheaper. Context up front is cheaper than correction later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some skills were cost hogs.&lt;/strong&gt; Per-skill dashboards made it obvious. Our scraping skills were the worst offenders. We reworked them and brought their cost down.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Faster PRs, because reviews start from a diff that already passed an agent review against the spec.&lt;/li&gt;
&lt;li&gt;Less rework, because agents stop guessing at context.&lt;/li&gt;
&lt;li&gt;More delivered per engineer, with the same team.&lt;/li&gt;
&lt;li&gt;AI spend became something we plan and budget, not a surprise on the invoice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this came from a better model. It came from giving the models a process, and giving ourselves a way to see what they were doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to start on your team
&lt;/h2&gt;

&lt;p&gt;You do not need our exact setup. The order matters more than the tools.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write specs before prompts.&lt;/strong&gt; Even a one-page spec with verification criteria changes what an agent produces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split the writer from the reviewer.&lt;/strong&gt; A second agent in a fresh context, reviewing against the spec, catches what the first one talks itself out of.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on Claude Code telemetry in the repo.&lt;/strong&gt; Set &lt;code&gt;CLAUDE_CODE_ENABLE_TELEMETRY=1&lt;/code&gt; and the OTLP endpoint in a versioned &lt;code&gt;.claude/settings.json&lt;/code&gt;, so every session exports by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with cost per developer and per skill.&lt;/strong&gt; Those two views surface most of the problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decide on prompt capture with HR and legal, not in a config file.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The workflow is open source: &lt;a href="https://github.com/rafagomes/ai-workflow" rel="noopener noreferrer"&gt;rafagomes/ai-workflow&lt;/a&gt;. If you are measuring AI coding on your team in a different way, I would like to hear how.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://rafagomes.tech/writing/instrumenting-claude-code-opentelemetry/" rel="noopener noreferrer"&gt;rafagomes.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>opentelemetry</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
