Chapter 26 · Testing, Emulator Suite, CI/CD, Index/Rules Deployment, and Schema Migration
Version Security Rules and Index Definitions, CI Validation, Staged Deployment, and Rollback
Version and deploy Firestore Rules/indexes as reviewed release artifacts with staged promotion, readiness evidence, compatibility ordering, and rollback.
1. AtlasMart drift incident: console hotfix meets source control
A developer fixes a production Rules bug in the console. Another
engineer merges a repository change to
firestore.rules and deploys with the CLI. The local
file overwrites the console rules, reintroducing the bug. At the
same time, a new query passed in the emulator because compound
indexes are not tracked there, but production rejects it until a
composite index finishes building.
The repair is not “be more careful.” Rules and index definitions are release artifacts: version them, diff them, test them, deploy them in a deliberate order, and record the resulting release evidence.
- Treat Rules/index JSON as code with reviewed diffs and deterministic tests.
- Use selective CLI deployment correctly and understand overwrite behavior.
- Define staging and canary deployment without inventing a Firestore preview environment that does not exist.
- Order index/rule/application releases to preserve compatibility.
- Design rollback around rule releases, index readiness and application feature flags.
Use the Emulator Suite, a Firebase demo project, or an isolated test project for destructive, security-sensitive, billing-sensitive, migration, backup/restore, or write-heavy exercises unless the lesson explicitly marks managed verification as required. Treat shown output as expected evidence unless it is explicitly identified as captured output, and re-check current Firebase/Google Cloud edition, mode, quota, pricing, and security documentation before production execution.
AtlasMart keeps project ID
demo-atlasmart-firestore, Standard-edition Native
mode, database (default), Node.js 22+, Firebase
CLI course baseline 15.30.0, Firebase JavaScript
SDK 12.19.0, Firebase Admin Node SDK
14.4.0,
@firebase/rules-unit-testing 5.0.2, Firestore
emulator 127.0.0.1:8080, Auth emulator
127.0.0.1:9099, and Emulator UI
127.0.0.1:4000. Mandatory work uses a
demo- project and local emulators only. Cloud
deployment, IAM, App Check enforcement, billing, production
indexes, quotas, contention, Query Insights/Key Visualizer,
backup/PITR, and Enterprise/MongoDB-compatibility canaries are
explicitly optional managed checks, never silently inferred
from emulator success.
2. Source-of-truth files
rules_version = '2';service cloud.firestore { match /databases/{database}/documents { function signedIn() { return request.auth != null; } function sameTenant(tenantId) { return signedIn() && request.auth.token.tenantId == tenantId; } match /products/{productId} { allow read: if resource.data.visibility == "public" || sameTenant(resource.data.tenantId); allow write: if sameTenant(request.resource.data.tenantId) && request.resource.data.schemaVersion in [1, 2]; } match /orders/{orderId} { allow read, write: if sameTenant(resource.data.tenantId); allow create: if sameTenant(request.resource.data.tenantId); } }}
{ "indexes": [ { "collectionGroup": "products", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "tenantId", "order": "ASCENDING" }, { "fieldPath": "active", "order": "ASCENDING" }, { "fieldPath": "updatedAt", "order": "DESCENDING" } ] } ], "fieldOverrides": [ { "collectionGroup": "products", "fieldPath": "rawDescription", "indexes": [] } ]}
The repository copy is authoritative for automated releases. If the console is used for emergency response, export/reconcile the change into source control before the next CLI deployment.
3. CLI deployment selectors and what they change
| Command | Effect | Release use |
|---|---|---|
firebase deploy --only firestore |
Deploys Firestore Rules and indexes for configured databases. | Full Firestore configuration promotion. |
firebase deploy --only firestore:rules
|
Deploys Rules for all configured Firestore databases. | Rules-only release after tests. |
firebase deploy --only
firestore:<databaseId>
|
Deploys configured Firestore resources for a specified database. | Named-database targeting where configured. |
firebase firestore:indexes --database (default)
|
Lists deployed indexes. | Post-deploy inventory / drift evidence. |
Rule updates can take time to affect new requests and longer to reach active listeners. A deployment command returning success is not the same as every client observing the new policy immediately.
4. There is no magic dry-run for production semantics
Local Rules tests are strong for authorization logic. Index JSON is reviewable and deployable. But the emulator cannot prove composite-index requirements/readiness. Use a separate managed staging project/database for production-only checks. Do not call a local emulator run a “dry-run deployment.”
set -euo pipefail# 1. Local deterministic gatefirebase emulators:exec --project demo-atlasmart-firestore \ --only auth,firestore "npm run test:rules && npm run test:integration"# 2. Explicit human/CI identity chooses STAGING, never implicit active projectfirebase deploy --project atlasmart-staging --only firestore:rulesfirebase deploy --project atlasmart-staging --only firestore# 3. Run bounded staging canary that exercises each new query shapenode scripts/canary-staging.mjs# 4. Production promotion is a separate approved job# firebase deploy --project atlasmart-prod --only firestore:rules# firebase deploy --project atlasmart-prod --only firestore
5. Deployment ordering is a compatibility problem
| Change | Safer order | Why |
|---|---|---|
| New query needs new composite index | Deploy/build index → verify ready → release query code. | Old app continues working while index builds; new code never sees missing-index failure. |
| Rules become stricter after schema v2 exists | Release dual-compatible writers/readers → backfill → measure old-client share → tighten Rules. | Old clients do not suddenly lose access. |
| Rules need a new claim | Issue/refresh claim and make app tolerate absence → then require it. | Already-issued tokens do not magically gain new claims. |
| Remove obsolete index | Stop query path → observe absence → delete index. | Avoid production errors caused by removing support before callers migrate. |
6. CI diff gate: reject unreviewed Rules/index drift
import fs from "node:fs";import crypto from "node:crypto";const files = ["firestore.rules", "firestore.indexes.json"];for (const file of files) { const text = fs.readFileSync(file, "utf8"); const sha = crypto.createHash("sha256").update(text).digest("hex"); console.log(`${file} ${sha}`); if (/(?:TO\x44O)|allow read, write: if true/.test(text)) { throw new Error(`unsafe marker in ${file}`); }}JSON.parse(fs.readFileSync("firestore.indexes.json", "utf8"));
Store hashes in the release record with commit SHA, Firebase CLI version, project/database ID and test results. The hash does not prove correctness; it proves which exact configuration was reviewed and deployed.
7. Rules rollback and index rollback are not symmetric
Rules can be restored to a previous reviewed ruleset, but active clients may experience propagation delay. An index change has build/delete lifecycle and query dependencies. Therefore rollback must be application-aware:
- Feature flag can disable the new query/write path without waiting for configuration rollback.
- Previous Rules source is tagged and has regression tests.
- Index removal is never the first rollback action; disable caller first.
- Rollback criteria include deny-rate, permission errors, missing-index errors and dual-read mismatch—not only deployment status.
8. Controlled failure: deploy rules and indexes together with unready app
Failure: a one-shot release simultaneously removes schema-v1 access, deploys an index, and releases schema-v2-only code. Old clients fail immediately; new code can race index build.
Repair: expand compatibility first, add index, verify, release code behind flag, backfill, observe, then contract Rules/schema. This is the database version of expand–migrate–contract.
9. Mandatory lab
-
Add a composite query to AtlasMart products:
tenantId == t-red,active == true, order byupdatedAt desc. - Show that it runs in the emulator even if the composite index definition is temporarily removed.
- Restore the source-controlled index JSON and record its SHA.
- Run Rules tests for tenant isolation and schema versions 1/2.
-
Simulate staging index readiness with a deterministic state
file (
CREATING → READY); gate app flag onREADY. Clearly label this as simulation, not managed-index proof. - Exercise rollback by disabling the feature flag first and restoring the prior Rules file in the local test harness.
A real staging database is required to prove the composite index is deployed/ready and the query succeeds under production index enforcement.
Production judgment and bridge
Configuration-as-code reduces drift only when deployment ordering and rollback are designed with client compatibility. Lesson 4 applies the same expand–migrate–contract discipline to document schema evolution and long-running backfills.
Knowledge check
- Why can a query pass locally yet fail in production for an index?
- What happens to console Rules when the CLI deploys the repository Rules?
- Why deploy an index before releasing the dependent query?
- Why is a feature flag part of database rollback?
- Why is index rollback not simply “delete the new index”?
Review the answers
1. The emulator does not track compound indexes.
2. The CLI source overwrites the deployed Rules for that target, so unreconciled console changes can be lost.
3. Index build/readiness may lag deployment; prebuilding preserves old behavior until ready.
4. It can stop the new application path immediately while slower config/data rollback proceeds.
5. Existing/new code may depend on it, and index deletion has its own lifecycle; disable callers first.
Summary and next step
This lesson established the working contract for Version Security Rules and Index Definitions, CI Validation, Staged Deployment, and Rollback. Keep its edition/mode assumptions, trust boundary, verification evidence, and operational constraints explicit when reusing the pattern.
Next, continue to Backward-Compatible Document Migrations, Dual Readers/Writers, Backfills, and Feature Flags.
Authoritative references
- Firebase: Connect your app to the Cloud Firestore Emulator — demo projects, edition selection, Admin SDK environment variables, reset/import/export, Rules reports, and production differences.
-
Firebase: Install, configure and integrate Local Emulator
Suite
—
emulators:exec, import/export and CI workflow. - Firebase: Test Cloud Firestore Security Rules — emulator rules testing and CI execution.
-
Firebase: Build Security Rules unit tests
—
@firebase/rules-unit-testing, mocked auth, clearing state and disabled-rules fixture setup. - Firebase: Manage and deploy Security Rules — source control, local testing and selective deployment.
- Firebase: Manage indexes in Cloud Firestore — source-controlled index definitions and CLI deployment.
- Firebase: Cloud Firestore index definition reference — composite, field override and vector index JSON shapes.
- Firebase CLI reference — configured multi-database rules/indexes and deployment selectors.