Firebaseを使うアプリでは、開発中の操作が本番データへ影響しないよう、開発環境と本番環境を分離します。
推奨する基本構成は、Firebaseプロジェクト自体を分け、Androidのproduct flavorごとに接続先を固定する方法です。
推奨構成
| 環境 | applicationId | Firebase project_id | 表示名 |
|---|---|---|---|
| 開発 | com.example.app.dev | example-app-dev | Example Dev |
| 本番 | com.example.app | example-app-prod | Example |
この構成なら、同じ端末へ開発版と本番版を同時にインストールできます。
なぜFirebaseプロジェクトを分けるのか
一つのFirebaseプロジェクトへ開発版と本番版のAndroidアプリを登録しても、Firestore、Authentication、Storageなどのバックエンドは同じプロジェクトです。
本番データを隔離するには、プロジェクトを分けます。
分離できるもの
- Authenticationユーザー
- Firestoreデータ
- Realtime Database
- Cloud Storage
- Cloud Functions
- Security Rules
- Remote Config
- Cloud Messaging
- Crashlytics
- Analytics
- App Check
ステップ1:Firebaseプロジェクトを作成
例:
example-app-dev
example-app-prod
example-appとexample-app-2のような曖昧な名前は避け、コンソールやCLIで見ただけで環境を判断できる名前にします。
ステップ2:Androidアプリを登録
開発用プロジェクト:
com.example.app.dev
本番用プロジェクト:
com.example.app
各Firebaseプロジェクトから対応するgoogle-services.jsonを取得します。
ステップ3:product flavorを設定
Kotlin DSL:
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")
}
}
}
生成されるvariant:
developmentDebug
developmentRelease
productionDebug
productionRelease
ステップ4:JSONをflavor別に配置
app/src/development/google-services.json
app/src/production/google-services.json
開発ファイル:
{
"project_info": {
"project_id": "example-app-dev"
}
}
本番ファイル:
{
"project_info": {
"project_id": "example-app-prod"
}
}
ルートのapp/google-services.jsonへ本番用ファイルだけを置くと、development側の配置漏れに気づきにくくなります。環境別運用では専用ディレクトリへ明示します。
ステップ5:表示名とアイコンを分ける
開発版を本番版と見間違えないようにします。
- 表示名にDevを付ける
- 開発版アイコンへDEVバッジを付ける
- 画面に環境表示を出す
- 開発版だけデバッグメニューを有効にする
本番版で環境名を常時表示する必要はありません。
接続先をログで確認する
Android:
val app = FirebaseApp.getInstance()
Log.d("FirebaseEnv", "project=${app.options.projectId}")
Flutter:
final app = Firebase.app();
debugPrint('project=${app.options.projectId}');
期待値:
development → example-app-dev
production → example-app-prod
Flutterで環境を分ける
ディレクトリ例:
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
開発用:
flutterfire configure \
--project=example-app-dev \
--android-package-name=com.example.app.dev \
--out=lib/firebase_options_development.dart
本番用:
flutterfire configure \
--project=example-app-prod \
--android-package-name=com.example.app \
--out=lib/firebase_options_production.dart
共通bootstrap
Future<void> bootstrap({
required FirebaseOptions options,
}) async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: options);
runApp(const MyApp());
}
開発版を起動
flutter run \
--flavor development \
--target lib/main_development.dart
本番AAB
flutter build appbundle \
--flavor production \
--target lib/main_production.dart
Firebase CLIを分ける
プロジェクトエイリアスを追加します。
firebase use --add
.firebaserc:
{
"projects": {
"dev": "example-app-dev",
"prod": "example-app-prod"
}
}
デプロイ時は対象を明示します。
firebase deploy --project=dev
firebase deploy --project=prod
本番デプロイで現在のactive projectだけに依存しない方が安全です。
CIでの検証
本番ビルド前に次を検証します。
- flavorがproduction
- applicationIdが
com.example.app - JSONのproject_idが
example-app-prod - 本番用署名設定
- 本番用API・Webhook
JSONの確認例:
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
Googleログイン・SHAも環境別
開発版:
- package:
com.example.app.dev - debug SHA-1・SHA-256
本番版:
- package:
com.example.app - Google Playアプリ署名鍵のSHA-1・SHA-256
各Firebaseプロジェクトの正しいAndroidアプリへ登録します。
Security Rulesも別管理
開発プロジェクトの緩いRulesを本番へ誤デプロイしないようにします。
本番では次を確認します。
- 認証必須
- 所有者チェック
- データ形式
- ロール
- Storageパス
- App Check
- インデックス
構成ファイルを秘密にすることだけではデータを保護できません。
Local Emulator Suiteとの使い分け
ローカル単体・結合テスト
→ Emulator Suite
実機・共有テスト
→ 開発Firebaseプロジェクト
Google Play製品版
→ 本番Firebaseプロジェクト
エミュレータで確認できる処理は、本番クラウドへ接続せずに試します。
避けたい運用
- 実行中のスイッチで開発・本番を切り替える
- 本番JSONをアプリモジュール直下へ共通配置
- Dart側だけ切り替えてネイティブ設定を共通にする
- 開発版と本番版を同じ表示名・アイコンにする
- CLIのactive projectだけを信頼する
- 開発用の緩いRulesを本番へコピーする
まとめ
安全な環境分離は次のセットで行います。
product flavor
applicationId
Firebase project_id
google-services.json
FirebaseOptions
署名・SHA
CLIデプロイ先
本番ビルド前に、flavor・applicationId・project_idの3点を自動検証すると、誤接続を大きく減らせます。
一次情報
公式参考資料
Google PlayやFirebaseの画面・要件は変更される場合があります。公開作業の前に、リンク先の最新情報も確認してください。