はじめに
本番サーバーに直接接続してALTER TABLEを実行したことはありませんか? 「昨日ステージングに反映したスキーマ変更が本番には適用されていなくて障害が発生しました」という報告を受けたことはありませんか?
データベーススキーマを手動で管理することは、アプリケーションコードをGitなしで管理するのと変わりません。 スキーマ変更履歴を追跡できず、環境間の不整合が発生し、ロールバックも不可能です。
DBマイグレーションツールはこの問題を解決します。 この記事では、最も広く使われているDBマイグレーションツールであるFlywayを深く掘り下げます。
1. DBマイグレーションとは
1.1 なぜ必要なのか
データベースマイグレーションとは、DBスキーマの変更を体系的に管理し、バージョンごとに順次適用するプロセスです。
手動管理の問題点:
- どの変更がどの環境に適用されたか追跡不可能
- 開発/ステージング/本番環境間のスキーマ不整合
- 複数の開発者が同時にスキーマを変更するとコンフリクト発生
- 誤ったDDLを実行するとロールバックが困難
- 監査(Audit)追跡が不可能
1.2 バージョン管理の重要性
アプリケーションコードはGitでバージョン管理しているのに、なぜDBスキーマは管理しないのでしょうか? DBマイグレーションツールはスキーマ変更をコードとして管理し、誰がいつどの変更を行ったかを追跡可能にします。
app-code v1.0 -> v1.1 -> v1.2 -> v2.0
| | | |
db-schema V1 -> V2 -> V3 -> V4
アプリケーションバージョンとDBスキーマバージョンを一緒に管理することで、どの時点のコードがどのスキーマを期待するかが明確になります。
2. Flyway vs Liquibase vs Alembic
| 項目 | Flyway | Liquibase | Alembic |
|---|---|---|---|
| 言語 | Java | Java | Python |
| スキーマ定義 | SQLファイルベース | XML/YAML/JSON/SQL | Pythonコード |
| 学習コスト | 低い | 中程度 | 中程度 |
| DB対応 | 20以上 | 50以上 | SQLAlchemy対応DB |
| ロールバック | 有料(Teams)または手動 | 内蔵(auto-rollback) | 内蔵(downgrade) |
| Spring Boot連携 | ネイティブ対応 | ネイティブ対応 | 該当なし |
| 価格 | Community無料 | Community無料 | 完全無料 |
| 哲学 | SQL中心、シンプル | 抽象化レイヤー | ORM連携 |
選択基準:
- Flyway: SQLを直接書きたい、シンプルさを重視するチーム。Java/Spring Bootプロジェクトに最適
- Liquibase: DBベンダー非依存のスキーマ管理が必要なチーム。自動ロールバックが重要な場合
- Alembic: Python/SQLAlchemyベースのプロジェクト
3. Flywayのインストール
3.1 CLIインストール
# macOS
brew install flyway
# Linux (tar.gz)
wget -qO- https://download.red-gate.com/maven/release/com/redgate/flyway/flyway-commandline/10.x/flyway-commandline-10.x-linux-x64.tar.gz | tar xz
sudo ln -s $(pwd)/flyway-10.x/flyway /usr/local/bin
# バージョン確認
flyway --version
3.2 Docker
docker run --rm flyway/flyway:10 -url=jdbc:postgresql://host:5432/mydb -user=admin -password=secret migrate
Docker Composeでの使用:
services:
flyway:
image: flyway/flyway:10
command: migrate
volumes:
- ./sql:/flyway/sql
- ./conf:/flyway/conf
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin -d mydb"]
interval: 5s
timeout: 5s
retries: 5
3.3 Maven/Gradle
<!-- Maven pom.xml -->
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>10.15.0</version>
<configuration>
<url>jdbc:postgresql://localhost:5432/mydb</url>
<user>admin</user>
<password>secret</password>
</configuration>
</plugin>
// Gradle build.gradle
plugins {
id "org.flywaydb.flyway" version "10.15.0"
}
flyway {
url = 'jdbc:postgresql://localhost:5432/mydb'
user = 'admin'
password = 'secret'
}
3.4 Spring Boot統合
Spring Bootでは、依存関係を追加するだけで起動時に自動的にマイグレーションが実行されます。
<!-- pom.xml -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
# application.yml
spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
baseline-version: '0'
4. コアコンセプト
4.1 マイグレーションファイルの命名規則
Flywayはファイル名の規則でマイグレーションのタイプ、バージョン、説明を区別します。
V2__add_email_column.sql
| | +-- 説明(アンダースコア2つで区切り)
| +-- バージョン番号
+-- タイプ (V=Versioned, U=Undo, R=Repeatable)
命名ルール:
- バージョン区切り: アンダースコア2つ(
__) - 単語区切り: アンダースコア1つ(
_) - バージョン番号: 数字、ドット(
.)、アンダースコア使用可能 - 例:
V1__init.sql,V1.1__add_index.sql,V2_1__create_orders.sql
4.2 flyway_schema_historyテーブル
Flywayはどのマイグレーションが適用されたかをflyway_schema_historyテーブルに記録します。
SELECT installed_rank, version, description, checksum, installed_on, success
FROM flyway_schema_history
ORDER BY installed_rank;
installed_rank | version | description | checksum | installed_on | success
----------------+---------+-----------------------+-------------+----------------------+---------
1 | 1 | init | 1234567890 | 2026-04-01 10:00:00 | t
2 | 2 | add email column | -987654321 | 2026-04-05 14:30:00 | t
3 | 3 | create orders table | 1122334455 | 2026-04-10 09:15:00 | t
4.3 チェックサム
Flywayは各マイグレーションファイルのチェックサム(CRC32)を計算して記録します。 一度適用されたマイグレーションファイルを変更するとチェックサムが変わり、次回実行時にエラーが発生します。
これはマイグレーションの不変性を保証します。 一度適用されたマイグレーションは絶対に変更せず、新しいマイグレーションを追加する必要があります。
5. マイグレーションの種類
5.1 Versioned Migration (V)
最も基本的なマイグレーションで、バージョン番号順に一度だけ実行されます。
-- V1__create_users_table.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_users_username ON users(username);
5.2 Undo Migration (U) -- Teams Edition
Versioned Migrationの逆方向を定義します。Flyway Teams(有料)の機能です。
-- U1__create_users_table.sql
DROP TABLE IF EXISTS users;
5.3 Repeatable Migration (R)
バージョン番号がなく、ファイル内容が変更されるたびに再実行されます。 ビュー、関数、プロシージャなどの管理に適しています。
-- R__refresh_user_statistics_view.sql
CREATE OR REPLACE VIEW user_statistics AS
SELECT
u.id,
u.username,
COUNT(o.id) AS order_count,
COALESCE(SUM(o.total_amount), 0) AS total_spent,
MAX(o.created_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username;
5.4 コールバック
マイグレーションライフサイクルの特定のポイントで実行されるスクリプトです。
sql/
beforeMigrate.sql -- マイグレーション開始前
afterMigrate.sql -- マイグレーション完了後
beforeEachMigrate.sql -- 各マイグレーションファイル実行前
afterEachMigrate.sql -- 各マイグレーションファイル実行後
beforeValidate.sql -- バリデーション前
活用例 -- マイグレーション後の統計更新:
-- afterMigrate.sql
ANALYZE;
SELECT schemaname, tablename, last_analyze
FROM pg_stat_user_tables
WHERE schemaname = 'public';
6. Flywayコマンド
6.1 migrate
未適用のマイグレーションを順番に実行します。
flyway migrate
6.2 info
現在のマイグレーション状態を表示します。
flyway info
+-----------+---------+---------------------+----------+---------------------+----------+----------+
| Category | Version | Description | Type | Installed On | State | Undoable |
+-----------+---------+---------------------+----------+---------------------+----------+----------+
| Versioned | 1 | init | SQL | 2026-04-01 10:00:00 | Success | No |
| Versioned | 2 | add email column | SQL | 2026-04-05 14:30:00 | Success | No |
| Versioned | 3 | create orders table | SQL | | Pending | No |
+-----------+---------+---------------------+----------+---------------------+----------+----------+
6.3 validate
適用済みマイグレーションのチェックサムとファイルシステム上のチェックサムを比較します。
flyway validate
CI/CDパイプラインでmigrateを実行する前に、まずvalidateを実行することをお勧めします。
6.4 repair
失敗したマイグレーション記録を整理します。
flyway repair
主な動作:
- 失敗したマイグレーション記録をflyway_schema_historyから削除
- 適用済みマイグレーションのチェックサムを現在のファイルに基づいて再計算
6.5 clean
注意: すべてのオブジェクトを削除します。本番では絶対に使用禁止!
flyway clean
開発環境でクリーンな状態から再開始する際に使用します。
Flyway 10以降、flyway.cleanDisabled=falseを明示的に設定する必要があります。
6.6 baseline
既存のDBにFlywayを導入する際に使用します。
flyway baseline -baselineVersion=5 -baselineDescription="existing schema"
すでにV1からV5までのスキーマが適用されているDBにFlywayを導入する場合、baselineをV5に設定し、V6から新しいマイグレーションを追加します。
7. 設定ファイル
7.1 flyway.conf
# flyway.conf
flyway.url=jdbc:postgresql://localhost:5432/mydb
flyway.user=admin
flyway.password=secret
flyway.schemas=public
flyway.locations=filesystem:sql
flyway.baselineOnMigrate=true
flyway.baselineVersion=0
flyway.connectRetries=3
flyway.cleanDisabled=true
flyway.outOfOrder=false
flyway.validateOnMigrate=true
7.2 Spring Boot (application.yml)
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: admin
password: secret
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
baseline-version: '0'
clean-disabled: true
validate-on-migrate: true
out-of-order: false
connect-retries: 3
table: flyway_schema_history
7.3 環境別設定
Spring Bootではプロファイルごとに設定を分離します。
# application-dev.yml
spring:
flyway:
clean-disabled: false
locations: classpath:db/migration,classpath:db/seed
# application-prod.yml
spring:
flyway:
clean-disabled: true
locations: classpath:db/migration
validate-on-migrate: true
8. 実践例
8.1 V1 -- ユーザーテーブル作成
-- V1__create_users_table.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(255),
password_hash VARCHAR(255) NOT NULL,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT uq_users_username UNIQUE (username)
);
COMMENT ON TABLE users IS 'ユーザー情報';
COMMENT ON COLUMN users.username IS 'ログインID';
8.2 V2 -- メール一意制約追加
-- V2__add_email_unique_constraint.sql
-- 既存の重複データ整理
DELETE FROM users a
USING users b
WHERE a.id > b.id
AND a.email = b.email
AND a.email IS NOT NULL;
-- NOT NULL制約追加
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
-- UNIQUEインデックス追加
CREATE UNIQUE INDEX CONCURRENTLY idx_users_email ON users(email);
ALTER TABLE users ADD CONSTRAINT uq_users_email UNIQUE USING INDEX idx_users_email;
8.3 V3 -- 注文テーブル作成
-- V3__create_orders_table.sql
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
order_number VARCHAR(20) NOT NULL UNIQUE,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
total_amount DECIMAL(12, 2) NOT NULL DEFAULT 0,
currency VARCHAR(3) NOT NULL DEFAULT 'KRW',
shipping_address TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT chk_orders_status CHECK (status IN ('PENDING', 'CONFIRMED', 'SHIPPED', 'DELIVERED', 'CANCELLED'))
);
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_created_at ON orders(created_at DESC);
COMMENT ON TABLE orders IS '注文情報';
8.4 R -- ビュー更新
-- R__refresh_views.sql
CREATE OR REPLACE VIEW v_user_order_summary AS
SELECT
u.id AS user_id,
u.username,
u.email,
COUNT(o.id) AS total_orders,
COALESCE(SUM(o.total_amount), 0) AS total_spent,
COALESCE(AVG(o.total_amount), 0) AS avg_order_amount,
MAX(o.created_at) AS last_order_at
FROM users u
LEFT JOIN orders o ON u.id = o.user_id AND o.status != 'CANCELLED'
GROUP BY u.id, u.username, u.email;
CREATE OR REPLACE VIEW v_daily_order_stats AS
SELECT
DATE(created_at) AS order_date,
COUNT(*) AS order_count,
SUM(total_amount) AS daily_revenue,
AVG(total_amount) AS avg_order_value,
COUNT(DISTINCT user_id) AS unique_customers
FROM orders
WHERE status NOT IN ('CANCELLED')
GROUP BY DATE(created_at);
8.5 プロジェクトディレクトリ構造
src/main/resources/
db/
migration/
V1__create_users_table.sql
V2__add_email_unique_constraint.sql
V3__create_orders_table.sql
V4__create_order_items_table.sql
V5__add_user_profile_fields.sql
R__refresh_views.sql
R__update_functions.sql
seed/
V100__insert_test_users.sql
V101__insert_test_orders.sql
9. チーム協業戦略
9.1 ブランチごとのマイグレーション競合
複数の開発者が同時にマイグレーションを作成すると、バージョン番号が競合する可能性があります。
タイムスタンプベースのバージョン番号を使用:
V20260412_001__feature_a_create_table.sql (開発者A)
V20260412_002__feature_b_add_column.sql (開発者B)
またはoutOfOrderを有効化:
spring:
flyway:
out-of-order: true
このオプションを有効にすると、V3の後にV2.5が追加されても実行されます。 ただし、チーム内で明確なルールを定めることが優先です。
9.2 命名規約
チームで合意すべき事項:
# 方法1: 連番
V1__create_users.sql
V2__add_index.sql
# 方法2: 日付ベース
V20260412.1__create_users.sql
V20260412.2__add_index.sql
# 方法3: チケット番号を含む
V1__JIRA_123_create_users.sql
V2__JIRA_456_add_index.sql
9.3 コードレビューチェックリスト
マイグレーションPRレビュー時の確認事項:
- ファイル名が命名規約に従っているか
- 既存のマイグレーションファイルを変更していないか
- 大規模テーブルにロックを引き起こすDDLはないか
- ロールバックスクリプトが準備されているか
- インデックス作成時にCONCURRENTLYオプションを使用しているか
10. CI/CD連携
10.1 GitHub Actions
name: Database Migration
on:
push:
branches: [main]
paths:
- 'src/main/resources/db/migration/**'
jobs:
migrate:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Run Flyway Validate
run: |
docker run --rm --network host \
-v $(pwd)/src/main/resources/db/migration:/flyway/sql \
flyway/flyway:10 \
-url=jdbc:postgresql://localhost:5432/testdb \
-user=test \
-password=test \
validate
- name: Run Flyway Migrate
run: |
docker run --rm --network host \
-v $(pwd)/src/main/resources/db/migration:/flyway/sql \
flyway/flyway:10 \
-url=jdbc:postgresql://localhost:5432/testdb \
-user=test \
-password=test \
migrate
- name: Run Flyway Info
run: |
docker run --rm --network host \
-v $(pwd)/src/main/resources/db/migration:/flyway/sql \
flyway/flyway:10 \
-url=jdbc:postgresql://localhost:5432/testdb \
-user=test \
-password=test \
info
10.2 Kubernetes Init Container
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
initContainers:
- name: flyway-migrate
image: flyway/flyway:10
args: ["migrate"]
env:
- name: FLYWAY_URL
value: "jdbc:postgresql://postgres-service:5432/mydb"
- name: FLYWAY_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: FLYWAY_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
volumeMounts:
- name: migration-scripts
mountPath: /flyway/sql
containers:
- name: app
image: my-app:latest
volumes:
- name: migration-scripts
configMap:
name: flyway-migrations
11. 無停止マイグレーション
11.1 危険なDDL操作
本番環境で注意が必要な操作:
| 操作 | リスク | 理由 |
|---|---|---|
| NOT NULL追加 | 高 | テーブル全体スキャンが必要 |
| カラム型変更 | 高 | テーブル全体リライト |
| インデックス作成 | 中 | テーブルロック(CONCURRENTLY未使用時) |
| カラム追加(デフォルトなし) | 低 | メタデータのみ変更 |
| カラム追加(デフォルトあり) | 中 | PostgreSQL 11以前は全体リライト |
11.2 Expand-Contractパターン
カラム名を変更する安全な方法:
Phase 1 -- Expand(拡張):
-- V10__expand_add_new_column.sql
ALTER TABLE users ADD COLUMN full_name VARCHAR(100);
-- 既存データのコピー
UPDATE users SET full_name = name WHERE full_name IS NULL;
-- 両方のカラムに書き込むトリガー設定
CREATE OR REPLACE FUNCTION sync_user_name()
RETURNS TRIGGER AS '
BEGIN
IF TG_OP = ''INSERT'' OR TG_OP = ''UPDATE'' THEN
IF NEW.name IS DISTINCT FROM OLD.name THEN
NEW.full_name := NEW.name;
END IF;
IF NEW.full_name IS DISTINCT FROM OLD.full_name THEN
NEW.name := NEW.full_name;
END IF;
END IF;
RETURN NEW;
END;
' LANGUAGE plpgsql;
CREATE TRIGGER trg_sync_user_name
BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION sync_user_name();
Phase 2 -- アプリケーションデプロイ:
新しいコードはfull_nameカラムを使用するように変更します。
Phase 3 -- Contract(縮小):
-- V11__contract_remove_old_column.sql
DROP TRIGGER IF EXISTS trg_sync_user_name ON users;
DROP FUNCTION IF EXISTS sync_user_name();
ALTER TABLE users DROP COLUMN name;
11.3 大規模テーブルへのインデックス追加
-- V12__add_index_concurrently.sql
-- FlywayでCONCURRENTLYを使用するには、トランザクションを無効化する必要があります
-- flyway.confで設定: flyway.postgresql.transactional.lock=false
-- またはSpring Bootで別途non-transactional migrationを使用
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_orders_created_at_status
ON orders(created_at, status);
12. トラブルシューティング
12.1 チェックサム不一致
症状: Migration checksum mismatch エラー
原因: すでに適用されたマイグレーションファイルが変更された
解決方法:
# 方法1: repairでチェックサムを更新(変更が意図的な場合)
flyway repair
# 方法2: 元のファイルを復元(誤って変更した場合)
git checkout -- src/main/resources/db/migration/V1__init.sql
12.2 失敗したマイグレーションの復旧
症状: マイグレーションが途中で失敗し、DB状態が不整合
# 1. 失敗状態の確認
flyway info
# 2. 失敗したマイグレーション記録の削除
flyway repair
# 3. SQLファイルを修正して再実行
flyway migrate
PostgreSQLはDDLがトランザクションをサポートするため、失敗時は自動ロールバックされます。 しかし、MySQLはDDLが暗黙的にコミットされるため、手動復旧が必要な場合があります。
12.3 baselineの活用
既存プロジェクトにFlywayを導入する場合:
# 1. 現在のスキーマをV1としてダンプ
pg_dump --schema-only mydb > V1__baseline.sql
# 2. baseline設定
flyway baseline -baselineVersion=1
# 3. 以降のマイグレーションはV2から作成
12.4 よくある間違い
間違い1: 適用済みマイグレーションファイルの修正
- 常に新しいマイグレーションを追加してください
間違い2: バージョン番号の飛ばし(V1, V3, V5)
- Flywayはデフォルトで連番を期待します
間違い3: 開発環境でclean後に本番設定をコピー
- 環境ごとに設定ファイルを分離してください
間違い4: マイグレーションにDMLとDDLの混在
- 可能な限り分離してください。DDL失敗時にDMLも一緒にロールバックされる可能性があります
13. ベストプラクティス
13.1 ロールバック戦略
Flyway Communityでは自動Undoがないため、手動ロールバックスクリプトを準備します。
sql/
migration/
V5__add_payment_table.sql
rollback/
V5__rollback_add_payment_table.sql
-- V5__rollback_add_payment_table.sql (手動実行用)
DROP TABLE IF EXISTS payments;
DELETE FROM flyway_schema_history WHERE version = '5';
13.2 データマイグレーション
スキーマ変更とデータ変更は別々のマイグレーションに分離します。
-- V6__add_role_column.sql (DDL)
ALTER TABLE users ADD COLUMN role VARCHAR(20) DEFAULT 'USER';
-- V7__migrate_admin_roles.sql (DML)
UPDATE users SET role = 'ADMIN' WHERE username IN ('admin', 'superadmin');
UPDATE users SET role = 'MODERATOR' WHERE username IN ('mod1', 'mod2');
13.3 シードデータ
開発/テスト環境でのみ使用するシードデータは別ディレクトリで管理します。
# application-dev.yml
spring:
flyway:
locations:
- classpath:db/migration
- classpath:db/seed
-- db/seed/V1000__seed_test_users.sql
INSERT INTO users (username, email, password_hash, role) VALUES
('testuser1', 'test1@example.com', 'hashed_pw_1', 'USER'),
('testuser2', 'test2@example.com', 'hashed_pw_2', 'USER'),
('testadmin', 'admin@example.com', 'hashed_pw_3', 'ADMIN')
ON CONFLICT (username) DO NOTHING;
13.4 全体チェックリスト
マイグレーション作成時の確認事項:
- 以前に適用されたマイグレーションを修正していないか
- バージョン番号が既存のマイグレーションと競合していないか
- 本番の大規模テーブルに対するALTER TABLEのロック影響を検討したか
- インデックスはCONCURRENTLYオプションで作成しているか
- ロールバックスクリプトが準備されているか
- データマイグレーションとスキーママイグレーションが分離されているか
- 環境別設定が正しいか
まとめ
Flywayはシンプルながらも強力なDBマイグレーションツールです。 SQLファイルベースのアプローチはDBAと開発者の双方に馴染みやすく、Spring Bootとのネイティブ統合はJavaエコシステムで特に力を発揮します。
重要な原則をまとめると:
- マイグレーションは不変である: 一度適用されたファイルは絶対に変更しない
- 常に前進する: 問題があれば新しいマイグレーションで修正する
- 環境を分離する: dev、staging、prodの設定を明確に区別する
- チームルールを定める: 命名規約、レビュープロセス、ロールバック戦略を合意する
- CI/CDに統合する: 手動実行はミスの始まり
DBスキーマ管理もソフトウェアエンジニアリングの一部です。 コードのようにバージョン管理し、テストし、自動化しましょう。
현재 단락 (1/519)
本番サーバーに直接接続してALTER TABLEを実行したことはありませんか?