TECH PLAY

コードリーディング

イベント

該当するコンテンツが見つかりませんでした

マガジン

該当するコンテンツが見つかりませんでした

技術ブログ

はじめに 皆さんこんにちは!九州大学修士1年の羽田野武蔵です! 2025年12月に、マッチングアプリ ...
はじめに こんにちは、2025年にiOSエンジニアとして新卒入社したZOZOTOWN開発1部iOSブロックの だーはま です。普段はZOZOTOWNのiOSアプリを開発しています。本記事では、新卒1年目の私がZOZOTOWNの画面へMVVM+UseCaseアーキテクチャを導入した過程と、工夫を紹介していきます。 目次 はじめに 目次 背景と目的 チーム配属から1か月でMVVM+UseCaseアーキテクチャ導入を担当した経緯 デバッグ画面について 課題 コードやドメインに対する知識不足 700行以上に及ぶFatViewController 課題を解決するための取り組み Claude CodeのSubagentsを使いキャッチアップを高速化 MVVM+UseCaseアーキテクチャ導入 結果 デバッグ画面のテストカバレッジ0→93.5% UI実装をStoryboardからSwiftUIへ完全移行 開発工数40%減少 MVVM+UseCaseアーキテクチャ導入を振り返って 最後に 背景と目的 チーム配属から1か月でMVVM+UseCaseアーキテクチャ導入を担当した経緯 ZOZOTOWN iOSでは、MVVMとUseCaseで構成されるチーム標準のアーキテクチャがあります(以後アーキテクチャという表記はチーム標準のアーキテクチャを指します)。現在、アプリ全体の保守性と開発効率を向上させるため、 チーム全体でこのアーキテクチャへの統一を進めるプロジェクトが進行しています 。 私自身、学生時代から設計への関心は高く、個人開発で同様の構成を取り入れた経験はありました。しかし、ZOZOTOWNのような歴史ある大規模なアプリの画面で、UIからビジネスロジックまでを含めて構成を見直した経験はありません。また、当時は入社して間もなく、経験したことのあるタスクはUIの修正やログ送信といった特定箇所の改修のみで、 ZOZOTOWNの大規模なコードの全体像も把握できていない状態 でした。 このような状況ではありましたが、iOSチームには以下のように アーキテクチャ導入を支える手厚いフォロー体制 1 が整っていました。 アーキテクチャの指針書が整備されている 各レイヤーの責務が言語化されている レイヤーごとにコードを使った実装例がまとめられている 設計段階でのレビューが義務化されており、実装後の手戻りを抑えられる これらの環境に背中を押され、アーキテクチャ導入がチームへの貢献と自己成長へ繋がると考えました。そのため、チームへ配属されて1か月目というタイミングではありましたが、 自ら手を挙げタスクオーナーとしてアーキテクチャ導入を進める ことになりました。 取り組む目的は以下の通りです。 技術的負債を抱えた画面のメンテナンスコストを下げること 疎結合でテスタブルなコードにする ZOZOTOWN iOSが採用しているアーキテクチャに慣れること ZOZOTOWNのiOSチームが採用しているアーキテクチャへの理解を深め、他画面のコードリーディングを高速化する テストコードの実装にも慣れるため、可能な限りテストを実装する 大規模なコードを高速にキャッチアップするための練習をする AIと協働して大規模なコードを高速にキャッチアップする方法を模索する 上記の目的を満たす画面をチーム内で議論し、社内向けでありユーザー影響のないデバッグ画面を選びました。 デバッグ画面について デバッグ画面は、ZOZOTOWNのデバッグ用にデータを編集する画面です。開発している案件に応じてサーバーの向き先を切り替えるなど、開発する上で便利な機能が複数用意されています。デバッグ画面を使うことでQAや案件ごとに実施される動作確認をスムーズに行えます。このデバッグ画面はiOSエンジニアだけでなく、QAチームやバックエンドを始めとした社内の様々なチームおよびメンバーが触れるため、 開発する上でとても重要な画面 です。 しかし、新規案件の実装を優先しておこなっていく中で、社内向けでありプロダクトコードではないことから保守に手をつけることができずにいました。その結果、UIからビジネスロジックまでの実装が1つのViewControllerに記述され、機能を追加するたびにメンテナンスコストが増大していました。 課題 アーキテクチャ導入にあたり解決すべき課題は以下の2つです。 コードやドメインに対する知識不足 700行以上に及ぶFatViewController コードやドメインに対する知識不足 先述したようにデバッグ画面は依存関係が複雑です。 また、導入にあたり以下のような課題がありました。どの課題にも共通して「キャッチアップするべき項目が多く、十分にできていなかった(本人は十分にできているつもりだった)」ということが挙げられます。 触れたことのないアーキテクチャの理解 学生時代に個人開発で触れていたアーキテクチャ(MVVM,TCAなど)とは思想やルール、実装する上での責任が異なるため、チームが採用しているアーキテクチャを理解して慣れるまで時間がかかる 既存実装の仕様を確認する難しさ プロパティの初期値やデータソースへのアクセス、データを読み書きするタイミングなど、既存実装には多くの仕様が存在しており、それらすべてを確認して実装を進めるのが困難 テストコード実装時に発覚した「隠れた密結合」 設計のレビューを通過し各レイヤーの責務を定義したはずが、いざ実装を進めるとデータソースの抽象化ができておらずユニットテストを書けない状態で手戻りが発生する 700行以上に及ぶFatViewController 既存の実装では1つのViewController内に画面の状態からデータソースへのアクセスまで全てのコードが記述されていました。責務分離がなされていないためクラスをDIできず、ユニットテストの記述が困難な状況です。また、StoryboardでUIを開発していたためメンテナンスコストが高くなっていました。実際にメンバーからは「デバッグ用のフラグを1つ追加したいだけなのに、Storyboardの修正や依存関係のあるコードの把握に時間がかかってしまう」という声が上がっていました。本来は開発を効率化するためのツールであるはずのデバッグ画面が、機能追加のたびに負債が溜まっていく画面になっていたのです。 課題を解決するための取り組み 前述した課題を解消するため2点取り組みました。 Claude CodeのSubagentsを使いキャッチアップを高速化 MVVM+UseCaseアーキテクチャ導入 Claude CodeのSubagentsを使いキャッチアップを高速化 1つ目の課題で挙げたコードやドメインに対する知識不足を解決するため、コードの分析からプランニングまでをサポートするClaude CodeのSubagentsを3つ作成しました。Subagentsで分析することで、自力で読み解くよりも高速なドメイン知識の獲得が可能です。また、分析結果をもとにドキュメントを作成してもらうことで、レビューの説明文にも活かせます。 作成したSubagentsと役割は以下の通りです。 spec-refactorer 既存コードを解析し、現状のロジックや仕様を整理 ios-architecture-engineer アーキテクチャ導入に向けた技術的な調査と、依存関係の整理 refact-planner 調査結果を元に、実装の優先順位や具体的な手順のプランニングを提案 3つのプロンプトを載せることは記事の都合上できないため、 spec-refactorer のみを紹介します。 spec-refactorer のプロンプトは以下の通りです。 ## 出力フォーマット に記載されているような画面の構成やデータフローなどをまとめます。 // 重要な部分のみ抜粋 ## 目的 * 画面を開いてからの **データフロー**(依存解決→API→変換→State更新→描画)と、ユーザー操作(例: **Aというコンポーネントをタップ**)時の **処理・I/O・状態/遷移** を一目で把握できるようにする。 ## 出力フォーマット 1. TL;DR(初回ロードと主要アクションのI/Oを箇条書き) 2. 画面の構成(UI/State/Lifecycleの表) 3. データフロー(画面表示→初回ロードのシーケンス図) 4. UIコンポーネント×アクション×副作用の対応表(Cell/ボタン/トグル/Pull-to-Refresh/ページネーション/セル内ボタン含む) 5. 主アクション毎のシーケンス図(最低A=主ボタン, Refresh, セル選択) 6. ネットワークI/O一覧(Method/Path/Auth/キャッシュ/失敗時/呼出根拠) 7. 状態管理(公開State, アクション(必ず網羅すること。アクションを過不足なく網羅できるかがUXに直結する), 非同期/キャンセル) 8. DI(依存解決の流れ)/ナビゲーション 9. エラー/ローディング/空状態 10. イベントログ送信箇所(ない場合は無しと記載) 11. 分析イベント/フラグ 12. リスク・改善 13. Reference ## 探索範囲 * **関連するファイルは可能な限り探索・調査** する(ViewModel/Reducer/Repository/API/Router/DI/テスト/拡張/ユーティリティを横断参照)。 ## 網羅してほしい操作例(該当するもののみ) * 画面表示(初回ロード) * 主要ボタン(例: AddToCart/Favorite/Buy)タップ * セル選択(詳細遷移) * Pull-to-Refresh / ページネーション * 失敗時のエラーハンドリング ## 出力スタイル * 判断根拠となるコードは丁寧に過不足なく提示してください。 * 技術構成を図に描く場合は、左側にUI層で右に行くほどDomain,Data層になるようにしてください。 * 出力は日本語でお願いします。 以下の点が整理されるようにSubagentsを設計しました。これによってハルシネーションや考慮漏れを抑えた出力を得られるようになります。 判断根拠となるコードをドキュメントの最後に記載させる ユーザーアクションを軸にしてデータフローを整理させる 見落としがちなポイントも確認させる エラーやローディング時の挙動 ログ送信の有無 外部へ公開しているプロパティ(画面の状態)は何か MVVM+UseCaseアーキテクチャ導入 2つ目の課題で挙げられていた「Fat」な実装を解消するため、MVVM+UseCaseアーキテクチャを導入しました。MVVM+UseCaseアーキテクチャは Android Architecture Components を参考にしたアーキテクチャであり、以下のように役割を分担させます。 View / ViewController UIレイアウトや画面遷移 ViewModel イベントハンドリングやViewに最適化したデータ整形などのプレゼンテーションロジック UseCase キャッシュ管理などの特定のViewに依存しないビジネスロジックをカプセルし、原則として状態を持たない Translator DataSourceで取得したデータをUseCaseで扱える型へ変換することで、UseCaseにAPI等の知識が入り込むことを防ぐ DataSource データソース(APIやUserDefaultsなど)へアクセス ViewModel、UseCase、DataSourceはprotocolで抽象化しDIを可能にしました。これにより、UseCase、DataSourceのモックを作ることで、依存関係のあるクラスのテストを書けるようになります。 アーキテクチャ導入によりプレゼンテーションとドメインが疎結合となり、UI層のコードの変更が容易になったため、SwiftUIへの移行がスムーズに行えました。 結果 取り組みによって得られた結果は以下の通りです。 デバッグ画面のテストカバレッジ0→93.5% UI実装をStoryboardからSwiftUIへ完全移行 開発工数40%減少 デバッグ画面のテストカバレッジ0→93.5% 2つ目の課題で挙げたように既存実装は密結合が原因でテストを書けませんでしたが、ViewModel、UseCase、Translatorに対してユニットテストを書けるようになりました。 カバレッジを計測するとデバッグ画面に関連するテストの平均が93.5%でした。残りの6.5%はテスト不要なコードが対象となっているため、実質100%となります。 UI実装をStoryboardからSwiftUIへ完全移行 画面の実装をStoryboardからSwiftUIへ完全に移行させました。既存実装ではカスタムコンポーネントを使っていました。しかし、これらはOSのアップデートに伴うデザインシステムの変更や仕様変更の影響を受けやすく、その都度レイアウトの調整や挙動の修正が必要になるため、メンテナンスコストが増大していました。このメンテナンスコストを抑えるため、カスタムコンポーネントをSwiftUI化に合わせて廃止し、 Apple標準のコンポーネントのみ で画面を構成しました。 開発工数40%減少 既存実装に合わせたViewModelのリファクタリングや網羅的なユニットテスト実装など、Claude Codeに実装をサポートしてもらいました。 結果、Claude Code導入前に(先輩社員と相談し)見積もっていた 工数35日が、Claude Codeを使うと20日で完了 しました。約40%の工数削減となります。また、コードを書く時間が減り設計や実装方針を考える時間が増えたためClaude Codeなしに比べきれいなコードを書けました。 MVVM+UseCaseアーキテクチャ導入を振り返って 振り返ると、序盤は以下のような不安を抱えていました。 デバッグ画面のキャッチアップに時間がかかりすぎて、他画面のキャッチアップに時間を使えないのではないか 既存仕様を確認できておらずリグレッションを発生させてしまうのではないか (今後アサインされるであろう)他案件と折り合いがつかず中途半端に終わらせてしまうのではないか 冒頭でもお話ししたように、これらの不安の根本は「広い範囲のキャッチアップをうまく行えない」ことです。原因に序盤で気づき、Claude CodeのSubagentsを使ったキャッチアップ方法を模索するようになりました。初めは欲しい情報を得られずプロンプトを何度もチューニングしていましたが、今では開発に活きる情報(画面の状態やデータフローなど)が得られるようになっています。このように試行錯誤しながら作り上げたSubagentsのおかげでスピード感を持ってキャッチアップを進められました。また、Subagentsに調査結果をドキュメント化してもらうことで、ドキュメント生成のスピードも格段に上がりました。 これらの実績をまとめ、2025年10月には社内の全エンジニアを対象とした技術共有会で紹介しました。紹介をきっかけにiOSチーム以外の方にも活動を認知してもらい、「デバッグ画面を使いやすくなって作業が楽になりました!」のようなポジティブなフィードバックを多くいただきました。嬉しかったです。 アーキテクチャ導入を担当させてくれてサポートまでしてくれた上長やチームメンバーに感謝でいっぱいです。 最後に 本記事では、新卒1年目の私がAIを使ってデバッグ画面にアーキテクチャを導入した際の取り組みを紹介しました。現在は、その経験を活かして、より難易度の高い別画面への導入を進めています。試行錯誤の日々ですが、今回作成したSubagentsのおかげでコードリーディングの負担は格段に減り、開発スピードも上がったように感じています。本記事が、私と同じくキャッチアップの速度に課題を感じている方に届き、開発の生産性向上に少しでも貢献できれば幸いです。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com アーキテクチャの設計およびレビュー体制は「 ZOZOTOWN iOSアプリでのFatViewController解消への取り組み 」をご覧ください ↩
メルペイ インターンでの挑戦と学び:EGP Cardsと向き合った3ヶ月間 こんにちは。メルペイのGrowth Platformでフロントエンド・エンジニアとしてインターンをしている @Yusaku (宮田 優作)です。 この記事は、 Merpay & Mercoin Advent Calendar 2025 の25日目の記事です。 私は2025年10月からインターンを開始し、今月で3ヶ月目になりました(図1)。 この記事では、インターン期間中に取り組んだタスクと得た学びについて紹介します。 図1:オフィスで撮影した私の写真 チームについて 私が所属する Growth Platform Frontend チームは、Engagement Platform(通称EGP)という社内向けマーケティングツールを開発しています。 このツールを使うことで、マーケターや プロジェクトマネージャーは、ポイントやクーポンなどのインセンティブ配布、ランディングページ(LP)の作成・公開、キャンペーン作成といった CRM業務をコーディング不要で簡単に行うことができます(図2)。 図2:EGPのノーコードエディタ(EGP Content) 今回のインターンではEGP Cardsという機能の向上に取り組みました。EGP Cardsは、Web・iOS・Androidのクロスプラットフォームで利用できるカード型のコンポーネントを作成・公開する機能です。 EGP Cardsは、いわゆるWebページのエディタ機能(EGP Pages)とは異なり、サーバがUIの構造を返却するというServer Driven UIアーキテクチャを採用しています。エディタ上で作成されたコンポーネントのコンテンツはJSONファイルとして保存され、各プラットフォームで共通のUIとして描画されます(図3)。 Server Driven UIとEGP Cardsのアーキテクチャについては、同じくGrowth Platformチームの@togamiさん、@Stefanさんの記事をそれぞれご覧ください。 WYSIWYGウェブページビルダーを支える技術とServer Driven UIへの拡張 Supercharging User Engagement: How Mercari is Using Server-Driven UI to Reduce Time-to-Market 図3:EGP Cardsの編集画面 タスク1 – Dry Run for EGP Cards タスク概要:Dry Runとは? Dry Runとは、変数を設定し、そこにデータを挿入することで、状態をエミュレートできる機能のことです。これにより、API呼び出しを記述したり実機で動作確認を行ったりする前に、コンテンツの挙動を確認することができます。 このタスクでは、EGP CardsでDry Run機能を利用できるようにする実装を行いました(図4)。 図4:今回実装した、EGP CardsのDry Run機能 動作の仕組み Dry Run機能は、以下の流れで動作します。 利用者がDry Runを有効化し、フィールドにモックデータを入力する エディタが構造ツリーを再帰的に走査し、動的にJavaScriptのコードを評価して変数を値に置換する 置換された値がキャンバス上に表示される 実装の流れ 以下の流れで実装を進めました。 EGP Pagesに既に実装されていたDry Run機能について、コードリーディングやロギングを行い、実装ロジックを理解する EGP Cards特有の仕様を理解した上で、同様に利用できるDry Run機能を実装する コーディングの際には、EGP PagesとEGP Cardsで共通利用できそうな処理を探し、適度にファイルを切り出すことで、可読性・保守性を意識した実装を心がけました。 タスク2 – Content Agent Improvement for EGP Card 背景:Content Agentの課題 EGPのノーコードエディタ(EGP Content)は、CardsのほかにもPagesやE-mailsなど、複数の種類のコンテンツを扱うことができます。 また、EGP ContentにはAIエージェント(通称 Content Agent)が導入されており、対話を通じてコンテンツの要約や書き換えを行うことができます(図5)。 一方で、当時のContent Agentは、コンテンツ種別ごとのエディタ仕様を十分に理解していないという課題がありました。その結果、UIが崩れたコンテンツを生成してしまい、利用者の期待通りのアウトプットを提供できない可能性がありました。 図5:Content Agentの会話処理パイプライン 実装の流れ この課題を解決するため、以下の流れで実装を進めました。 EGP Cardsの仕様やデータ構造を記述したプロンプトを作成する 作成したプロンプトを、Content AgentのAgent Layerで条件付きで注入する EGP Cardsには、メディアクエリに対応していないことや、すべての要素がFlexで構成されていることなど、いくつかの制約があります。これらの制約や期待される出力をプロンプトに明示することで、Content AgentがCardsに適したコンテンツを生成できるようにしました(図6)。 図6:EGP CardsでContent Agentを利用する様子 学んだこと チーム開発におけるアウトプットの出し方を学んだこと Dry Run 機能の実装を通じて、チーム開発におけるアウトプットの出し方について多くの学びがありました。実装の正しさや機能の完成度だけでなく、Pull Request(PR)の切り方やレビューの受け方が、チーム全体の開発効率や生産性に大きく影響することを実感しました。 具体的には、バグ修正やリファクタリングであっても、PRのサイズが大きくなりすぎる場合やタスクのスコープ外に及ぶ場合には、別のPRとして切り出すことでレビューコストを抑えられることを学びました。また、コードやPRコメントには、なぜその実装にしたのか、どのような選択肢があり、何をしない判断をしたのかといった実装意図を明示することが重要です。これにより、レビュワーとの認識齟齬を防ぎ、建設的なレビューにつながると感じました。 レビューを受けた際にも、指摘内容をすぐに修正として反映するのではなく、まずはレビュワーの意図を正しく理解することが重要です。場合によっては背景や前提条件をすり合わせた上で議論することで、より良い設計や実装にたどり着けることを学びました。これらの経験を通じて、個人としてコードを書く力だけでなく、チームで価値を届けるためのコミュニケーションや姿勢の重要性を強く認識しました。 メルカリカルチャーを実体験として理解できたこと メルカリでは、情報の透明性やフラットな意思疎通によって、個人に大きな裁量が与えられていると言われることが多いです。実際にインターンを通じて、その点を強く実感しました。一方で、個人的に特に印象に残ったのは、英語を前提としたグローバルな開発環境です。 これまで参加してきたインターンはすべて日本語環境だったため、ドキュメントやコミュニケーション、議論の場がすべて英語になる経験は非常に新鮮でした。グローバルなチームである以上、英語でのスムーズな意思疎通が求められることは理解していましたが、実際に業務の中でそれを実践し、議論や開発を進められたことに大きなやりがいを感じました。 実際の業務では、英語で書かれたREADMEや仕様書を読み込んだ上でPull Requestを作成し、設計意図や懸念点を英語で説明・議論しました。認識の齟齬を防ぐため、必要に応じて日本語での補足も行いながら、主体的にコミュニケーションを取ることを意識しました。この経験を通じて、メルカリのカルチャーは単なるスローガンではなく、日々の業務に根付いたものだと感じました。 技術的挑戦を通じて、学びの広がりを再認識できたこと これまで私は、フロントエンドを主な技術領域として、インターンや個人開発に取り組んできました。そのため、フロントエンド領域における学びは、徐々に頭打ちになりつつあるのではないかと感じていました。 しかし、EGPというツールに触れたことで、その考えは大きく変わりました。EGPは非常にインタラクティブでリッチなUIを持つだけでなく、その裏側では、ノーコードによるコンテンツの作成・配信の仕組みや、安全かつ効率的にAI Agentとやりとりを行うための設計など、複雑で奥深いロジックが支えられていることを知りました。 タスクでは、ある程度抽象度のある状態で要件を受け取り、自分で具体的な実装タスクへ分解した上で設計・実装を進めました。また、EGPの使い方をキャッチアップしている最中に、イメージのプレビュー機能があることで利用者体験が向上するのではないかといった改善提案も行いました。 さらに、Content Agentの改善では、今回のCards向け実装に閉じることなく、将来的にPagesやE-mailsなど他のContent typeにも展開しやすいよう、Typeごとにプロンプトを切り出す設計とし、可読性や拡張性を意識しました。エンジニアがプロダクトの将来を見据えて設計・実装することが、利用者体験の向上や業務効率化につながり、結果として事業価値に直結する点は、メルカリならではの魅力だと感じています。 おわりに 今回のインターンでは、EGP Cardsという機能の向上に取り組みました。インターンを通じて、技術的なスキルだけでなく、プロダクトの価値やチームとの関わり方を含めてエンジニアリングに向き合う姿勢を学ぶことができました。 実務を通して得たこれらの学びを、今後の開発や自身の成長につなげていきたいと考えています。最後までお読みいただきありがとうございました。

動画

該当するコンテンツが見つかりませんでした

書籍