TestCrew
Firebase and Android Setup

How to Separate Firebase Development and Production Environments

Separate Firebase development and production with distinct projects, Android product flavors, google-services.json files, FlutterFire options, and CI checks.

Published
August 2, 2026
Updated
August 5, 2026
Official info checked
August 3, 2026
TestCrew guide cover showing separate Firebase development and production environments

An app that uses Firebase should isolate development activity from production data.

A practical default architecture is to use separate Firebase projects and bind each Android product flavor to one fixed environment.

EnvironmentapplicationIdFirebase project_idApp name
Developmentcom.example.app.devexample-app-devExample Dev
Productioncom.example.appexample-app-prodExample

Because the package names differ, both apps can be installed on the same device at the same time.

Why separate Firebase projects?

Registering both Android apps in one Firebase project does not isolate their backend data. Firestore, Authentication, Storage, and other products still belong to that shared project.

Use separate Firebase projects when development data must not affect production.

This separates resources such as:

  • Authentication users
  • Firestore data
  • Realtime Database
  • Cloud Storage
  • Cloud Functions
  • Security Rules
  • Remote Config
  • Cloud Messaging
  • Crashlytics
  • Analytics
  • App Check

Step 1: Create clearly named Firebase projects

Example:

example-app-dev
example-app-prod

Avoid ambiguous names such as example-app and example-app-2. The environment should be obvious in the Firebase console, Google Cloud console, CLI, and logs.

Step 2: Register the Android apps

Development project:

com.example.app.dev

Production project:

com.example.app

Download the corresponding google-services.json from each Firebase project.

Step 3: Configure Android product flavors

Kotlin DSL example:

android {
    namespace = "com.example.app"

    defaultConfig {
        applicationId = "com.example.app"
    }

    flavorDimensions += "environment"

    productFlavors {
        create("development") {
            dimension = "environment"
            applicationIdSuffix = ".dev"
            versionNameSuffix = "-dev"
            resValue("string", "app_name", "Example Dev")
        }

        create("production") {
            dimension = "environment"
            resValue("string", "app_name", "Example")
        }
    }
}

This creates variants such as:

developmentDebug
developmentRelease
productionDebug
productionRelease

Step 4: Place the JSON file by flavor

app/src/development/google-services.json
app/src/production/google-services.json

Development configuration:

{
  "project_info": {
    "project_id": "example-app-dev"
  }
}

Production configuration:

{
  "project_info": {
    "project_id": "example-app-prod"
  }
}

Avoid keeping only the production file at app/google-services.json in a flavor-based setup. Explicit flavor directories make missing or incorrect environment configuration easier to detect.

Step 5: Make development builds visually distinct

Reduce the risk of using the wrong app or environment.

Common safeguards include:

  • Add “Dev” to the development app name
  • Add a DEV badge to the development icon
  • Show the active environment inside debug builds
  • Enable internal debugging tools only in development

The production app does not need a permanent environment label.

Log the active Firebase project

Android:

val app = FirebaseApp.getInstance()
Log.d("FirebaseEnv", "project=${app.options.projectId}")

Flutter:

final app = Firebase.app();
debugPrint('project=${app.options.projectId}');

Expected values:

development → example-app-dev
production  → example-app-prod

Do not log secrets or user data while checking the environment.

Separate environments in Flutter

Example structure:

lib/
├── bootstrap.dart
├── main_development.dart
├── main_production.dart
├── firebase_options_development.dart
└── firebase_options_production.dart

android/app/src/
├── development/google-services.json
└── production/google-services.json

Generate development options:

flutterfire configure \
  --project=example-app-dev \
  --android-package-name=com.example.app.dev \
  --out=lib/firebase_options_development.dart

Generate production options:

flutterfire configure \
  --project=example-app-prod \
  --android-package-name=com.example.app \
  --out=lib/firebase_options_production.dart

Shared bootstrap

Future<void> bootstrap({
  required FirebaseOptions options,
}) async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: options);
  runApp(const MyApp());
}

Run development

flutter run \
  --flavor development \
  --target lib/main_development.dart

Build the production AAB

flutter build appbundle \
  --flavor production \
  --target lib/main_production.dart

Ensure each entry point passes the matching generated FirebaseOptions to the shared bootstrap.

Separate Firebase CLI targets

Add project aliases:

firebase use --add

Example .firebaserc:

{
  "projects": {
    "dev": "example-app-dev",
    "prod": "example-app-prod"
  }
}

Specify the target during deployment:

firebase deploy --project=dev
firebase deploy --project=prod

For production deployments, do not rely only on whichever project is currently active in a developer’s local CLI session.

Validate the environment in CI

Before a production build, verify at least:

  • The selected flavor is production
  • The final applicationId is com.example.app
  • The JSON project_id is example-app-prod
  • Production signing is active
  • Production API and webhook endpoints are selected

Example JSON check:

python - <<'PY'
import json
from pathlib import Path

path = Path('android/app/src/production/google-services.json')
data = json.loads(path.read_text())
actual = data['project_info']['project_id']
expected = 'example-app-prod'
if actual != expected:
    raise SystemExit(f'Wrong Firebase project: {actual}')
print(actual)
PY

Failing the build is safer than publishing an app connected to the wrong backend.

Configure Google Sign-In and SHA by environment

Development:

  • Package: com.example.app.dev
  • Debug SHA-1 and SHA-256

Production:

  • Package: com.example.app
  • Google Play app-signing SHA-1 and SHA-256

Register each fingerprint on the correct Android app inside the correct Firebase project.

Manage Security Rules separately

Do not accidentally deploy permissive development rules to production.

Production reviews should include:

  • Authentication requirements
  • Ownership checks
  • Data-shape validation
  • Roles and permissions
  • Storage paths
  • App Check
  • Required indexes

Keeping configuration files private is not a substitute for secure backend rules.

When to use the Local Emulator Suite

A useful separation is:

Local unit and integration testing
→ Firebase Local Emulator Suite

Shared device and team testing
→ Development Firebase project

Google Play production app
→ Production Firebase project

Use emulators for workflows that do not need the real production cloud environment.

Practices to avoid

  • Switching development and production through a runtime toggle
  • Sharing one production JSON from the app-module root
  • Switching only Dart options while leaving native configuration shared
  • Giving development and production identical names and icons
  • Trusting only the Firebase CLI active-project state
  • Copying permissive development Security Rules into production

Summary

A safe environment boundary includes all of the following:

product flavor
applicationId
Firebase project_id
google-services.json
FirebaseOptions
signing and SHA configuration
CLI deployment target

Automatically checking the flavor, final application ID, and Firebase project ID before a production build significantly reduces accidental cross-environment connections.

For configuration-file details, see What Is google-services.json?.

Primary sources

Official references

Check the linked official documentation before a production release.

Continue reading

TestCrew

Find testers through mutual testing

Test other Android apps, provide useful feedback, and use earned credits to recruit testers for your own Google Play closed test.

Learn how TestCrew works

Analytics settings

TestCrew uses Google Analytics provided by Google LLC to improve the website. You can change whether it is used; this applies to future measurement on this page and later visits. See the Privacy Policy.