1300万ユーザー規模の Web コンテンツ配信基盤リプレース ─ アジャイルアプローチと Go による実現
はじめに
はじめまして。@glassmonkey です。
現職の Ubie 株式会社に入社して約 1 年が経ちました。早いものです。
入社直後から関わることになった大規模なリプレースプロジェクトを完遂できたので、その知見を共有したいと存じます。
今回取り組んだのは、事業の屋台骨となる機能の一部を置き換える大規模なリプレースプロジェクトです。対象システムは 1300 万人のユーザーが日常的に利用する Web システムの基盤です。このような大規模リプレースは、技術的な課題だけでなく、多くのステークホルダーとの調整や事業継続性の確保など、複数の重要な要素を同時に満たす必要があります。
本記事では、約 1 年間かけて取り組んだシステム基盤のリプレースプロジェクトについて紹介します。特に、入念な設計フェーズとアジャイルな開発プロセスを組み合わせることで、どのように大規模リプレースを実現したのか、その実践について解説します。
プロジェクトでは、以下のような特徴的な取り組みを行いました。
- 設計フェーズでのステークホルダーとの密な合意形成
- MVP(Minimum Viable Product) を起点とした段階的な開発と展開
- Go による新規基盤の実装と学習サイクルの確立
- 継続的な品質検証の仕組み作り
この記事が、大規模システムのリプレースや新しい技術スタックへの移行を検討している方々の参考になれば嬉しいです。
プロジェクトの背景
医療とマルチプロダクト戦略における課題
Ubie はコンパウンドスタートアップとして複数のプロダクトを展開しています。弊社の VP of Technology である @yukukotani がUbie のプロダクト戦略を BtoC コンパウンドスタートアップとして捉えるという記事で解説しているように、私たちは「テクノロジーで人々を適切な医療に案内する」というミッションの実現に向けて、ペイシェントジャーニー全体の改善を目指しています。
Ubie株式会社_会社紹介資料より ペイシェントジャーニーの略図
「同時多拠点での突破」という理念のもと、複数のプロダクトを同時展開する私たちにとって、プロダクト間の連携とデータの統合は極めて重要です。
症状検索エンジン「ユビー」には「パーソナライズ」機能が実装されていましたが、プロダクト内部の 1 機能として閉じていたため、以下の制約がありました。
- 他プロダクトからの利用が困難
- プロダクト間での統合的なパーソナライズの実現が難しい
- 機能の拡張性や再利用性に制限
「とりあえずユビーを開けば、どうすればよいか分かる」という体験を実現するには、ユーザーの状態に応じて最適なソリューションを提案するパーソナライズ機能を進化させる必要がありました。
リプレースの必要性
マルチプロダクト戦略を推進する中で、既存パーソナライズ機能の制約が浮き彫りになり、技術面でも変革が求められる状況となっていました。
既存のパーソナライズ機能は Kotlin で実装され、これまで製薬事業向けの案件管理システムと連動して運用されていました。一方、社内全体で Go と Node への技術スタック統一が進んでいます。詳しくはUbie は Go と Node.js の会社になりますをご参照ください。
統一された技術スタックにより、以下のメリットが期待できます。
- エンジニア間での知識共有の促進
- コードレビューの効率化
- 開発環境やツールの標準化
- 保守性の向上
さらに、これまで運用していた製薬事業向け案件管理システムは、案件数の増加や業務プロセスの複雑化により運用の限界に達していました。加えて、パーソナライズ需要の高まりを受け、従来の案件管理システムをより汎用的な社内施策管理およびパーソナライズ管理基盤へ進化させる必要が生じました。
これらの背景を踏まえ、次の要件を満たす新基盤の構築が求められました。
- プロダクト間で再利用可能なパーソナライズ機能の提供
- 統一された技術スタック(Go)での実装
- 汎用的な施策管理およびパーソナライズ管理基盤の提供
- 将来的な拡張性を考慮した設計
その結果、パーソナライズ基盤という新たなハブシステムを構築し、既存システムの一部を置き換えるリプレースプロジェクトを開始するに至りました。これは単なる技術的リプレースではなく、Ubie のマルチプロダクト戦略を支える重要な基盤構築でもあります。
余談ですが、私は元々案件管理システムのエンジニアとして Ubie に参加し、テックリードを担ってきました。その流れで、今回のパーソナライズ基盤のテックリードにも任命され、プロジェクト全体の技術的リードをすることになりました。
アーキテクチャイメージ
過去に同僚の@Mayが同プロジェクトのQA戦略にフォーカスした記事を執筆したので、合わせてご覧ください。
【1200万MAU】基盤システム刷新プロジェクトのQA戦略と実践(前編)
【1200万MAU】基盤システム刷新プロジェクトのQA戦略と実践(後編)
設計フェーズでの取り組み
実際には各フェーズを行ったり来たりしながら進めましたが、大きく以下の 4 ステップで設計を進めていきました。
1. チーム内の認識合わせ
プロジェクト開始時、私を始めとして主に入社 1 年以内の新メンバーが中心だったため、まずはチーム内での共通認識を確立することが重要と考えました。
そこで、Design It を参考に施策を実施しました。
最初にチームが抱える課題の全体像を把握し、不安を共有するワークとしてリスクストーミングに取り組みました。
取り組んだリスクストーミングの詳細は同僚の@Mayによる【1300 万 MAU】基盤システム刷新プロジェクトの QA 戦略と実践(前編)をご覧ください。
これにより、チームとして取り組むべき課題の優先順位と重要度を定性的に設定できました。
2. 要件定義とステークホルダーとの合意形成
チーム内で基本認識が固まった後も、プロジェクトに必要なドメイン知識が十分ではありませんでした。
さらに、詳細な部分については品質特性として、以下のようなシナリオを作成しました(これも前述の Design It の手法を参考にしています)。
- パーソナライズ処理において、99%tile のユーザーに対して Xms 以内にレスポンスを返却する
- パーソナライズ処理において、Yrps の処理を応答劣化がほぼ無い状態で処理できる
- ログなどの要配慮な個人情報は、一部のユーザーのみ閲覧できるようになっている
これらのシナリオの妥当性のレビューをドメイン知識を十分に持つ他チームのメンバーから受けることで、チームを横断した合意形成および作るべきシステムの要件定義を進めていきました。
さらに詳細な業務ロジック部分はシーケンス図を中心に策定していきました。
3. アーキテクチャ設計と技術選定
社内では技術スタックを Go と Node.js に統一する方針がありましたが、API サーバーをどちらで実装するかは重要な検討事項でした。管理画面は Node.js で開発することが決まっていたため、API も同じ言語にすることで開発効率を高める選択肢もありました。
しかし、今回のパーソナライズ基盤は複数のプロダクトから利用されるハブシステムとなるため、以下の点を考慮し、API サーバーには Go を採用しました。
-
並行処理性能とリソース効率:
多くの外部 API 連携や高トラフィックが予想されたため、Go の軽量なゴルーチンによる効率的な並行処理能力は大きな魅力でした。また、コンパイル後のバイナリサイズが小さく、メモリフットプリントも比較的少ないため、コンテナ環境でのスケーリングや運用コストの面でも有利と判断しました。 -
シングルバイナリによるデプロイの容易さ:
Go はシングルバイナリとしてデプロイできるため、依存関係の管理がシンプルになり、デプロイプロセス全体の信頼性向上に繋がると考えました。 -
運用負荷の小ささ:
Go は標準ライブラリが充実しており、言語仕様や API の後方互換性が手厚く維持されているため、長期的な運用を見据えた場合にメリットが大きいと判断しました。具体的には、ライブラリや Go 自体のバージョンアップに伴う追従コストが低く抑えられ、安定した運用が期待できると考えました。
これらの理由から Go を採用し、結果として開発効率やコードレビューの容易さに加え、運用面でのメリットも享受できるシステム構築を目指しました。
4. フィーチャートグルによる早期リリースと事前データ同期
いわゆる「ビッグバンリリース」はコントロールが難しいため、避ける方針で考えました。
また、今回扱うシステムは既存事業の屋台骨でもあるため、チーム内外で品質をコントロールすることが重要でした。
特に弊社のサービスの特性として、利用者の皆様から得られたデータを元に外部のステークホルダーと連携している点もあるため、プロダクトの挙動の観点だけでなく、データとしてもデグレしないという点が重要でした。
今回、汎用化するにあたりデータ構造も大幅に変更することになったので、この互換性確認は重要な要素でした。
そこで、フィーチャートグルを使った社内限定リリースを実施し、本番環境での動作確認ができるようにしました。
さらに、既存のデータを定期的に新データ形式に変換する「データ移行バッチ」を用意し、新データ形式でも既存のデータと互換性が取れることを念入りにテストできる体制を整えました。
これにより、リリース前に本番環境で挙動だけでなくデータの確認も行えるようにしました。また、万が一の際にもロールバックを最小限の影響で済ませられるような設計にしました。
MVP の策定と実装
設計フェーズで完全な仕様を固めるのではなく、最小限の機能から実装を始めるアプローチを採用しました。特に今回のプロジェクトは既存システムの置き換えという性質上、リスクを最小化することが重要でした。
MVP の定義と範囲
パーソナライズ基盤の MVP として、まず「表示の一部を制御するだけ」という最小限の機能に絞り込みました。具体的には以下の機能を含めました。
- 基本的なパーソナライズルールエンジン:まずは外部 API 通信を伴わないシンプルな条件(例:ユーザーの年齢や性別といったデモグラフィック情報に基づくもの)から対応しました。
- 最低限の管理画面機能
- 既存システムとの接続ポイント
この範囲に絞ることで、以下のメリットを得られました。
- 早期にフィードバックループを回せる
- 技術的な課題や運用上の問題を早期に発見できる
- ステークホルダーに具体的な形で新システムをデモできる
MVP 実装の技術的ポイント
MVP 実装では、特に以下の点に注力しました。
-
フィーチャートグルの組み込みによるチーム間連携の促進:
MVP の段階から、機能の ON/OFF 制御、新旧システムの切り替え、さらには提供する体験バリエーションの出し分けを容易にするため、フィーチャートグルの仕組みを活用しました。 -
ユーザーセグメントマッチ機能の拡張性確保によるチーム内開発の効率化:
MVP では基本的なデモグラフィック情報に基づくシンプルなセグメントマッチングから開始しましたが、将来的には問診情報のような複雑な条件や多様なデータソースを活用した高度なセグメンテーションへの対応を見据えていました。そのため、MVP の段階からユーザーセグメントのマッチングロジック及びその周辺の処理フローは、新しい条件定義やデータ連携方式を容易に組み込めるよう拡張性を重視して設計しました。 -
基本的なオブザーバビリティの確保:
MVP を早期に本番環境へ投入しフィードバックを得るために、基本的なログ収集やエラー監視の仕組みを整備しました。これにより、問題発生時の迅速な検知と対応、そして本番環境での動作状況の把握を目指しました。
段階的な展開
MVP での基本機能の検証後、段階的に機能を拡充していきました。ここでは 2 つの観点で並行して開発を進めることで、効率的に機能を拡充していきました。
機能拡充の戦略
段階的な展開は主に以下の 2 つの軸で進めました。
-
パーソナライズ条件のバリエーション拡充
ユーザー属性やコンテキストに基づく表示制御の条件を拡充していきました。これには社内の他チームが持つ API との連携も含まれるため、バックエンドエンジニアリングが得意なメンバーを中心に担当しました。
具体的には以下の要素を順次追加していきました。
- 症状検索エンジン上の問診ロジックで得られる回答データ(どういう症状か?など)
- 他サービスとの連携データ
- などなど
-
体験バリエーションの拡充
表示内容や UI/UX 自体のバリエーションを拡充していきました。こちらはフロントエンド部分や管理画面機能の追加が中心だったため、フロントエンドエンジニアリングが得意なメンバーで担当しました。
主に以下の要素を段階的に追加しました。
- 様々な表示フォーマットのサポート
- インタラクションのバリエーション
- 管理画面での施策設定機能の充実
この 2 軸での並行開発により、全体の開発効率を高めながらも、各フェーズで確実に動作する機能をリリースできました。
- ** データ移行 **
機能実装と並行してデータ移行処理も作業を進めました。想定する移行パターンをintegration testレベルで定義することで、テストコードを通して互換性パターンのバージョン管理をすることができました。
これにより自分たちの仕様にどこで破壊的変更が発生するのかが非常にコントロールしやすく進められました。
特に工夫したポイント
ウォーターフォールとアジャイルの融合
本プロジェクトでは、純粋なウォーターフォールでもなく、純粋なアジャイルでもない、ハイブリッドなアプローチを採用しました。
既存システムを置き換えるという性質上、ある程度の事前設計は不可欠でした。しかし、すべてを事前に完璧に設計するのは現実的ではありません。特に、私を含めプロジェクトメンバーの大半が入社 1 年未満であり、リプレース対象のサービスは 3 年以上テストが不十分なまま改修を重ねられ、仕様が複雑化していました。加えて、私たちのシステムが生成するログデータが製薬事業向けのレポーティングを支える重要な柱であったため、このデータの互換性を絶対に損なうことは許されませんでした。
この不確実性の高い状況と厳しい制約の中で、私たちは「設計の安定性」と「実装の柔軟性」という相反しがちな要素を両立させるために、いくつかの工夫を凝らしました。
設計による「北極星」の設定
プロジェクトの初期設計フェーズは、全体で最も難航した部分です。この「北極星」を定めるために、私たちはシステムの根幹をなす設計に注力し、主に以下の 3 点に取り組みました。
- アーキテクチャの可視化: アーキテクチャ図を用いて、既存システムと新システム間でどのような通信が発生するのかを明確にしました。
- 処理フローの定義: シーケンス図を作成し、詳細な処理の流れを定義しました。
- データモデリング: 厳密なドメイン駆動設計(DDD)のドメインモデルとまではいかなくとも、「どのような役割のデータ(モデル)が登場するのか」という点について、ゼロベースで徹底的に議論を重ねました。
設計タスクの細分化と進捗管理
この複雑な設計プロセスをうまく進めるため、私たちは設計を具体的な成果物として強く意識し、タスクをスクラムのタイムボックスに収まるサイズまで細分化することを徹底しました。設計という抽象的な作業を、具体的なドキュメント作成や図の更新といった「完了」できるタスクに分解することで、チームは毎週着実に進捗を生み出すことができました。
論点分割による意思決定の仕組み化
論点を分割して意思決定のプロセスを仕組み化することも、複雑な設計を前に進める上で不可欠な取り組みでした。いきなり大きなテーマで議論を始めると空中戦に陥りがちになるため、私たちは いわゆるADR(Architecture Decision Record)に近い考え方を採用し、大きな設計課題を具体的な論点にまで分割していきました。
具体的な論点の例としては、以下のようなものがありました。
- 技術選定:「管理画面は Next.js で実装するか」「配信システムに Go を採用するか」など
- スコープ定義:「排他制御を今回のスコープに含めるか」など
そして、各論点に担当者を割り当てることで、議論が停滞することなく、一つひとつ確実に意思決定を進められるようにしました。これにより、議論が停滞することなく一つひとつ確実に意思決定を進められただけでなく、ステークホルダーに対して設計の意図を透明性高く共有し、納得感のある合意形成を行う上でも非常に有効でした。
イテレーティブな実装による柔軟性の確保
「北極星」となる設計方針が定まった後、私たちは詳細設計の全てを事前に固めるのではなく、イテレーティブな開発アプローチに移行しました。このアプローチでは、主に以下の 3 つのサイクルを回すことを意識しました。
-
実装を通じた学習とフィードバックのサイクル:
まず MVP(Minimum Viable Product)として最小限のスコープを定義し、実装と並行して詳細設計を進めました。これにより、経験の浅いチームでも実装を通じて知識を蓄積できました。また、定期的なデモでステークホルダーからのフィードバックを得ることで、認識の齟齬を早期に解消しました。 -
本番環境でのデータ検証サイクル:
前述の通り、本システムのログデータは事業の柱であり、その検証は最優先事項でした。イテレーティブな開発とフィーチャートグルを組み合わせることで、新システムを早期に本番投入し、限定的な範囲で動作させながらデータの検証サイクルを迅速に回すことが可能になりました。これは、ステージング環境では不可能な、本番環境特有のデータパターンでの検証を可能にする上で極めて重要でした。 -
技術的負債の意図的なコントロール:
実装を進める中で生じる技術的負債(妥協点)については、前述した「論点分割による意思決定の仕組み」を活用しました。なぜその妥協が必要なのか、将来的にどう解消するのかといった点を論点として記録・管理することで、負債を意図的にコントロールすることができました。
特に、仕様として複雑ながらも現行の仕様を残す必要性が生まれたときに、複雑な仕様をどうやったらやめられるのか?という点を踏まえて関係チームと議論するきっかけを作れた点は大変有意義でした。
技術スタックの選定と活用
本プロジェクトでは、Ubie が Go と Node.js の会社になるという全社的な方針に基づき、API サーバーには Go を採用しました。このため、他言語との厳密な比較検討を行ったわけではありませんが、結果として Go の採用はプロジェクトの生産性向上に大きく寄与しました。
特に、以下の 3 点が生産性向上に繋がったと感じています。
-
シンプルな文法と学習のしやすさ:
私を含め、開発チームのほとんどが Go の経験がほぼない状況でスタートでした。しかし、Go の文法はシンプルで書き手の癖が出にくいため、コードのスタイルにブレが生じにくく、結果としてチーム全体の生産性向上に繋がりました。公式チュートリアルである A Tour of Go や、ブラウザ上で手軽にコードを試せる Go Playground の存在も大きく、必要に応じてペアプログラミングやモブプログラミングを通じて、チーム内に効率的に知識を蓄積していくことができました。 -
テストの書きやすさ:
私たちが開発したシステムは外部システムへの依存箇所が多く、テストの複雑化が懸念でした。Go は標準パッケージの機能が充実しており、特にnet/http/httptest
パッケージを使えば、テストプロセスの中で外部システムの振る舞いを模したモックサーバーを立ち上げ、一貫したテストを実行することが容易でした。この点が、追加のライブラリやツールへ大きく依存せずに実現できたことは、生産性へ大きく寄与しました。 -
並行処理との親和性:
Go は並行処理が書きやすいと言われますが、並行処理そのものは依然として人間にとって難しいものです。そのため、私たちは並行処理化を最終手段と位置付けていました。しかし、今回のシステムはパフォーマンス要件が厳しい側面もあったため、必要に迫られた際に並行処理を容易に導入できる選択肢は非常に重宝しました。特に、複数の API リクエストを並行で処理するケースで、golang.org/x/sync/errgroup
パッケージのErrGroup
を活用しました。これにより、Go ルーチンを直接扱うと生じがちなバグを避けつつ、安全かつ容易に並行処理を実装できた点は大きなメリットでした。
学びと反省
今回のプロジェクトをテックリードという肩書でリードした中で、得られた学びと今後のための反省点を整理します。
本プロジェクトから得られた学び
-
ウォーターフォールとアジャイルのハイブリッドアプローチの有効性:
大規模で不確実性の高いリプレースプロジェクトにおいて、純粋なウォーターフォールやアジャイル開発手法に固執する必要はありませんでした。本プロジェクトでは、初期段階でシステムの「北極星」となるコアな設計を入念に行い、関係者間の認識を揃えるウォーターフォール的なアプローチを採用しました。その上で、実装フェーズでは MVP を設定し、イテレーティブな開発でフィードバックサイクルを高速に回すアジャイル的なアプローチへ移行しました。このハイブリッドな進め方が、不確実性に対応しながらも、プロジェクト全体の安定性を担保する上で極めて有効でした。 -
Go 言語の生産性と長期運用への貢献:
チームメンバーの多くが Go の経験が浅い状況でスタートしましたが、結果として Go の採用はプロジェクトの成功に大きく貢献しました。シンプルな言語仕様は学習コストを低く抑え、コードの属人性を排してチーム開発を円滑に進める上で効果的でした。また、httptest
に代表される標準ライブラリの充実度は、外部依存を減らし、テストの作成を容易にしました。長期的な視点では、手厚い後方互換性が運用負荷の軽減に繋がり、技術スタックの持続可能性を高めるという点でもメリットが大きいと実感しました。 -
フィーチャートグルによるリスク管理と継続的デリバリーの実現:
いわゆる「ビッグバンリリース」は、大規模プロジェクトにおいて最大のリスク要因の 1 つです。本プロジェクトでは、フィーチャートグルを全面的に活用することで、このリスクを効果的に管理しました。新機能を安全に本番環境へデプロイし、限定された範囲で動作確認をしたり、データ移行の互換性テストを継続的に行ったりする体制は、品質向上に直結しました。 -
汎用性の高い設計への先行投資:
本プロジェクトは、単なる機能の置き換えに留まらず、将来の多角的な事業展開を見据えた汎用性の高い設計を志向しました。1 年という開発期間は決して短くない投資でしたが、この時に厚めに設計した基盤は、現在まさに新しい事業の立ち上げに再利用され、その価値を開花させています。目先の開発効率だけでなく、長期的な視点に立った設計投資が、将来のビジネスの選択肢を広げるという貴重な成功体験を得られました。
今後に向けた反省点
テックリードとしてプロジェクトを推進する中で多くの学びがあった一方、いくつかの反省点も明らかになりました。今後のプロジェクトをより円滑に進めるため、具体的な改善策と共に整理します。
1. プロジェクトマネジメントの役割分担
課題:
リリースが近づき、外部チームとの連携が本格化する中で、工程管理の役割が不明瞭になる瞬間がありました。特に、実装と並行してプロジェクトマネジメントの役割を担うエンジニアの関心が分散し、負担が大きくなるという問題がありました。タスク管理についても、JIRA での PBI(Product Backlog Item)管理から一歩進んだ、日付単位での詳細なガントチャートの導入が遅れ、連携の初期段階で手探りの状態を生んでしまいました。
改善策:
- プロジェクト進行に応じた役割の明確化: プロジェクトのフェーズに応じて、進行管理の責任者を明確に移行させる仕組みを導入します。例えば、初期の要件定義は PO、設計・実装期はテックリード、そしてテスト工程が中心となるリリース直前期は QA エンジニアが責務を担うといった形です。ただし、リリース時期やリソース管理といった全体責任は、プロジェクトを通して Lead/PO が一貫して持ち続けます。
- マイルストーンの厳格な運用: より短い単位でマイルストーンを設定し、達成状況を定期的に確認します。もし未達成の場合は、感情的にならずに一度プロセスを止め、計画を再評価・再設計する規律を徹底することが重要です。
- 連携開始時点での詳細な計画: 他チームとの連携が始まる段階で、粒度の粗いバックログ管理から脱却し、日付単位での詳細なタスクと依存関係を示したガントチャートを作成して、関係者間の共通認識を初期段階で確立します。
2. 関係チームとの連携強化
課題:
プロジェクト初期に行ったリスクストーミングを開発チーム内に閉じてしまったため、運用などに関わる他チームの視点が不足し、後工程で考慮漏れが発覚する一因となりました。特に、構築したシステムはチーム外のメンバーに使ってもらうものであったにも関わらず、「実運用にギリギリ耐える品質」という当初の見立てが楽観的であり、実際のユーザーによる受け入れテストやフィードバックの機会を十分に確保できていませんでした。
改善策:
- 初期段階からのステークホルダーの巻き込み: リスクストーミングのような重要なワークショップには、開発チームだけでなく、運用、QA、ビジネスサイドといった関連ステークホルダーにも初期段階から参加を依頼し、多角的な視点を取り入れます。
- 受け入れ基準の事前合意: 複雑なシナリオテストをどこまで実施するかといった品質に関する受け入れ基準をプロジェクトの初期段階で関係者とすり合わせ、明確に合意・ドキュメント化します。これにより、リリース直前での「期待値のズレ」を防ぐことができます。
3. チームのスキルセットと開発ボトルネック
課題:
プロジェクトを通して、常にバックエンドエンジニアの開発がボトルネックとなる状況でした。これは、特定のスキルセットを持つメンバーにタスクが集中しやすかったことが原因です。
改善策:
- スキルの標準化と多能工化: 今後、AI による開発支援を積極的に活用して個人の能力を補うことはもちろんですが、それと同時にチームとして特定の技術領域への投資を継続します。チームメンバーが一定水準のタスクであれば、自身の専門領域を越えて相互にサポートし合える「多能工」な状態を目指すことが、チーム全体の生産性を高める上で重要だと考えています。
終わりに
本記事では、1300 万ユーザー規模の Web システム基盤リプレースプロジェクトについて、特に設計とアジャイル開発の融合という観点から、その実践的な取り組みを紹介しました。
このプロジェクトは、私にとって技術的にも組織的にも非常にチャレンジングな経験でしたが、チーム一丸となって乗り越えられたことを誇りに思っています。この記事が、同様の課題に直面している開発者やプロジェクトマネージャーの方々の参考となれば幸いです。
弊社ではエンジニアの採用を絶賛実施中です!
少数精鋭でやりがいのある環境です。ぜひカジュアル面談含めてお待ちしています!
Discussion