Finalize Your Dapr Skill
You've built a dapr-deployment skill in Lesson 0 and refined it through nine lessons. Each lesson added patterns: sidecar architecture, building blocks vs components, Helm deployment, state management, pub/sub messaging, service invocation, bindings, the Jobs API, secrets, and configuration.
Now it's time to make that skill production-ready.
A skill isn't complete when it covers all the topics. It's complete when it helps you avoid the mistakes you'd make without it. This lesson focuses on three things: testing your skill against real prompts, adding safety guardrails that prevent production disasters, and documenting the errors you'll encounter so you can fix them quickly.
Review: What Your Skill Should Cover
Throughout this chapter, you've added patterns to your skill after each lesson. Here's what a complete dapr-deployment skill should include:
| Lesson | Pattern Added | Check Your Skill |
|---|---|---|
| L01 | Sidecar architecture explanation | Translator analogy, daprd ports (3500/50001), Kubernetes annotations |
| L02 | Building blocks vs components distinction | APIs vs implementations, component YAML structure, backend swapping |
| L03 | Helm deployment + state patterns | Helm commands for Dapr 1.14+, state component YAML, ETag concurrency |
| L04 | Service invocation patterns | invoke_method() usage, app-id discovery, mTLS automatic |
| L05 | Pub/Sub patterns | publish_event(), subscription decorators, CloudEvents format |
| L06 | Binding patterns | Input bindings (triggers), output bindings (external calls) |
| L07 | Jobs API patterns | Job creation, scheduling, one-time vs recurring |
| L08 | Secrets/config patterns | get_secret(), secrets component, configuration subscriptions |
| L09 | Complete integration example | Full FastAPI app with state + pub/sub + invoke |
Open your skill file and check each row. If something's missing, this is the lesson to add it.
Test Your Skill Against Production Prompts
A skill that only works for the examples you learned from isn't production-ready. Test it against prompts that simulate real deployment scenarios.
Test Prompt 1: Kubernetes Deployment
Using my dapr-deployment skill, deploy Dapr 1.14 on a fresh Kubernetes cluster.
Show me the Helm commands and how to verify all control plane pods are running.
Expected skill output should include:
# Add Dapr Helm repo
helm repo add dapr https://dapr.github.io/helm-charts/
helm repo update
# Install Dapr control plane
helm upgrade --install dapr dapr/dapr \
--version=1.14.0 \
--namespace dapr-system \
--create-namespace \
--wait
# Verify installation
kubectl get pods -n dapr-system
Plus verification that you see: dapr-operator, dapr-sidecar-injector, dapr-sentry, dapr-placement-server, dapr-scheduler-server.
If your skill fails this test: Add the Helm deployment section from Lesson 03.
Test Prompt 2: State Management
Using my dapr-deployment skill, add state management to my FastAPI app.
I need to save and retrieve task objects using Redis as the backend.
Show me the component YAML and Python code.
Expected skill output should include:
State component YAML:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis-master.default.svc.cluster.local:6379
Python code using DaprClient:
from dapr.clients import DaprClient
import json
with DaprClient() as client:
# Save state
client.save_state(
store_name='statestore',
key='task-123',
value=json.dumps({'title': 'Buy groceries', 'status': 'pending'})
)
# Get state
state = client.get_state(store_name='statestore', key='task-123')
task = json.loads(state.data) if state.data else None
If your skill fails this test: Add the state management patterns from Lesson 03.
Test Prompt 3: Pub/Sub Setup
Using my dapr-deployment skill, set up pub/sub messaging for my FastAPI service.
I need to publish task events and subscribe to them in another service.
Expected skill output should include:
Pub/sub component YAML:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: redis-master.default.svc.cluster.local:6379
Publishing code:
from dapr.clients import DaprClient
import json
with DaprClient() as client:
client.publish_event(
pubsub_name='pubsub',
topic_name='task-events',
data=json.dumps({'event_type': 'task.created', 'task_id': 'task-123'}),
data_content_type='application/json'
)
Subscription code:
from fastapi import FastAPI
from dapr.ext.fastapi import DaprApp
app = FastAPI()
dapr_app = DaprApp(app)
@dapr_app.subscribe(pubsub='pubsub', topic='task-events')
async def handle_task_event(event_data: dict):
print(f"Received: {event_data}")
return {"status": "SUCCESS"}
If your skill fails this test: Add the pub/sub patterns from Lesson 05.
Test Prompt 4: Scheduled Job
Using my dapr-deployment skill, schedule a recurring cleanup job that runs
every hour to remove completed tasks older than 7 days.
Expected skill output should include:
Jobs API usage:
from dapr.clients import DaprClient
with DaprClient() as client:
# Schedule recurring job
client.start_workflow(
workflow_component='dapr',
workflow_name='cleanup-job',
input={'retention_days': 7}
)
Or the Jobs API endpoint pattern:
POST http://localhost:3500/v1.0/jobs/cleanup-old-tasks
Content-Type: application/json
{
"schedule": "@every 1h",
"data": {"retention_days": 7}
}
If your skill fails this test: Add the Jobs API patterns from Lesson 07.
Test Prompt 5: Secrets Configuration
Using my dapr-deployment skill, configure my FastAPI service to retrieve
API keys from Kubernetes secrets instead of environment variables.
Expected skill output should include:
Secrets component YAML:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: kubernetes-secrets
spec:
type: secretstores.kubernetes
version: v1
metadata: []
Python code:
from dapr.clients import DaprClient
with DaprClient() as client:
secret = client.get_secret(
store_name='kubernetes-secrets',
key='api-credentials'
)
api_key = secret.secret.get('api-key')
If your skill fails this test: Add the secrets management patterns from Lesson 08.
Add Safety Guardrails
Your skill should prevent production mistakes, not just show working patterns. Add these guardrails to your dapr-deployment skill.
NEVER
Add a "Safety: NEVER" section to your skill:
## Safety: NEVER
- **Call Dapr before sidecar is ready** - Your app will get connection refused errors. Always wait for sidecar health check.
- **Hardcode component names** - Use configuration or environment variables. Component names may differ between environments.
- **Skip error handling for Dapr API calls** - Dapr calls can fail (sidecar not ready, component misconfigured, backend unavailable). Always wrap in try/except.
- **Store sensitive data in state without encryption** - State stores are not encrypted by default. Use secrets component for credentials, or enable encryption at the component level.
- **Expose Dapr HTTP/gRPC ports externally** - Dapr's sidecar ports (3500, 50001) are for internal pod communication only. External access bypasses mTLS.
- **Use `dapr.io/app-port` for gRPC apps without specifying protocol** - If your app uses gRPC, set `dapr.io/app-protocol: "grpc"` or Dapr will send HTTP to your gRPC server.
ALWAYS
Add a "Safety: ALWAYS" section to your skill:
## Safety: ALWAYS
- **Wait for sidecar readiness** before making Dapr calls:
```python
import httpx
import asyncio
async def wait_for_sidecar(timeout: int = 30):
"""Wait for Dapr sidecar to be ready."""
async with httpx.AsyncClient() as client:
for _ in range(timeout):
try:
response = await client.get("http://localhost:3500/v1.0/healthz")
if response.status_code == 204:
return True
except httpx.ConnectError:
pass
await asyncio.sleep(1)
raise RuntimeError("Dapr sidecar not ready")
-
Use secrets component for credentials - Never put passwords in component YAML. Use
secretKeyRef:metadata:
- name: redisPassword
secretKeyRef:
name: redis-secret
key: password -
Enable mTLS in production - Dapr enables mTLS by default when Sentry is running. Verify with:
kubectl get pods -n dapr-system | grep sentry -
Configure retry policies for resilience:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
spec:
policies:
retries:
myRetryPolicy:
policy: constant
duration: 1s
maxRetries: 3 -
Set resource limits on sidecar to prevent resource contention:
annotations:
dapr.io/sidecar-cpu-limit: "500m"
dapr.io/sidecar-memory-limit: "256Mi"
---
## Common Errors Reference
Add this error table to your skill. When something breaks, this is the first place you'll look.
```markdown
## Common Errors
| Error | Cause | Fix |
|-------|-------|-----|
| `ERR_STATE_STORE_NOT_FOUND` | State component not configured or not in same namespace | Apply component YAML: `kubectl apply -f components/statestore.yaml` |
| `ERR_PUBSUB_NOT_FOUND` | Pub/sub component not configured | Apply component YAML: `kubectl apply -f components/pubsub.yaml` |
| `connection refused :3500` | Sidecar not ready when app starts | Add startup probe or wait for `/v1.0/healthz` before Dapr calls |
| `ERR_DIRECT_INVOKE` | Target app-id not found | Check target has `dapr.io/app-id` annotation and is running |
| `DEADLINE_EXCEEDED` | Request timeout | Increase timeout or check if target service is overloaded |
| `sidecar not found` | Sidecar injection not enabled | Check `dapr.io/enabled: "true"` annotation and namespace has injection enabled |
| `ERR_SECRET_STORE_NOT_FOUND` | Secrets component not configured | Apply secrets component YAML |
| `ERR_PUBSUB_FORBIDDEN` | App not in component's scope | Add app-id to component's `scopes` list |
Final Validation: Complete Integration Test
Run this prompt against your skill as a final validation:
Using my dapr-deployment skill, generate a complete Dapr setup for:
- A FastAPI service that saves tasks to state store
- Publishes events to pub/sub when tasks are created
- Calls a notification service via service invocation
- Schedules a daily cleanup job
- Gets API keys from Kubernetes secrets
Show all component YAMLs, Python code, and Kubernetes deployment manifest.
A production-ready skill should generate:
- Component YAMLs: statestore, pubsub, kubernetes-secrets
- Python code: Complete FastAPI app with DaprClient
- Kubernetes manifest: Deployment with Dapr annotations
- Sidecar readiness: Health check before Dapr calls
- Error handling: Try/except around Dapr operations
If your skill generates incomplete or incorrect output, identify the gap and add the missing pattern.
Your Growing Skills Library
Your dapr-deployment skill joins a growing collection of production-ready tools:
.claude/skills/
+-- skill-creator/
+-- fetching-library-docs/
+-- fastapi-agent-api/ # Chapter 40
+-- docker-deployment/ # Chapter 49
+-- kubernetes-deployment/ # Chapter 50
+-- helm-chart/ # Chapter 51
+-- kafka-events/ # Chapter 52
+-- dapr-deployment/ # This chapter - COMPLETE
Each skill compounds your capability. The dapr-deployment skill doesn't just know Dapr patterns---it encodes the safety guardrails and error diagnostics you'd otherwise learn through production incidents.
That's the value of skill-first learning. You don't just learn Dapr. You build an asset that makes every future Dapr project faster and safer.
Try With AI
You've completed your dapr-deployment skill. Use AI to validate, extend, and stress-test it.
Prompt 1: Coverage Audit
Review my dapr-deployment skill. Does it cover all 6 building blocks from
Chapter 53?
The building blocks are:
1. State Management
2. Pub/Sub Messaging
3. Service Invocation
4. Bindings (input and output)
5. Secrets Management
6. Jobs API
For each building block, tell me:
- Is it covered in my skill? (Yes/No)
- Is the coverage complete? (Component YAML + Python code + common patterns)
- What's missing?
[Paste your SKILL.md content here]
What you're learning: External review reveals blind spots. You may have excellent state management coverage but missed input bindings entirely. This prompt forces a systematic audit of completeness.
Prompt 2: Safety Guardrails Review
I've added safety guardrails to my dapr-deployment skill. Review them and tell me:
1. Are my NEVER rules comprehensive? What production mistakes am I still vulnerable to?
2. Are my ALWAYS rules actionable? Can someone follow them without additional research?
3. What guardrails do experienced Dapr operators add that I'm missing?
Current guardrails:
NEVER:
- Call Dapr before sidecar is ready
- Hardcode component names
- Skip error handling
- Store secrets in state
- Expose Dapr ports externally
ALWAYS:
- Wait for sidecar readiness
- Use secretKeyRef for credentials
- Enable mTLS in production
- Configure retry policies
- Set sidecar resource limits
What you're learning: Safety guardrails emerge from production experience. AI can surface patterns from thousands of Dapr deployments, identifying risks you haven't encountered yet.
Prompt 3: Stress Test
Test my dapr-deployment skill by generating a complete Dapr-enabled microservice
from scratch. The service should:
- Accept HTTP requests to create orders
- Save order state with ETag-based optimistic concurrency
- Publish order.created events to pub/sub
- Call inventory-service to check stock via service invocation
- Retrieve Stripe API keys from secrets
- Schedule order cleanup job for midnight daily
Generate all YAMLs, Python code, and Kubernetes manifests.
After generating, evaluate: Did my skill produce production-ready output?
What was missing or incorrect?
What you're learning: Real-world scenarios expose gaps. An order system has different patterns than a task system---different event types, different concurrency requirements, different integration points. This stress test validates that your skill generalizes beyond the Task API examples.
Safety Note: When deploying Dapr-enabled services to production clusters, always verify component configurations in a staging environment first. Misconfigured components can cause silent data loss (state not persisted) or message drops (pub/sub misconfigured). Your skill provides patterns; you validate they match your specific infrastructure.