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.

Advanced · 180–240 minutesRules/indexes as code · staged deployment · index readiness · rollbackNode 22+ · Firebase CLI course baseline 15.30.0 · JS SDK 12.19.0 · Admin SDK 14.4.0 · rules-unit-testing 5.0.2Mandatory lab demo project + Emulator Suite/no-cost · managed staging/production canaries explicitly optionalLast reviewed: 17 September 2026

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.

Learning outcomes
  • 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.
Execution and safety note

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.

Chapter 26 reproducibility baseline · reviewed 17 September 2026

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

firestore.rules
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);    }  }}
firestore.indexes.json
{  "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.
Propagation is not instantaneous

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.”

staged-release.sh
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

scripts/config-gate.mjs
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

  1. Add a composite query to AtlasMart products: tenantId == t-red, active == true, order by updatedAt desc.
  2. Show that it runs in the emulator even if the composite index definition is temporarily removed.
  3. Restore the source-controlled index JSON and record its SHA.
  4. Run Rules tests for tenant isolation and schema versions 1/2.
  5. Simulate staging index readiness with a deterministic state file (CREATING → READY); gate app flag on READY. Clearly label this as simulation, not managed-index proof.
  6. Exercise rollback by disabling the feature flag first and restoring the prior Rules file in the local test harness.
Production-only check

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

  1. Why can a query pass locally yet fail in production for an index?
  2. What happens to console Rules when the CLI deploys the repository Rules?
  3. Why deploy an index before releasing the dependent query?
  4. Why is a feature flag part of database rollback?
  5. 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

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.