Top View


今日は、「キャリアリセット:Fusic Tech Blog CMSのAWS移行を通じた学びの旅」について書きます。

この記事を読んでくださっている皆さん、こんにちは。 Fusic でエンジニアをしているトーマです。

この記事は Fusic Advent Calendar 2025 16日目の記事です。

昨日は @suamoto-mikiya さんの「Mountpoint for Amazon S3のファイル操作で躓いた話 - 制約を理解して正しく使おう」 でした。

はじめに

30代でキャリアをやり直したことはありますか?2022年にそれを経験しました。フランス人エンジニアで、日本語学の修士号を取得した後、2017年に日本に移住しました。フランスを離れてからテクノロジーに戻るまでの間、ホテルで働いたり、居酒屋でビールを注いだり、宮大工(日本の伝統的な寺院大工)として働いたりしました。

しかし2022年、何かが変わりました。テクノロジーに戻ることを決意しました。2007年にフランスで元々学んだ分野です。2022年のWeb開発は、10年以上前に学んだホームオートメーションシステムとは全く異なるものでした。

Fusicがアマゾン ウェブ サービス(AWS)パートナーであることを知り、入社前の1ヶ月間はAWSの基礎を学びました。最初の週はReactとAmplifyの集中講座でした。そして、課題が与えられました。会社のテックブログCMSをAmplify Gen1からGen2に移行することです。これはチュートリアルプロジェクトではありませんでした。全員が記事を公開するために使用している本番システムでした。

これは、Fusic Tech Blog CMSへの10ヶ月間の旅の物語です。

パート1:課題の理解

実際に何を移行していたのか?

Fusic Tech Blogには2つの側面があります:今読んでいる公開側(Fusic Tech Blog)と、社員が記事を書いて管理する管理側(CMS)です。

Tech blog CMS and Public

Gen1 vs Gen2:何が違うのか?

Amplify Gen1は自動モードのようなものです。AWSが裏側でリソースを作成します。セットアップは速いですが、裏で何が起こっているかをあまりコントロールできません。

Gen2はAWS CDKを使用したInfrastructure as Code(IaC)です。Gen1もIaCを使用していましたが、多くのリソースがAmplifyによって自動生成され、裏側の構成が見えにくい状態でした。Gen2では、AWS CDKを使ってアーキテクチャのすべての部分を明示的にコードで定義します。より多くの透明性、より多くのコントロール、より多くの柔軟性がありますが、その分複雑さも増します。問題は?自動移行ツールがないことです。すべてを手作業で再構築する必要がありました。

なぜこれが重要だったのか

これは単なる学習演習ではありませんでした。CMSは会社全体で積極的に使用されていました。Fusicのブログで公開されるほとんどの技術記事は、このシステムを通過します。壊すということは、全員のワークフローを壊すことを意味していました。

そして、新しい仕事を始めて1週間の私が、それを完全に再構築しようとしていました。

スタート地点

Gen1リポジトリをクローンして、ただ…じっと見つめました。これは他人の本番コードベースで作業するのが初めてでした。何かをする前に、理解する必要がありました:

  • 認証の仕組み(Slack OAuth + メール/パスワード)
  • 投稿の保存と管理方法(DynamoDB)
  • 画像のアップロード方法(S3 + CloudFront)
  • CMSが公開ブログとどのように通信するか
  • Lambda関数が裏で何をしているか

スコープは膨大でした:認証、ユーザー管理、投稿のCRUD操作、メディア管理、アナリティクス、そして最終的にすべてを本番ブログに接続すること。

深呼吸をして、最も難しい部分から始めました:認証です。

パート2:マイグレーションの旅

フェーズ1:認証地獄(2月〜3月)

最初の本当の課題は、ユーザーにログインしてもらうことでした。システムは2つの方法をサポートする必要がありました Slack OAuth(Fusic社員用)と従来のメール/パスワードです。両方ともAWS Cognitoを経由しますが、これまで使ったことがありませんでした。

何週間も学習しました:

  • ユーザープール - ユーザーアカウントが存在する場所
  • IDプロバイダー - Slack OAuthを接続する方法
  • Lambdaトリガー - ログイン中に自動的に実行される関数

ブレークスルーの瞬間は、post-confirmationトリガーを動作させたときでした。初めてログインするとき、今は以下のことが起こります:

  1. ユーザーがSlackまたはメールで認証
  2. Lambda関数が自動的に起動
  3. DynamoDBで既存のプロフィールをチェック(メールで照合)
  4. 見つかった場合、新しいログインを既存のプロフィールにリンク
  5. 見つからなかった場合、新しいプロフィールを作成

シンプルに聞こえますか?CloudWatchログをデバッグし、動作しないLambda関数に悪態をつく無数の時間がかかりました。

フェーズ2:コアの構築(3月)

認証が機能したので、CMSの中核に移りました:ユーザー、投稿、画像の作成、読み取り、更新、削除(CRUD操作)です。

Gen1とGen2は完全に異なる構造を持っているため、これは単なる移行ではありませんでした。再設計でした。物事がどのように機能すべきかを再考し、ゼロから実装することができました。

学んだ技術スタック:

  • DynamoDB - すべてを保存するNoSQLデータベース
  • S3 + CloudFront - 画像の保存と配信
  • API Gateway - フロントエンド用のRESTエンドポイント
  • Lambda - サーバーレスで実行されるビジネスロジック
  • EventBridge - スケジュールされたジョブ(アナリティクスなど)

Lambdaは特に苦痛でした。フロントエンド開発は楽です。コードを変更し、ブラウザを更新し、結果を見る。Lambda?コードを変更し、サンドボックスにデプロイし、5分待ち、CloudWatchログをチェックし、タイプミスに気づき、繰り返す。

しかし、徐々にまとまってきました。投稿を作成、編集、削除できるようになりました。画像が適切にアップロードされました。ユーザーはプロフィールを管理できました。

フェーズ3:スマート機能の適応(3月)

基本機能が動作したら、CMSを特別なものにしていた既存のスマート機能を移行して改善する必要がありました。

OpenAI チャットボット統合: Gen1システムにはすでにライター向けのサイドバーアシスタントがありました。それをGen2に移行し、OpenAI API統合を新しいアーキテクチャで動作するように適応させました。ライターは下書き中に相談でき—構造についてヘルプを得たり、導入部分の行き詰まりを解消したり—できますが、新しいシステムでよりスムーズに実行されるようになりました。

日本語用Textlint: 既存のTextlint統合(Web Workerで実行)をGen2用に適応させる必要がありました。日本語のテキスト品質を自動的にチェックし、一般的な間違いをキャッチし、すべての記事で一貫した執筆基準を維持し続けることを確保しました。

これらの機能はすでに存在していましたが、適切に移行し、その過程で改善を加えることは、CMSを以前と同じくらい良く—またはそれ以上に—保つために重要でした。

フェーズ4:最も怖い部分—データ移行(4月〜5月)

マイグレーションについて誰も教えてくれないこと:コードは簡単な部分です。何も失わずに何年もの本番データを移動すること?それは恐ろしいです。

ddb-to-ddb

Amplify Gen1とGen2は完全に異なるデータベース構造を使用しています。データをコピー&ペーストするだけではできませんでした。2つの移行アプローチを構築しました:

方法1:CMS経由のCSVアップロード

  • Gen1データをCSVファイルとしてエクスポート
  • CMSで作成したフォームからアップロード
  • クライアント側での検証とGen2 DynamoDBへのインポート
  • 管理者が特定のデータを手動で検証して移行するのに適しています

方法2:サーバーサイドスクリプト

  • AWS SDKを使用した直接DynamoDB間転送
  • コマンドラインで実行:npx vite-node migration-script.ts
  • 異なる環境のサポート(main、staging、production、sandbox)
  • バルク操作により信頼性が高い

本当の魔法は、先ほど述べたpost-confirmation Lambdaトリガーで起こりました。既存のユーザーは何も手動で移行する必要がありませんでした。新しいCMSにログインするだけで、システムが自動的に:

  • 古いプロフィールを見つける
  • 新しいCognito IDにリンクする
  • すべての投稿を新しいユーザーIDで更新する
  • 全履歴を保持する

これが初めて機能するのを見たとき、魔法のように感じました。

export users flow

フェーズ5:本番稼働(6月)

CMSは実際のブログに接続しなければ役に立ちませんでした。DynamoDB Streamsを設定して、投稿テーブルの変更を監視しました。誰かが記事を公開すると:

  1. DynamoDB Streamが変更を検出
  2. Lambda関数が自動的にトリガー
  3. ブログのビルドサーバーでwebhookを呼び出す
  4. ブログが新しいコンテンツで再ビルド
  5. 記事がtech.fusic.co.jpに表示される
trigger architecture new post

パイプライン全体が自動化されました。書いて、公開して、完了。

フェーズ6:マイグレーション以降(7月〜12月)

マイグレーションは6月に「完了」しました。しかし、その後6ヶ月間の改善が続きました:

7月:

  • Qiitaアナリティクスの追加(Qiitaにクロスポストされた記事の追跡)
  • Lambda経由のGoogle Analyticsレポート統合

8月〜10月:

  • generoutedからTanStack Routerへの移行(型安全性の向上)
  • Tailwind CSS v4へのアップグレード
  • すべてのAWS Amplify UIコンポーネントをshadcn/uiに置き換え
  • Fusicのブランドカラーを全体に実装

10月〜11月:

  • ダークモードサポートの追加
  • npmからpnpmへの切り替え(高速、より効率的)
  • すべてのUIテキストを日本語に翻訳
  • トースト通知の追加(sonnerを使用)でUX向上

11月〜12月:

  • PDFとPowerPointファイルのアップロードサポートの追加
  • プレゼンテーション埋め込みの統合(Speaker Deck、SlideShare、Google Slides、Figma Slides)

注:このプレゼンテーションはアップロード機能のデモ用にCanva AIで作成したサンプルです。内容は検証されていません。

  • Slack OAuth エラーハンドリングの修正
  • 文字数制限とバリデーション改善の追加

本当に「完了」するまでに、10ヶ月間で239コミットを行い、70以上のプルリクエストをマージしました。

パート3:学んだこと

技術スキル

基本的なReactを知っている状態から、以下を理解するようになりました:

  • サーバーレスアーキテクチャパターン
  • 認証フロー(OAuth、OIDC、Cognito)
  • データベース設計とマイグレーション
  • AWS CDKを使用したInfrastructure as Code
  • CI/CDパイプラインとブランチベースのデプロイメント
  • LambdaデバッグとCloudWatchログ分析
aws icons of skilled learnt

Lambdaの教訓

Lambda関数は忍耐を教えてくれました。コードがクラウドで実行されるとき、単にconsole.log()してチェックすることはできません。デプロイし、待ち、CloudWatchをチェックし、さらにログを追加し、再度デプロイし、さらに待つ。コードを書く前に問題を体系的に考えることを強制されました。

レガシーコードを読む

他人のアーキテクチャ上の決定を理解することは、それ自体がスキルです。なぜこのように構造化したのか?どんな問題を解決しようとしていたのか?時々答えは「Gen1がそれを強制したから」ですが、しばしば一見奇妙な選択の中に知恵が隠れています。

段階的な進歩の力

ある日は、1つのバグしか修正しませんでした。ある週は、同じLambda関数で行き詰まっていました。それで大丈夫でした。前進は前進です、たとえ遅く感じても。

日本語での作業

私の日本語は劇的に向上しました。技術ドキュメントを読み、コミットメッセージを書き、コードレビューでコミュニケーションをとる—すべて日本語で。最初は怖く見えた言語の壁は、ナビゲートすることを学んだもう一つのことになりました。また、本当に助けが必要な場合は、今はAIがあります。

パート4:結果

今日、Fusic Tech Blog CMSは完全にAmplify Gen2上で実行されています。より速く、より保守しやすく、古いシステムが夢にも思わなかった機能を持っています。さらに重要なことは、同じ技術スタックを使用する本番アプリケーションであるユニオンシステムプロジェクトにすぐに参加できるほど十分に学んだことです。

私のチューターのコメントが要約してくれました:「すごい。定期ジョブ、API、Cognitoトリガー、OAuth、OpenAI、Textlint(Web Worker)などなど、モリモリな実装全部こなしたので、これでAmplifyマスターだ。」

ほんの数年前に木を切っていた人間にしては悪くありません。

パート5:次は何か

マイグレーションは、複雑なシステムを学び、本番の問題をデバッグし、実際の機能を出荷できることを教えてくれました。今は実際のプロジェクトで働いており、学んだすべてを適用し、新しい課題に取り組んでいます:Prisma ORM、高度なデータベースパターン、そして移行するのではなくゼロから機能を構築すること。

今後の目標:

  • タスク管理をもっと上手になる - 大きな機能を分解し、時間を見積もる
  • もっと書く - この記事が最初です
  • AWSの知識を深める - いつか認定試験に挑戦したい
  • 学び続ける - 現在、生成AIと宇宙技術を探求中(はい、宇宙です!)

最後の考え

キャリアチェンジを考えている、人生の後半でコーディングを学んでいる、または本当にやり直せるかどうか疑問に思っているなら—できます。簡単ではありません。後で見れば些細に見える問題で何日も立ち往生することがあります。自分がこれに向いているかどうか疑問に思うでしょう。

しかし、学んだことは:進歩は直線的ではなく、それで問題ないということです。ある週は素晴らしい機能を出荷します。ある週は何かが壊れた理由を理解するだけです。両方が重要です。

日本文学を学び、大工として働き、33歳でコーディングを始めることを決めたフランス人。そんな私が本番CMS移行を出荷し、最初の技術記事を書きました。

私にできるなら、誰でもできます。

謝辞

この旅を通じて、忍耐強く、指導し、サポートしてくれたチューターの夛田さん、リーダーの礒谷さん、先輩の浦田さん、そしてFusicのFlairチーム全員に心から感謝します。私のコードをレビューし、何を構築するかだけでなく、どのようにうまく構築するかを理解するのを助けてくれたすべての人に特別な感謝を。

Guiart Thomas

Guiart Thomas

フランス出身のエンジニア。日本語学修士号取得後、2017年来日。 宮大工見習いを経て、2022年に独学でフロントエンド開発を再開。 2025年Fusic入社、AWS Amplify/React/NextJS/TypeScriptで開発中。