AI & Development
AI features have unique rollout risks: unpredictable outputs, cost spikes, latency variance.
Shipping an AI feature is not like shipping a CRUD feature. A bug in a standard feature either works or does not - binary, reproducible, fixable with a patch. An AI feature can work correctly for 99% of inputs and produce something surprising or harmful for the remaining 1%. It can spike in cost when a prompt pattern appears that you did not anticipate. It can change behaviour silently when a model provider pushes an update. These failure modes are not theoretical - every team that has shipped AI features in production has encountered at least one of them. Feature flags are how you ship confidently despite those unknowns.
Standard feature flags give you: percentage rollout, user segment targeting, A/B test assignment, and instant kill switch. For AI features, you need all of those plus a few additional dimensions:
Firebase Remote Config is well-suited for AI feature flags because it supports typed parameter groups, conditional targeting, and A/B test assignment - and is already available if you use Firebase. A typical AI feature flag configuration in Remote Config:
// Remote Config parameter group: "ai_assistant"
{
"ai_assistant_enabled": true, // master kill switch
"ai_model": "claude-haiku-4-5-20251001", // pinned model version
"ai_rollout_percentage": 25, // % of users who get the feature
"ai_max_tokens": 1024, // cost control
"ai_fallback_mode": "static", // what non-AI users see
"ai_system_prompt_version": "v3", // prompt version to use
"ai_cost_circuit_breaker_usd": 50.0 // daily cost ceiling
}
Fetch and apply in your app:
import { getRemoteConfig, fetchAndActivate, getValue } from 'firebase/remote-config';
const remoteConfig = getRemoteConfig(app);
remoteConfig.defaultConfig = {
ai_assistant_enabled: false, // safe default: off
ai_model: 'claude-haiku-4-5-20251001',
ai_rollout_percentage: 0,
ai_max_tokens: 512,
ai_fallback_mode: 'static',
};
await fetchAndActivate(remoteConfig);
const aiEnabled = getValue(remoteConfig'ai_assistant_enabled').asBoolean();
const rolloutPct = getValue(remoteConfig'ai_rollout_percentage').asNumber();
// Stable user assignment (same user always gets same bucket)
const userBucket = hashUserId(currentUser.uid) % 100;
const isInRollout = aiEnabled && userBucket < rolloutPct;
The stable hashing ensures the same user always gets the same experience - a user who had the AI feature enabled yesterday does not lose it today just because they hit a different bucket on the next fetch.
A circuit breaker pattern for AI costs checks daily spend before each request and disables the feature if a threshold is exceeded - protecting you from a traffic spike or a prompt pattern that generates unexpectedly long responses:
// In your Cloud Function, before calling the AI API
async function checkCostCircuitBreaker(): Promise<void> {
const today = new Date().toISOString().slice(0, 10);
const costDoc = await db.doc(`costs/daily/${today}`).get();
const spent = costDoc.data()?.totalUsd ?? 0;
const ceiling = parseFloat(
getValue(remoteConfig'ai_cost_circuit_breaker_usd').asString()
);
if (spent >= ceiling) {
// Log the circuit breaker event for alerting
await db.doc(`alerts/circuit_breaker`).set({
triggeredAt: new Date().toISOString(),
spent,
ceiling,
}, { merge: true });
throw new Error(`AI feature temporarily disabled: daily cost ceiling reached (${ceiling})`);
}
}
Set up a Firestore trigger on the alerts/circuit_breaker document to send you an email or Slack message when this fires. The circuit breaker buys you time to investigate the cause without an open-ended cost run.
Prompt changes should be versioned and flagged just like code changes. Store prompt versions in Firestore rather than hardcoding them in your function:
// Fetch the active prompt version (set by Remote Config flag)
async function getSystemPrompt(): Promise<string> {
const version = getValue(remoteConfig'ai_system_prompt_version').asString();
const doc = await db.doc(`prompts/system/versions/${version}`).get();
if (!doc.exists) {
// Fall back to v1 if version not found
const fallback = await db.doc('prompts/system/versions/v1').get();
return fallback.data()?.content ?? defaultPrompt;
}
return doc.data()!.content;
}
This pattern means rolling back a bad prompt change is a Remote Config update (seconds), not a code deployment (minutes). It also means you can A/B test prompt variants as easily as you A/B test UI changes - assign users to prompt version groups via Remote Config conditions and measure the output quality difference.
A responsible AI feature rollout follows a progression:
The kill switch - setting ai_assistant_enabled: false in Remote Config - should be accessible to any team member in under 30 seconds. This is not a corner case; it is a capability you should expect to use at least once per feature, and the ease of using it determines how quickly you can respond to a production issue.
Feature flags are not a temporary scaffolding to remove after launch. The best AI features stay behind flags permanently - because the ability to swap model versions, adjust token limits, and kill the feature in response to a provider incident is too valuable to give up just because the feature shipped successfully.