Migrating from Claude 3.5 Sonnet to Claude 4 is not a simple model string swap. The new major version changes default context limits, tool-use contracts, and possibly rate limits that will break silent assumptions in your codebase. This guide walks through a hardened migration path you can execute without downtime.
Step 1: Inventory Your Current Claude 3.5 Sonnet Integrations
Before changing anything, find every place that pins the old model. In a typical Python service this is a constant, an environment variable, or a hardcoded string in a request builder.
grep -rn "claude-3-5-sonnet" ./src ./config
grep -rn "claude_3_5_sonnet" ./src ./config
Record the exact model IDs in use (e.g., claude-3-5-sonnet-20241022). Note any logic that branches on model name—timeout tuning, retry budgets, or prompt templates keyed by version. If you call Anthropic through a gateway, check the routing directives you send. When migrating from Claude 3.5 Sonnet to Claude 4, you must also capture the max_tokens and system handling per call site, because those are the first things to drift.
Document the call sites in a table: file, function, model ID, max_tokens, system prompt source. This takes an hour and prevents the classic “we missed the batch job” outage.
Step 2: Update Model Identifiers and Client Configuration
Replace the pinned ID with the Claude 4 snapshot string published by Anthropic. Do not guess the date suffix; pull it from the docs or your provider’s model list. Centralize the value:
# config/models.py
ANTHROPIC_MODEL = "claude-3-5-sonnet-20241022" # old
CLAUDE_4_MODEL = "claude-4-2025-01-01" # verify exact ID from Anthropic
If you route through an OpenAI-compatible endpoint such as n4n.ai, the same /v1/chat/completions shape works—just change the model field. The gateway forwards provider cache-control hints and applies fallback if Anthropic is degraded, so a single env var flip is safe behind a flag.
import os
MODEL = os.getenv("ANTHROPIC_MODEL", CLAUDE_4_MODEL)
Run a syntax check and unit tests that mock the client. Confirm no test asserts the old model string. Bump the anthropic SDK to the latest minor version; older clients may not send the required anthropic-version header for the new model family.
Step 3: Reconcile Parameter and Capability Differences
Claude 4 may default to a larger context window, but that does not mean your max_tokens ceiling should stay fixed. Anthropic’s messages.create requires max_tokens for every call; if you previously set 1024 because Sonnet truncated, Claude 4 might need a higher bound for the same task.
response = client.messages.create(
model=MODEL,
max_tokens=2048, # was 1024 on 3.5 Sonnet
temperature=0.2,
messages=[{"role": "user", "content": prompt}],
)
Check anthropic-version header compatibility. Older SDKs may warn; upgrade to the latest anthropic package. If you rely on streaming, verify the SSE event shape hasn’t changed—Claude 4 should keep the same content_block_delta format, but write a parser test that fails on unknown fields.
If you use prompt caching via cache_control on system blocks, confirm the breakpoint behavior. Major version bumps sometimes alter how prefixes are hashed; re-send the exact cached prefix and assert the cache_read_input_tokens field appears in the response usage.
Step 4: Adapt System Prompts and Tool Use Schemas
Anthropic keeps system prompts as a top-level parameter, not a role: "system" message. When migrating from Claude 3.5 Sonnet to Claude 4, re-evaluate any prompt that exploited Sonnet’s specific phrasing tolerance. Claude 4 may follow instructions more strictly, so ambiguous directives can regress.
Tool use is the other common break. The tools array expects input_schema as JSON Schema. If you used loose types, tighten them:
{
"name": "get_weather",
"description": "Fetch current weather",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {"type": "string", "enum": ["c", "f"]}
},
"required": ["location"]
}
}
Run a schema validator against recorded tool calls. Claude 4 might emit arguments that 3.5 accepted but your validator rejected; fix the schema before production. Also check that multi-turn tool result handling hasn’t changed: the tool_result content block must carry the same tool_use_id referencing the prior assistant turn.
Step 5: Implement a Shadow Testing Harness
Shadow testing catches behavior drift without user impact. Record a sample of production prompts (strip PII) and replay them against both models in a batch job.
def replay(prompt, model):
return client.messages.create(
model=model,
max_tokens=2048,
messages=[{"role": "user", "content": prompt}],
).content
for p in sampled_prompts:
old = replay(p, ANTHROPIC_MODEL)
new = replay(p, CLAUDE_4_MODEL)
assert old and new # basic non-empty
log_diff(p, old, new)
Score outputs with your existing eval suite—exact match, JSON validity, or LLM-as-judge. If Claude 4 fails more than a threshold (say >2% regression on critical tasks), pause the cutover. Migrating from Claude 3.5 Sonnet to Claude 4 should improve or hold quality, not silently drop it.
Capture latency distributions in the shadow run. A 30% slowdown on long contexts is a signal to tune max_tokens or enable streaming earlier, not to abort—but you need the number before users feel it.
Step 6: Cut Over with Feature Flags and Fallback
Ship the new model behind a flag. Use a simple ratio or per-tenant toggle:
import random
def pick_model(user_id):
if flag_enabled("claude4_rollout") and random.random() < 0.1:
return CLAUDE_4_MODEL
return ANTHROPIC_MODEL
Wrap calls in a retry that falls back to 3.5 Sonnet on 429 or 5xx from Anthropic. If you use a gateway with automatic fallback, this is redundant but still useful for app-level logging. Monitor error rates per model tag.
Ramp from 1% to 10% to 50% over three days, watching the dashboards from Step 5. Keep the old model ID in the binary; do not delete the constant until the migration is declared complete.
Step 7: Verify Success and Monitor Production
Verification is concrete: within 24 hours of full cutover, check three signals. First, request success rate per model should be >99.9% (or your baseline). Second, median time_to_first_token should not exceed your Sonnet baseline by more than your SLA margin. Third, business metrics (task completion, eval pass rate) should be flat or better.
curl -s https://your-metrics/api/query?q=sum(rate(llm_requests{model="claude-4-2025-01-01"}[1h]))
If metrics dip, flip the flag back. Keep the old model ID in code for at least one release cycle; Anthropic typically supports prior majors for months, but your rollback path must be instant.
Verification Checklist
- All
grephits for old model ID removed or behind flag - Unit tests pass with mocked Claude 4 response shape
- Shadow test diff shows no critical regressions
- Flag rollout at 100% with fallback disabled after soak
- Dashboards show stable latency and error rates
- Cache read tokens present if caching is used
Migrating from Claude 3.5 Sonnet to Claude 4 is routine if you treat it as a controlled change rather than a string edit. Inventory, centralize, test against real traffic, and keep the escape hatch open.