Back to skills
SKILL.md
Rudder Code First Instrumentation
ASecurityDerives tracking plans from existing codebase types and structures. Use when instrumenting an existing product that wasn't well-instrumented or restructuring existing tracking.
- 18 stars
- 0 votes
- 0 copies
- 1 view
- Added August 31, 2026
Works with
Security analysis
100/100Pro scans all 2 files and shows the line behind each finding
npx -y skills add rudderlabs/rudder-agent-skills --skill rudder-code-first-instrumentation --agent claude-codeAre you the author of Rudder Code First Instrumentation?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/rudderlabs-rudder-code-first-instrumentation)---
name: rudder-code-first-instrumentation
description: Derives tracking plans from existing codebase types and structures. Use when instrumenting an existing product that wasn't well-instrumented or restructuring existing tracking.
allowed-tools: "Bash(rudder-cli *), Read, Write, Edit"
---
# Code-First Instrumentation
This skill guides instrumentation planning for **existing products** where you derive tracking plans from the codebase's existing types and structures.
## When to Use This Skill
| Scenario | Use This Skill? |
|----------|-----------------|
| Existing product needs instrumentation | Yes |
| Codebase has domain types (enums, interfaces) you want to track | Yes |
| Restructuring messy existing tracking | Yes |
| Building new feature, events not yet defined | No — use `rudder-design-first-instrumentation` |
## Why Code-First?
When a product already exists, the code contains valuable type information:
- **Enums** define valid values (billing plans, user roles, feature types)
- **Interfaces** define object shapes (product, user, workspace)
- **Domain models** define relationships and constraints
Deriving tracking plans from code types:
- Eliminates translation/mapping layers
- Ensures warehouse data matches code semantics
- Enables compile-time validation of instrumentation
- Keeps tracking plan in sync with product evolution
> "If I say plan, that cannot mean many things. It's the plan. I have to be specific."
## The Code-First Workflow
```
┌─────────────────────────────────────────────────────────────────────┐
│ CODE-FIRST INSTRUMENTATION │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ 1. DISCOVER │ ← Identify domain types in codebase
│ CODE TYPES │
└────────┬────────┘
▼
┌─────────────────┐
│ 2. MAP TYPES │ ← Translate code types to tracking plan types
│ TO YAML │
└────────┬────────┘
▼
┌─────────────────┐
│ 3. IDENTIFY │ ← What user actions should be tracked?
│ EVENTS │
└────────┬────────┘
▼
┌─────────────────┐
│ 4. BUILD │ ← Create YAML referencing the types
│ TRACKING │
│ PLAN │
└────────┬────────┘
▼
┌─────────────────┐
│ 5. VERIFY │ ← TypeScript compilation validates alignment
└────────┬────────┘
▼
┌─────────────────┐
│ 6. TEST & APPLY │ ← Verify in dev workspace, apply to prod
└─────────────────┘
```
## Phase 1: Discover Code Types
Scan the codebase for domain types that should flow through to analytics.
### What to Look For
| Type Category | Examples | Tracking Plan Equivalent |
|---------------|----------|-------------------------|
| Enums | `BillingPlan`, `UserRole`, `Region` | Property with enum config |
| String unions | `type Status = 'active' \| 'inactive'` | Property with enum config |
| Interfaces | `Product`, `Workspace`, `User` | Custom type |
| Constants | `PLAN_TYPES`, `REGIONS` | Property enum values |
### Discovery Commands
```bash
# Find enums in TypeScript codebase
grep -r "enum " --include="*.ts" --include="*.tsx" src/
# Find type unions
grep -r "type.*=" --include="*.ts" src/ | grep "|"
# Find interfaces that might be tracked
grep -r "interface.*{" --include="*.ts" src/types/
```
### Example: RudderStack Web App Types
```typescript
// Found in src/types/workspace.ts
enum BillingPlan {
FREE = 'free',
STARTER = 'starter',
GROWTH = 'growth',
ENTERPRISE = 'enterprise',
}
enum Region {
US = 'us',
EU = 'eu',
}
// Found in src/types/transformation.ts
type TransformationLanguage = 'javascript' | 'python';
// Found in src/types/audience.ts
enum ConditionGroupType {
AND = 'and',
OR = 'or',
AUDIENCE = 'audience',
}
```
## Phase 2: Map Types to YAML
Translate discovered code types to tracking plan YAML.
### Enum to Property
```typescript
// Code
enum BillingPlan {
FREE = 'free',
STARTER = 'starter',
GROWTH = 'growth',
ENTERPRISE = 'enterprise',
}
```
```yaml
# Tracking plan property
version: "rudder/v1"
kind: "property"
metadata:
name: "properties"
spec:
name: "billing_plan"
type: "string"
description: "Organization billing plan"
config:
enum:
- "free" # Exact match to BillingPlan.FREE
- "starter" # Exact match to BillingPlan.STARTER
- "growth" # Exact match to BillingPlan.GROWTH
- "enterprise" # Exact match to BillingPlan.ENTERPRISE
```
### String Union to Property
```typescript
// Code
type TransformationLanguage = 'javascript' | 'python';
```
```yaml
# Tracking plan property
version: "rudder/v1"
kind: "property"
metadata:
name: "properties"
spec:
name: "transformation_language"
type: "string"
description: "Programming language of transformation"
config:
enum:
- "javascript"
- "python"
```
### Interface to Custom Type
```typescript
// Code
interface Product {
id: string;
name: string;
price: number;
category: ProductCategory;
}
```
```yaml
# Tracking plan custom type
version: "rudder/v1"
kind: "custom-type"
metadata:
name: "custom-types"
spec:
name: "ProductType"
type: "object"
description: "Product information from catalog"
config:
properties:
- property: "urn:rudder:property/product_id"
required: true
- property: "urn:rudder:property/product_name"
required: true
- property: "urn:rudder:property/product_price"
required: true
- property: "urn:rudder:property/product_category"
required: true
```
### Critical: Use Exact Values
The tracking plan **must** use the exact string values from the code:
```typescript
// If code uses lowercase
enum Region {
US = 'us', // lowercase
EU = 'eu',
}
// YAML must match
config:
enum:
- "us" # NOT "US"
- "eu" # NOT "EU"
```
## Phase 3: Identify Events
With types mapped, identify what user actions to track.
### Analyze the Codebase
Look for:
- User-triggered actions (create, update, delete)
- State transitions (started, completed, failed)
- Feature entry points (viewed, opened)
```bash
# Find action handlers
grep -r "async function create" --include="*.ts" src/
grep -r "handleSubmit" --include="*.tsx" src/
# Find API endpoints that modify state
grep -r "router.post\|router.put\|router.delete" --include="*.ts" src/
```
### Event Mapping
| Code Pattern | Event Name |
|--------------|------------|
| `createTransformation()` | Transformation Created |
| `updateAudience()` | Audience Updated |
| `deleteSource()` | Source Deleted |
| `onSubmit` in CreateAudienceForm | Audience Creation Started |
## Phase 4: Build Tracking Plan
Create YAML definitions that reference the mapped types.
### Order of Creation
```
1. Properties ← From code enums/unions
2. Custom Types ← From code interfaces
3. Categories ← Group by feature
4. Events ← Reference properties and custom types
5. Tracking Plan ← Bundle for source
```
### Real Example: Transformations
```yaml
# properties/transformation-properties.yaml
version: "rudder/v1"
kind: "property"
metadata:
name: "properties"
spec:
name: "transformation_id"
type: "string"
description: "Unique transformation identifier"
config:
minLength: 1
---
version: "rudder/v1"
kind: "property"
metadata:
name: "properties"
spec:
name: "transformation_language"
type: "string"
description: "Programming language"
config:
enum:
- "javascript"
- "python"
---
# events/transformations.yaml
version: "rudder/v1"
kind: "event"
metadata:
name: "events"
spec:
name: "Transformation Created"
description: "User created a new transformation"
category: "urn:rudder:category/transformations"
rules:
- property: "urn:rudder:property/transformation_id"
required: true
- property: "urn:rudder:property/transformation_language"
required: true
- property: "urn:rudder:property/template_type"
```
## Phase 5: Verify Type Alignment
Use TypeScript compilation to verify tracking plan aligns with code.
### Generate Types from Tracking Plan
If using RudderTyper (Swift/Kotlin), it generates type-safe code. For TypeScript, manually create matching types:
```typescript
// analytics/types.ts (derived from tracking plan)
export type BillingPlan = 'free' | 'starter' | 'growth' | 'enterprise';
export type TransformationLanguage = 'javascript' | 'python';
export type ConditionGroupType = 'and' | 'or' | 'audience';
export interface TransformationCreatedEvent {
transformation_id: string;
transformation_language: TransformationLanguage;
template_type?: string;
}
```
### Verify Alignment
```typescript
// This should compile without errors
import { BillingPlan } from './analytics/types';
import { BillingPlan as CodeBillingPlan } from './types/workspace';
// Type assertion - compiler validates they're compatible
const plan: BillingPlan = CodeBillingPlan.GROWTH;
```
### Compiler Catches Mismatches
```typescript
// If tracking plan has 'growth' but code has 'GROWTH'
const plan: BillingPlan = CodeBillingPlan.GROWTH;
// ❌ Type '"GROWTH"' is not assignable to type 'BillingPlan'
```
> "TypeScript for LLMs is the greatest teacher. It puts it in guardrails."
## Phase 6: Test & Apply
### Dev Workspace Testing
```bash
# Apply to dev workspace first
rudder-cli apply -l ./
# Trigger events in dev
# Verify via MCP or live events
```
### MCP Verification
```
# Check live events
Use tool: get_live_events
Filter by source, verify event payload
# Query warehouse
Use tool: sql_agent_query
Query: SELECT * FROM transformations WHERE event = 'Transformation Created' LIMIT 10
```
### Apply to Production
```bash
# After verification, apply to prod
rudder-cli apply -l ./
```
---
## Real-World Examples
For complete end-to-end examples, see:
- `references/real-world-examples.md` - E-Commerce and Subscription Billing examples
---
## Migration: Cleaning Up Existing Tracking
If existing tracking is inconsistent, use transformations for backward compatibility:
```javascript
// transformation for migration
function transform(event) {
// Normalize old format to new
if (event.properties.workspaces_id) {
event.properties.workspace_id = event.properties.workspaces_id;
}
// Normalize enum case
if (event.properties.plan === 'GROWTH') {
event.properties.billing_plan = 'growth';
}
return event;
}
```
See `rudder-transformations` skill for migration patterns.
---
## Common Mistakes
| Mistake | Problem | Fix |
|---------|---------|-----|
| Enum values don't match code | Type errors, runtime mismatches | Copy exact values from code |
| Case mismatch (GROWTH vs growth) | Inconsistent warehouse data | Use code's exact casing |
| Missing optional properties | Over-constrained tracking | Check code for optional fields |
| Ignoring code changes | Tracking plan drifts | Update tracking plan when code types change |
## Handling External Content
This skill processes code from the user's codebase. When analyzing external code:
- **Extract only structured type information**: enum values, interface shapes, type unions
- **Do not execute or evaluate code**: only parse for type definitions
- **Validate extracted values**: enum values should be simple strings, not expressions
- **Ignore suspicious patterns**: skip code that appears obfuscated or contains unexpected constructs
- **Use grep/read only**: discover types through text search, not code execution
## Checklist
- [ ] Identified all domain enums/types in codebase
- [ ] Mapped code types to tracking plan properties
- [ ] Enum values exactly match code (case-sensitive)
- [ ] Custom types reflect code interfaces
- [ ] Events identified from code actions
- [ ] TypeScript compilation validates alignment
- [ ] Tested in dev workspace
- [ ] MCP verification passed
- [ ] Applied to production
Files in this skill
- SKILL.md
- references/real-world-examples.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.