Slide 1

Slide 1 text

Next.js でリアーキテクトした話 JSConf JP 2021 @Takepepe

Slide 2

Slide 2 text

吉井 健文 / Takepepe 株式会社リクルート 横断エンジニアリング部 APソリューショングループ フロントエンドエンジニア・アーキテクト

Slide 3

Slide 3 text

リクルートでの働き方 横断組織の一員として、 社内事業ソフトウェア開発・技術支援をしています。 主に、得意なフロントエンド開発で携わっています。

Slide 4

Slide 4 text

これまでの働き方 【新規立ち上げプロジェクト】 ■ 最適な技術スタックをメンバーと選定し、フルスクラッチ 【リアーキテクトプロジェクト】 ■ 既存 MPA(Template-Engine)を FE / BE に分離するプロジェクト

Slide 5

Slide 5 text

これまでの働き方 【新規立ち上げプロジェクト】 ■ 最適な技術スタックをメンバーと選定し、フルスクラッチ 【リアーキテクトプロジェクト】 ■ 既存 MPA(Template-Engine)を FE / BE に分離するプロジェクト 「開発体制構築・開発フロー構築」をした話

Slide 6

Slide 6 text

フロントエンド技術スタック ■ Next.js ■ CSS Modules ■ SWR ■ OpenAPI Generator ■ MSW ■ Storybook ■ reg-suit ■ jest ■ testing-library ■ Cypress

Slide 7

Slide 7 text

Next.js 選定基準 ■ ページ分割による最適化など、パフォーマンス面でメリットを得やすい ■ 数あるアプローチの中から、要件に適合したものを選べる ■ SSR / ISR / SSG など、組み合わせが自在(AMP の実績も) 社内ナレッジも蓄積されてきている

Slide 8

Slide 8 text

アーキテクチャ構成 ■ Node.js プロセスは稼働しない ■ Static Generation による full CSR ■ Web Server は Nginx API Server Next.js SSG + CSR Nginx

Slide 9

Slide 9 text

API 検討 ■ BFF として機能する API サーバー ■ BFF 開発はバックエンドチームが担当 ■ FE 観点から、適した API を提案 ■ Component 境界 = API 境界 = タスク境界 画面単位(Component単位)がタスク境界としても適切な粒度だった

Slide 10

Slide 10 text

プロジェクト課題整理 【1】リリース済み内容をリグレッションさせないこと 【2】React 開発に慣れたメンバーがアサインできるとは限らないこと 【3】フロントエンドエンジニアが先行して OpenAPI を定義する必要がある 【4】10人並列でも開発可能な体制をつくること

Slide 11

Slide 11 text

【課題1】 リリース済み内容を リグレッションさせないこと

Slide 12

Slide 12 text

リリース済み内容に沿った、マークアップの再現について。 再現するプロジェクトの CSS は BEM で書かれていました。 (全体的に綺麗に設計されている良い設計) BEM の Block は Component 単位として扱うことができるため、 CSS 定義から遡り、Component へ書き換えを進めました。 React Component への書き換え 【課題1】リリース済み内容をリグレッションさせないこと

Slide 13

Slide 13 text

書き換え前のマークアップ .breadcrumb { display: flex; &__item { padding-left: 24px; } &--mb0 { margin-bottom: 0; } } 【課題1】リリース済み内容をリグレッションさせないこと

Slide 14

Slide 14 text

書き換え後の React Component // Block はアッパーキャメル変換し、そのまま Component名称とする function Breadcrumb(props) { return
    } // Element は Block 接頭辞をもった Component名称とする function BreadcrumbItem(props) { return
  • } function MyComponent() { return ( ) } .breadcrumb { display: flex; } .item { padding-left: 24px; &[data-mb0] { margin-bottom: 0; } } 【課題1】リリース済み内容をリグレッションさせないこと
  • Slide 15

    Slide 15 text

    書き換えのマッピングコスト 元の CSS 設計が整備されていたこともあり、 スムーズに書き換えを行うことができました。 ■ CSS Modules に BEM 定義を移植 ■ 命名は変更せず、Case 変換し Component 名称とした ■ Modifier を data 属性に変換した ■ class 名から Component (Atom) の特定が容易 【課題1】リリース済み内容をリグレッションさせないこと

    Slide 16

    Slide 16 text

    元の CSS 設計が整備されていたこともあり、 スムーズに書き換えを行うことができました。 ■ CSS Modules に BEM 定義を移植 ■ 命名は変更せず、Case 変換し Component 名称とした ■ Modifier を data 属性に変換した ■ class 名から Component (Atom) の特定が容易 書き換えのマッピングコスト 探しやすく、マッピングコストが最小限。再現性の高い書き換えに 【課題1】リリース済み内容をリグレッションさせないこと

    Slide 17

    Slide 17 text

    CSS 定義の削除 .alert-text { color: $col-accent; } .annotation { font-size: 1rem; } .nodata { display: block; text-align: center; } 最後まで残った、 使われているか不明な Global 定義。 【課題1】リリース済み内容をリグレッションさせないこと

    Slide 18

    Slide 18 text

    CSS 定義の削除 最後まで残った、 使われているか不明な Global 定義。 この定義削除にあたり、 Visual Regression Testing を活用。 【課題1】リリース済み内容をリグレッションさせないこと .alert-text { color: $col-accent; } .annotation { font-size: 1rem; } .nodata { display: block; text-align: center; }

    Slide 19

    Slide 19 text

    ■ 書き換えのタイミングで、Storybook をコミットしていた ■ リグレッションに備え、reg-suit を導入していた ■ CSS に対し、積極的なリファクタリングが行えた 書き換えのマッピングコスト 【課題1】リリース済み内容をリグレッションさせないこと

    Slide 20

    Slide 20 text

    ■ 書き換えのタイミングで、Storybook をコミットしていた ■ リグレッションに備え、reg-suit を導入していた ■ CSS に対し、積極的なリファクタリングが行えた 書き換えのマッピングコスト Storybook は開発軸だけでなく、テスト戦略としても必須 【課題1】リリース済み内容をリグレッションさせないこと

    Slide 21

    Slide 21 text

    ■ 現在、CSS ソリューションはかつてないほど乱立 ■ 異なる CSS ソリューション間で移植しやすい定義かどうか ■ 特定の CSS ソリューションでしか実現できない内容ではないか 次世代のリアーキテクトに向けて 【課題1】リリース済み内容をリグレッションさせないこと

    Slide 22

    Slide 22 text

    ■ 現在、CSS ソリューションはかつてないほど乱立 ■ 異なる CSS ソリューション間で移植しやすい定義かどうか ■ 特定の CSS ソリューションでしか実現できない内容ではないか 次世代のリアーキテクトに向けて CSSソリューションは、次世代のマッピングコストも配慮して 【課題1】リリース済み内容をリグレッションさせないこと

    Slide 23

    Slide 23 text

    【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと

    Slide 24

    Slide 24 text

    まだ React は「誰でも書ける」まで至っていません。 初学者であっても、参画しやすいコードベースを目指しました。 ■ 見様見真似でも、ある程度実装できる ■ 学習しながらでも、開発に参画できる ■ つまづきやすいポイント・そうではないポイントが分離されている 初学者でも参画しやすいコードベースを目指して 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと

    Slide 25

    Slide 25 text

    初学者でも参画しやすいコードベースを目指して まだ React は「誰でも書ける」まで至っていません。 初学者であっても、参画しやすいコードベースを目指しました。 ■ 見様見真似でも、ある程度実装できる ■ 学習しながらでも、開発に参画できる ■ つまづきやすいポイント・そうではないポイントが分離されている コードベースの理解しやすさは、学習曲線に影響する 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと

    Slide 26

    Slide 26 text

    ■ 【易】Component 構築 → マークアップの延長線 ■ 【易】型定義で縛って、Props を渡す ■ 【難】込み入った hooks の記述 ■ 【難】React Context Provider の設計 React 初学者でも習得しやすい点 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと

    Slide 27

    Slide 27 text

    ■ 【易】Component 構築 → マークアップの延長線 ■ 【易】型定義で縛って、Props を渡す ■ 【難】込み入った hooks の記述 ■ 【難】React Context Provider の設計 React 初学者が習得し辛い点 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと

    Slide 28

    Slide 28 text

    ■ 【易】Component 構築 → マークアップの延長線 ■ 【易】型定義で縛って、Props を渡す ■ 【難】込み入った hooks の記述 ■ 【難】React Context Provider の設計 React 初学者が習得し辛い点 Custom Hooks の整備に着手 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと

    Slide 29

    Slide 29 text

    Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent() { const { openModal } = useModal(); const handleClick = () => { openModal({ contentsNode:
    }); }; } モーダルを表示する Custom Hooks

    Slide 30

    Slide 30 text

    Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent() { const { openAlertDialog } = useAlertDialog(); const handleClick = () => { openAlertDialog({ text: "エラーが発生しました ", buttonLabel: "OK", }); } } ダイアログを表示する Custom Hooks

    Slide 31

    Slide 31 text

    Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent() { const { showNotification } = useNotification(); const handleClick = () => { showNotification("削除しました"); } } ノティフィケーションを表示する Custom Hooks

    Slide 32

    Slide 32 text

    Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent() { const { errorMessages, handleErrorWithErrorMessages, } = useHandleErrorWithErrorMessage(); const { execFetch } = useFetcher(async () => { try { ... } catch (err) { handleErrorWithErrorMessages(err); } }); } エラーハンドリングを集約した Custom Hooks

    Slide 33

    Slide 33 text

    Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent() { const { validationErrors, handleValidationErrorsWithAlertDialog, } = useHandleValidationErrorsWithAlertDialog(); const { execFetch } = useFetcher(async () => { try { ... } catch (err) { handleValidationErrorsWithAlertDialog(err); } }); } エラーハンドリング・Component 表示を兼ねた Custom Hooks

    Slide 34

    Slide 34 text

    ■ 多くの画面で処理を共通化できた ■ 見様見真似でも Component 作成が可能に ■ 画面毎の考察ポイントは、画面固有のロジック実装のみ Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと Custom Hooks でコードベースを標準化することができた

    Slide 35

    Slide 35 text

    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 36

    Slide 36 text

    OpenAPI でバックエンドとフロントエンドの齟齬をなくす ■ BFF として機能する API サーバー ■ Component を起点として API を考える ■ フロントエンドエンジニアが OpenAPI を書く スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 37

    Slide 37 text

    OpenAPI でバックエンドとフロントエンドの齟齬をなくす ■ BFF として機能する API サーバー ■ Component を起点として API を考える ■ フロントエンドエンジニアが OpenAPI を書く スキーマ駆動開発 API 定義が予め決まっている通常の開発フローとは「逆」 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 38

    Slide 38 text

    OpenAPI でバックエンドとフロントエンドの齟齬をなくす ■ BFF として機能する API サーバー ■ Component を起点として API を考える ■ フロントエンドエンジニアが OpenAPI を書く スキーマ駆動開発 OpenAPI 定義に慣れている開発メンバーは少数。定義のしやすさが課題に 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 39

    Slide 39 text

    OpenAPI 定義への取り組み ー User 編集モーダルを作る例 ー 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 40

    Slide 40 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある Component を組み上げ、API に求めるデータを考える export type ServerProps = { }; export const ModalEditUser: React.VFC = (props) => { return ( ); };

    Slide 41

    Slide 41 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある export type ServerProps = { id: string; firstName?: string; lastName?: string; }; Component を組み上げ、API に求めるデータを考える export const ModalEditUser: React.VFC = (props) => { return ( 送信 ); };

    Slide 42

    Slide 42 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある Component を組み上げ、API に求めるデータを考える export type ServerProps = { id: string; firstName?: string; lastName?: string; }; export const ModalEditUser: React.VFC = (props) => { return ( 送信 ); };

    Slide 43

    Slide 43 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある MSW でモックを書き、Component が成立するか検討 export const handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({})); }), ];

    Slide 44

    Slide 44 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある MSW でモックを書き、Component が成立するか検討 export const handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ];

    Slide 45

    Slide 45 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある 更新系のメソッドも、モックが記述可能 export const handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ]; export const handlers = [ rest.put("/path/to/api", (req, res, ctx) => { return res(ctx.json({ statusCode: 200 })); }), ];

    Slide 46

    Slide 46 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある 要件定義とすりあわせ、考慮漏れの洗い出し export const handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ]; export const handlers = [ rest.put>("/path/to/api", (req, res, ctx) => { return res(ctx.json({ statusCode: 200 })); }), ];

    Slide 47

    Slide 47 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある 要件定義とすりあわせ、考慮漏れの洗い出し export const handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ]; export const handlers = [ rest.put>("/path/to/api", (req, res, ctx) => { if (req.body.id) { return res(ctx.json({ statusCode: 200 })); } return res( ctx.status(400), ctx.json({ statusCode: 400, errorMessages: ["不正なリクエストです"] }) ); }), ];

    Slide 48

    Slide 48 text

    API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある API スキーマ考察の軸となった export const handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ]; export const handlers = [ rest.put>("/path/to/api", (req, res, ctx) => { if (req.body.id) { return res(ctx.json({ statusCode: 200 })); } return res( ctx.status(400), ctx.json({ statusCode: 400, errorMessages: ["不正なリクエストです"] }) ); }), ];

    Slide 49

    Slide 49 text

    Stoplight Studio を使う Open API 定義にあたり、 チーム内標準ツールとして Stoplight Studio を使用。 MSW のレスポンスから、 サンプル JSON を取得。 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 50

    Slide 50 text

    Stoplight Studio を使う Stoplight Studio には、 JSON から OpenAPI 定義を Generateできる機能がある。 この機能を使い、 OpenAPI 定義の工数を削減。 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 51

    Slide 51 text

    Stoplight Studio を使う 不足している定義を追加し、 description に詳細を記載。 レビューコミュニケーションに 役立てた。 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 52

    Slide 52 text

    スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある $ npm run openapi:gen 書き上がった OpenAPI から、 fetch client を生成 ######################################### # Thanks for using OpenAPI Generator. # # Please consider donation to help us maintain this project 🙏 # # https://opencollective.com/openapi_generator/donate # #########################################

    Slide 53

    Slide 53 text

    スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある fetch client を、 Component にバインド export const ModalEditUserBase: React.VFC = (props) => { return ( 送信 ); }; export const ModalEditUser: React.VFC = () => { const { data, error } = useSWR( "/path/to/api", get("path/to/api") ); if (error) return ; if (!data) return <>loading...>; return ; };

    Slide 54

    Slide 54 text

    スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある export const ModalEditUserBase: React.VFC = (props) => { return ( 送信 ); }; export const ModalEditUser: React.VFC = () => { const { data, error } = useSWR( "/path/to/api", organismsApi.organismsModalEditUserGet() ); if (error) return ; if (!data) return <>loading...>; return ; }; fetch client を、 Component にバインド

    Slide 55

    Slide 55 text

    スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある この段階で期待値と、OpenAPI 定義に齟齬があれば気付く(型推論あわせ) export const ModalEditUserBase: React.VFC = (props) => { return ( 送信 ); }; export const ModalEditUser: React.VFC = () => { const { data, error } = useSWR( "/path/to/api", organismsApi.organismsModalEditUserGet() ); if (error) return ; if (!data) return <>loading...>; return ; };

    Slide 56

    Slide 56 text

    スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある ■ 確度の高い状態で、バックエンド担当にレビュー依頼 ■ レビューで受けた指摘のもと、OpenAPI 定義を修正 ■ 修正内容を伝搬させるため、fetch client を再生成 ■ 影響箇所を TypeScript のコンパイルエラーで検知

    Slide 57

    Slide 57 text

    スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある ■ 確度の高い状態で、バックエンド担当にレビュー依頼 ■ レビューで受けた指摘のもと、OpenAPI 定義を修正 ■ 修正内容を伝搬させるため、fetch client を再生成 ■ 影響箇所を TypeScript のコンパイルエラーで検知 OpenAPI と Component の疎通齟齬が TypeScript で担保された

    Slide 58

    Slide 58 text

    【課題4】 10人並列でも開発可能な体制を つくること

    Slide 59

    Slide 59 text

    課題「123」の取り組みでコードベースの標準化が整う ✅ 課題1:マークアップから Component への確実なマッピング ✅ 課題2:React Component の一律な実装 ✅ 課題3:OpenAPI 定義検討のフロー 増員とチームビルディング 【課題4】10人並列でも開発可能な体制をつくること

    Slide 60

    Slide 60 text

    量産フェーズに入り、大人数での並列開発が開始 ■ 2人 → 5人 → 10人 と、1ヶ月間隔でメンバーが倍増 ■ 新規メンバー受入を兼ねながら進行 ■ 並行して開発可能な体制を整える 増員とチームビルディング 【課題4】10人並列でも開発可能な体制をつくること

    Slide 61

    Slide 61 text

    量産フェーズに入り、大人数での並列開発が開始 ■ 2人 → 5人 → 10人 と、1ヶ月間隔でメンバーが倍増 ■ 新規メンバー受入を兼ねながら進行 ■ 並行して開発可能な体制を整える 増員とチームビルディング スムーズな増員計画を念頭に 【課題4】10人並列でも開発可能な体制をつくること

    Slide 62

    Slide 62 text

    定型処理の自動化 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 63

    Slide 63 text

    hygen による定型出力 大人数での並列開発は、lint・formatter だけでは 防げないガイドライン違反が起こりえます。 また、管理上 OpenAPI と Component の 命名規則・書式を一律にする必要性がありました。 そこで活用したのが「hygen」です。 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    Slide 64

    Slide 64 text

    $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > [email protected] new:swrfc > hygen new swrfc ✔ What is the name of component? · npm scripts に hygen のエントリーポイントを登録 hygen による定型出力

    Slide 65

    Slide 65 text

    $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > [email protected] new:swrfc > hygen new swrfc ✔ What is the name of component? · ModalEditUser CLI に Component 名称を入力すると… hygen による定型出力

    Slide 66

    Slide 66 text

    $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > [email protected] new:swrfc > hygen new swrfc ✔ What is the name of component? · ModalEditUser Loaded templates: .hygen added: src/components/organisms/ModalEditUser/apis.mock.ts added: src/components/organisms/ModalEditUser/ModalEditUser.stories.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.test.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.tsx added: src/components/organisms/ModalEditUser/index.tsx added: /Users/Me/openapi/src/paths/organisms/modal_edit_user.yaml added: src/components/organisms/ModalEditUser/styles.module.scss Component 命名に沿ったファイル群が出力される hygen による定型出力

    Slide 67

    Slide 67 text

    $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > [email protected] new:swrfc > hygen new swrfc ✔ What is the name of component? · ModalEditUser Loaded templates: .hygen added: src/components/organisms/ModalEditUser/apis.mock.ts added: src/components/organisms/ModalEditUser/ModalEditUser.stories.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.test.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.tsx added: src/components/organisms/ModalEditUser/index.tsx added: /Users/Me/openapi/src/paths/organisms/modal_edit_user.yaml added: src/components/organisms/ModalEditUser/styles.module.scss React Component に必要なファイル群と… hygen による定型出力

    Slide 68

    Slide 68 text

    $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > [email protected] new:swrfc > hygen new swrfc ✔ What is the name of component? · ModalEditUser Loaded templates: .hygen added: src/components/organisms/ModalEditUser/apis.mock.ts added: src/components/organisms/ModalEditUser/ModalEditUser.stories.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.test.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.tsx added: src/components/organisms/ModalEditUser/index.tsx added: /Users/Me/openapi/src/paths/organisms/modal_edit_user.yaml added: src/components/organisms/ModalEditUser/styles.module.scss OpenAPI 定義の雛形・MSW ハンドラーも同時に自動生成 hygen による定型出力

    Slide 69

    Slide 69 text

    ModalEditUser.tsx 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある export const ModalEditUser: React.VFC = () => { const { data, error } = useSWR(apiPath(), () => get(apiPath())); if (error) return ; if (!data) return <>loading...>; return ; }; ファイル命名ミスがなく、コンポーネント書式も一律に hygen による定型出力

    Slide 70

    Slide 70 text

    ModalEditUser.stories.tsx 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある const defaultArgs: Props = { ...data }; const Template: Story = (args) => ( ); export const Index: Story = Template.bind({}); Index.args = {}; export default { title: "organisms/ModalEditUser", } as Meta; Storybook の雛形ファイルも同時に生成 hygen による定型出力

    Slide 71

    Slide 71 text

    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある describe("organisms/ModalEditUser", () => { describe("異常系レスポンスの表示", () => { describe("エラーレスポンスが返ってきた時", () => { test.skip("エラーが表示される", async () => {}); }); }); describe("正常系レスポンスの表示", () => { describe("正常レスポンスが返ってきた時", () => { test.skip("コンテンツが表示される", async () => {}); }); }); }); ModalEditUser.test.tsx テストの雛形ファイルも同時に生成 hygen による定型出力

    Slide 72

    Slide 72 text

    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある modal_edit_user.yaml post: summary: XXX::画面名称::POST description: 画面概要 operationId: OrganismsModalEditUserPost tags: - Organisms responses: '201': description: Created content: application/json: schema: $ref: ../../components/common_201.yaml '400': description: Bad Request content: application/json: schema: $ref: ../../components/common_400.yaml '403': description: Forbidden content: application/json: schema: $ref: ../../components/common_403.yaml Component に紐づいた Open API の雛形ファイルも同時に生成 hygen による定型出力 openapi: 3.0.0 info: title: XXX::画面名称 version: '1.0' servers: - url: http://localhost:8080 description: development paths: /organisms/modal_edit_user: get: summary: XXX::画面名称::GET description: 画面概要 operationId: OrganismsModalEditUserGet tags: - Organisms responses: '200': description: OK content: application/json: schema: $ref: ../../components/common_200.yaml

    Slide 73

    Slide 73 text

    コード標準化・定型作業の自動化にあわせ「タスク標準化」にも着手 ■ 「定型的な工程・非定型的な工程」を分離 ■ 「レビュー依頼先・レビュー観点」を分離 増員とチームビルディング 【課題4】10人並列でも開発可能な体制をつくること

    Slide 74

    Slide 74 text

    増員とチームビルディング 大人数での開発が本格化する前に、ばらつきが出ない「標準化」を徹底した 【課題4】10人並列でも開発可能な体制をつくること コード標準化・定型作業の自動化にあわせ「タスク標準化」にも着手 ■ 「定型的な工程・非定型的な工程」を分離 ■ 「レビュー依頼先・レビュー観点」を分離

    Slide 75

    Slide 75 text

    分離された5段階タスク 【課題4】10人並列でも開発可能な体制をつくること 1画面あたり、5段階に分離されたタスク Scaffold 生成 Component 構築 API 考察 接続 OpenAPI 定義 テスト拡充 1画面

    Slide 76

    Slide 76 text

    分離された5段階タスク 【課題4】10人並列でも開発可能な体制をつくること PR 単位も一律になるため、差分・レビュー観点が一律に Scaffold 生成 Component 構築 API 考察 接続 OpenAPI 定義 テスト拡充 1画面

    Slide 77

    Slide 77 text

    新規メンバー受け入れと、複数人並列開発を実現した要因 ■ 参画ハードルが低く、標準化されたコードベース ■ タスク境界が分離されているため、閉じた影響範囲で作業が完遂できる ■ 完了した類似タスク範例があるため、作業内容の認知負荷が低い API とComponentが結合している設計 メンバーからも「開発しやすい」と評価 【課題4】10人並列でも開発可能な体制をつくること

    Slide 78

    Slide 78 text

    新規メンバー受け入れと、複数人並列開発を実現した要因 ■ 参画ハードルが低く、標準化されたコードベース ■ タスク境界が分離されているため、閉じた影響範囲で作業が完遂できる ■ 完了した類似タスク範例があるため、作業内容の認知負荷が低い API とComponentが結合している設計 【成果】マイルストンから6週間前倒して開発完了 【課題4】10人並列でも開発可能な体制をつくること

    Slide 79

    Slide 79 text

    設計と開発フローは不可分であり、 「継続的チームビルディングの基礎となる」 という事を実感できた案件でした

    Slide 80

    Slide 80 text

    ー ご清聴ありがとうございました ー