4Hえんぴつ|React+TypeScriptでWeb RPGを作る
第8章「Web公開・互換性・永続化」
第46回:正式公開までに作った品質基盤を振り返る
1. ゲームが動いたあとにも、作るものがあった
この連載では、React+TypeScriptでWeb RPG『冒険に行こう!』を作ってきました。
町で依頼を受ける。
街道へ出る。
イベントが発生する。
戦闘・交渉・調査から解決方法を選ぶ。
結果によってHPやゴールド、評判、依頼進捗が変わる。
そして、セーブして続きを遊ぶ。
ゲームとして必要な流れは、少しずつ形になっていきました。
しかし、実際に正式公開まで進めてみると、
ゲームが動くことと、安心して公開できることは別だった
と感じます。
今回は、第30回以降で整えてきた品質基盤を振り返ります。
2. 最初の課題は「セーブできる」ではなくなった
最初は、
GameState
↓
保存
↓
読み込み
ができれば、セーブ機能として成立するように見えました。
しかし、公開を考えると、それだけでは足りません。
例えば、
古いセーブを読めるか
壊れたセーブをどうするか
将来形式を誤って上書きしないか
保存先を変更しても既存データを残せるか
まで考える必要がありました。
そこで、
appVersion
と、
saveSchemaVersion
を分けました。
アプリの版と、セーブ形式の版は、同じものではないからです。
3. Migrationは「古いデータを捨てない」ために作った
セーブ形式を変更すると、
以前の形式で保存したプレイヤーが存在します。
そこで、
旧SaveData
↓
形式を判定
↓
Migration
↓
現行SaveData
という流れを作りました。
また、Migration前にはbackupを残します。
変換に失敗した場合でも、
元のデータまで失わないためです。
ここで意識したのは、
新しい形式へ進めることより、以前の旅を失わないこと
でした。
4. future schemaは無理に読まない
反対方向の互換性もあります。
例えば、現在のアプリがsaveSchemaVersion: 3までしか知らないのに、
saveSchemaVersion: 99
というデータを受け取った場合です。
これを、
たぶん読めるだろう
として処理すると、データを壊す可能性があります。
そこで、
future schema
↓
unsupported
↓
ロードしない
↓
通常上書きもしない
というfail-closedにしました。
知らない形式を推測して扱わない設計です。
5. 壊れたセーブを「消して終わり」にしなかった
次に扱ったのが、破損セーブです。
現在の保存基盤では、
current
backup
quarantine
を使い分けています。
例えば、
current
↓
破損
backup
↓
正常
なら、
正常なbackupから復旧できます。
一方、壊れたcurrentは即座に捨てるのではなく、quarantineへ隔離します。
つまり、
壊れている
=
すぐ削除
ではありません。
復旧できるものは復旧し、問題のあるデータは安全に分離する設計へ変わりました。
6. localStorageからIndexedDBへ移した
保存するデータや管理方法が増えるにつれて、
保存基盤も見直しました。
最終的には、
localStorage
↓
IndexedDB
↓
Dexie.js
へ移行しています。
ただし、新しい保存先へ移しただけではありません。
既存ユーザーのlocalStorageデータを、
一度だけIndexedDBへ移行できるようにしました。
そのために、
migration marker
も持たせています。
これによって、起動するたびに古いデータを再移行して巻き戻してしまうことを防ぎます。
7. 保存技術をゲームから切り離した
第41回では、さらに、
React / Game Logic
↓
Save Service
↓
Save Repository
↓
Dexie.js
↓
IndexedDB
という境界を整理しました。
ゲーム側が知りたいのは、
このスロットへ保存する
このスロットを読み込む
ということです。
Dexie.jsやIndexedDBの細かな扱いまで、ゲームロジックへ広げる必要はありません。
Repositoryを挟むことで、
ゲームのルールと保存技術を分離する
ようにしました。
8. 手動確認を自動テストへ移した
機能が増えると、
すべてを毎回手作業で確認するのは難しくなります。
そこで、テストも層に分けました。
Vitest
↓
小さい単位のロジック
Playwright
↓
実際のユーザー操作
実機・Emulator
↓
端末条件
production
↓
実際の公開環境
正式公開候補では、Vitestは、
7 / 7 files
68 / 68 tests
まで増えました。
ただ、数そのものが目的ではありません。
変更するたびに、
以前動いていたところが壊れていないかを繰り返し確認できること
が重要でした。
9. E2Eでは「最後まで進めた」だけを見なかった
Playwrightでは、
タイトルから始めて、
キャラクターを作り、
町へ進み、
セーブし、
ロードする、
といった操作を自動化しました。
さらに、
破損セーブからのRecoveryやfuture schemaも確認対象にしています。
そして、第39回では、
シナリオ成功
+
console warning/error 0
+
pageerror 0
までを条件にしました。
画面上で最後まで遊べたとしても、
ブラウザ内部で例外が発生していたら、
正常とは考えないためです。
10. Chromiumだけでは終わらせなかった
Webアプリは、一つのブラウザだけで使われるわけではありません。
そこで、
Chromium
15 / 15 PASS
WebKit + iPhone emulation
7 / 7 PASS
まで確認しました。
さらに、
物理iPhone Safariと、
Android Chrome Emulatorでも受入を行っています。
レスポンシブについても、
スマートフォン縦持ちを主対象として、
320~430px級の画面幅、
横overflow、
タッチ領域、
focus、
keyboard navigation、
aria、
contrast
などを確認しました。
11. localhostで動くことと、公開先で動くことも分けた
React Routerを使ったSPAは、
アプリ内遷移なら正常でも、
/character
/saves?mode=manage
/town
を直接開いたときや、再読み込みしたときに問題が出ることがあります。
そこでNetlify側にSPA fallbackを用意し、
React RouterへURLを戻すようにしました。
そのうえで、
local production-hosting
7 / 7 PASS
production hosting
7 / 7 PASS
まで確認しています。
開発環境だけでなく、
実際にユーザーがアクセスするURLでも同じ確認を通す
ようになりました。
12. 公開できるだけでなく、戻れることも確認した
正式公開前には、もう一つ問題がありました。
今回が初回リリースだったため、
戻るべき過去の正常production deployが存在しませんでした。
そこで、
Exact Frozen Candidate
↓
INITIAL_RELEASE_ANCHOR
を作りました。
そして、
Anchor確認
↓
一時Publish
↓
fresh session確認
↓
元のproduction deployへ復元
↓
復元後もhosting 7 / 7 PASS
まで実行しています。
ここで確認したかったのは、
公開できることだけでなく、公開状態を安全に切り替えて戻せること
でした。
13. 最後にRelease Gateへまとめた
一つずつの確認ができても、
正式公開時に抜けがあれば意味がありません。
そこで第45回では、
それまでの確認をRelease Gateとしてまとめました。
最終的には、
PACK_MANIFEST
clean npm ci
Vitest
typecheck
lint
production build
artifact verification
npm audit
Chromium
WebKit
local hosting
production hosting
console / pageerror
rollback
Blocker / High
などを確認しています。
つまり、
何となく大丈夫そう
ではなく、
公開条件
↓
すべて確認
↓
正式公開判定
へ変わりました。
14. 既知課題が0になるまで待ったわけではない
正式公開時にも、既知課題は残っています。
スマートフォンでNetlifyのバッジが、画面下部の固定操作領域右側へ重なる問題です。
ただし、
操作可能領域は残る
ゲーム進行に影響しない
保存・ロードに影響しない
console / pageerrorに影響しない
ことを確認しました。
そのため、Mediumの非Blockerとして継続管理しています。
重要なのは、
課題が一つもないことではなく、その課題が公開を止めるものかを判断できること
でした。
15. 「公開品質」は一つの技術では作れなかった
ここまでを振り返ると、
公開品質を作った技術が一つあるわけではありません。
IndexedDBだけでもありません。
Playwrightだけでもありません。
Netlifyだけでもありません。
互換性
+
Recovery
+
永続化
+
Unit Test
+
E2E
+
Browser
+
Device
+
A11y
+
Hosting
+
Rollback
+
Release Gate
という複数の層が重なっています。
一つの強力な仕組みを作るというより、
問題が起こる場所ごとに、小さな安全策を重ねていった
という方が近いと思います。
16. Web MVPは「ゲームが完成した」ではなく「基準版ができた」
現在のWeb MVPには、
町 3
街道 5
依頼 5
イベント 10
があります。
戦闘・交渉・調査の共通解決モデルがあり、
3つのセーブスロットもあります。
しかし、
これですべてのゲーム内容が完成した、
という意味ではありません。
今回到達したのは、
これから内容を増やしたり改善したりするときの、正式な基準版ができた
という状態です。
17. 正式公開後の変更も、基準版から考えられる
正式公開版があることで、
今後何かを変更するときも、
現在の正式版
↓
変更
↓
テスト
↓
新しい候補
という形で比較できます。
セーブ互換性が壊れていないか。
新しい町を増やして既存依頼が壊れていないか。
UI変更でスマートフォン表示が崩れていないか。
公開後の開発でも、
今回作った品質基盤を再利用できます。
18. この第8章で扱ったこと
第8章では、次の内容を扱いました。
第41回
IndexedDB / Dexie.js / Repository
第42回
React Router / Netlify / SPA fallback
第43回
production URLでのE2E
第44回
rollback anchor
第45回
Release Gate
第46回
品質基盤の振り返り
保存から始まり、
最後は正式公開判断まで進みました。
この章で扱ってきたのは、
Webアプリを作る技術というより、作ったWebアプリを安全に外へ出すための技術
だったと言えます。
19. Web MVP公開品質編をここで一区切りにする
React+TypeScriptでWeb RPGを作り始めてから、
データ設計、
状態管理、
セーブ、
互換性、
テスト、
公開環境
まで進んできました。
そして現在、
Web MVP『冒険に行こう!』は、
Web MVP Phase G v1.6.0
として正式公開できる品質基盤まで到達しています。
この連載自体をここで終えるわけではありません。
ただ、
「小さなWeb RPGを作り、正式公開できる品質まで持っていく」
という現在の技術的な区切りとしては、ここで一度まとまったと考えています。
次の工程については、内容や完了条件を正式に決めてから進めます。
20. 今回のポイント
今回振り返って感じたことは、3つです。
- 公開品質は、セーブ・テスト・ブラウザ・端末・hosting・rollbackなど複数の層で作る
- 「動く」だけでなく、「壊れたときに守れる」「変更しても確認できる」「問題があれば戻れる」ことが重要
- Release Gateによって、公開可否を感覚ではなく確認結果から判断できるようになった
最終的には、
動くWeb RPG
↓
壊れにくくする
↓
繰り返し確認できるようにする
↓
公開環境で確認する
↓
戻れるようにする
↓
公開条件を満たす
↓
正式公開できるWeb MVP
という流れになりました。
ゲームを作ることと、ゲームを安心して公開すること。その間には、想像していたより多くの設計がありました。
今回作った品質基盤は、これからWeb版を育てるときにも、次の制作段階へ進むときにも、基準として使っていきます。
この記事を最後まで読んでいただき、ありがとうございます。
「いいね」を押していただけるとうれしいです。これからの記事づくりの励みになります。
もし気に入っていただけましたら、フォローもよろしくお願いします。
今後について
第46回で、現在のWeb MVP公開品質編は一度一区切りとします。
今後の機能拡張や次の開発工程については、内容・範囲・完了条件を正式に整理したうえで、この連載でも続きを扱う予定です。
前の記事
第45回 「公開できる」を自動判定する――Release Gateを組み立てる
連載トップ
第0回 React+TypeScriptで異世界RPGを作る――連載の目的と開発ロードマップ
note関連記事
正式公開の前に、どこまで確認したか #26
noteでは、正式公開の直前に、セーブ互換性、破損復旧、自動テスト、スマートフォン、本番環境、rollbackまでをどのように確認したのかを、制作側の視点から振り返っています。
note「正式公開の前に、どこまで確認したか #26」を読む
Web版『冒険に行こう!』
ここまでの記事で扱ってきた設計・実装・テスト・公開基盤を使って作った
Web版MVP『冒険に行こう!』は、現在ブラウザから遊べます。
正式公開URL
