Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ステップバイステップで進めるYahoo!知恵袋のフロントエンドリアーキテクト
Search
Koki Tsumura
November 23, 2024
0
4.3k
ステップバイステップで進めるYahoo!知恵袋のフロントエンドリアーキテクト
Koki Tsumura
November 23, 2024
Tweet
Share
Featured
See All Featured
Building Your Own Lightsaber
phodgson
103
6.1k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
Thoughts on Productivity
jonyablonski
68
4.4k
KATA
mclloyd
29
14k
The Art of Programming - Codeland 2020
erikaheidi
53
13k
Optimising Largest Contentful Paint
csswizardry
33
3k
Mobile First: as difficult as doing things right
swwweet
222
9k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
6.9k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
Making the Leap to Tech Lead
cromwellryan
133
9k
Agile that works and the tools we love
rasmusluckow
328
21k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
32
2.7k
Transcript
© LY Corporation ステップバイステップで進める Yahoo!知恵袋のフロントエンド リアーキテクト LINEヤフー株式会社 津村光輝
© LY Corporation 自己紹介 2 2022/04 新卒入社 2022/06-2023/12 Yahoo!しごとカタログ 2023/01-現在
Yahoo!知恵袋 経歴: 中華料理、中国語、釣り、ボドゲ マイブーム: 名前: 津村 光輝
© LY Corporation 機能 • AI回答機能 • 企業公式Q&A機能 • 商品に関するQ&A機能
3 Yahoo!知恵袋の紹介 • 2004年からあるQ&Aサイト(今年20周年 ) • 登録利用者数5,200万人、質問総数2億8,000万件、回答総数6億5,000万件以上 フロントエンドチーム/システムについて • フロントエンドチーム: 6人 • 行数: 380,000行程度 • 利用技術: Node.js、React、Express.js 等を利用 ※2024年4月3日時点のデータ
© LY Corporation 4 フロントエンドチームが抱える問題 4 問題はたくさんある コードを置く 場所が不明確 バグの検知
がしづらい コーディング規約/ レビュー観点がない コンフリクトの嵐 不具合対応に 時間がかかる
© LY Corporation 5 何をいつ解決するか 何: 開発効率や品質の影響が大きい問題 5 いつ: 解決に必要な時間/分割可能性に依存
• コスト小 • コスト大/分割可能 • コスト大/分割不可能
© LY Corporation 6 何をいつ解決するか 何: 開発効率や品質の影響が大きい問題 6 いつ: 解決に必要な時間/分割可能性に依存
• コスト小 → スキマ時間に解決 • 例: ABテストの仕組み、feature flag、コーディング規約 • コスト大/分割可能 • コスト大/分割不可能
© LY Corporation 7 何をいつ解決するか 何: 開発効率や品質の影響が大きい問題 7 いつ: 解決に必要な時間/分割可能性に依存
• コスト小 → スキマ時間に解決 • 例: ABテストの仕組み、feature flag、コーディング規約 • コスト大/分割可能 → リファクタリングデー • 例: デザインシステムのコンポーネントの実装 • コスト大/分割不可能
© LY Corporation 8 何をいつ解決するか 何: 開発効率や品質の影響が大きい問題 8 いつ: 解決に必要な時間/分割可能性に依存
• コスト小 → スキマ時間に解決 • 例: ABテストの仕組み、feature flag、コーディング規約 • コスト大/分割可能 → リファクタリングデー • 例: デザインシステムのコンポーネントの実装 • コスト大/分割不可能 → 専用の時間を確保 • 例: リアーキテクト、TypeScript化、ユニットテスト改善
© LY Corporation 9 何をいつ解決するか 何: 開発効率や品質の影響が大きい問題 9 いつ: 解決に必要な時間/分割可能性に依存
• コスト小 → スキマ時間に解決 • 例: ABテストの仕組み、feature flag、コーディング規約 • コスト大/分割可能 → リファクタリングデー • 例: デザインシステムのコンポーネントの実装 • コスト大/分割不可能 → 専用の時間を確保 • 例: リアーキテクト、TypeScript化、ユニットテスト改善 ↑ここについて話します
© LY Corporation 10 コスト大/分割不可能な問題 10 バグの検知がしづらい コードを置く場所が不明確 ???
© LY Corporation 11 コスト大/分割不可能な問題 11 バグの検知がしづらい コードを置く場所が不明確 ???
© LY Corporation 12 問題点 12 controllers/detail.js • ルーティング •
受け取ったリクエストを元にレスポンスを返す • リクエストパラメーターのバリデーション 本来あるべき処理 コードを置く場所が不明確
© LY Corporation 13 問題点 13 • 各層との連携 • データの加工
• 業務ロジック • ページに表示する文言に関するロジック controllers/detail.js 特定の関数が肥大化しやすく、処理が追いづらい/品質が下がる • ルーティング • 受け取ったリクエストを元にレスポンスを返す • リクエストパラメーターのバリデーション 本来あるべき処理 それ以外の処理 コードを置く場所が不明確 viewModel applicationService
© LY Corporation 14 14 リアーキテクトする • ドキュメント/ルールの整備 コードを置く場所が不明確 解決策:
リアーキテクト controller controller: • ルーティング • リクエストを元にレスポンスを返す • リクエストパラメーターのバリデーション applicationService model utility
© LY Corporation 15 15 リアーキテクトする • ドキュメント/ルールの整備 コードを置く場所が不明確 解決策:
リアーキテクト controller controller: • ルーティング • リクエストを元にレスポンスを返す • リクエストパラメーターのバリデーション applicationService: • 各層との連携(処理の進行役)を行う • データの加工/様々な業務ロジック model utility
© LY Corporation 16 16 リアーキテクトする • ドキュメント/ルールの整備 コードを置く場所が不明確 解決策:
リアーキテクト controller controller: • ルーティング • リクエストを元にレスポンスを返す • リクエストパラメーターのバリデーション applicationService: • 各層との連携(処理の進行役)を行う • データの加工/様々な業務ロジック model: データとふるまいをまとめたオブジェクト utility: 繰り返し色々な箇所で利用する関数
© LY Corporation 17 17 リアーキテクトする • ドキュメント/ルールの整備 • リファレンス実装
コードを置く場所が不明確 解決策: リアーキテクト controller controller: • ルーティング • リクエストを元にレスポンスを返す • リクエストパラメーターのバリデーション applicationService: • 各層との連携(処理の進行役)を行う • データの加工/様々な業務ロジック model: データとふるまいをまとめたオブジェクト utility: 繰り返し色々な箇所で利用する関数
© LY Corporation 18 解決策: リアーキテクト 18 リファレンス実装の追加 • 今後のリアーキテクトが進めやすくする
• 実装をしているときに迷いづらくする コードを置く場所が不明確
© LY Corporation 19 成果: リアーキテクト 19 • コードの見通しが良くなった(controllerの行数が1100行から120行に!) •
リファレンス実装を元に新アーキテクチャでの実装が進んでいる • ユニットテストが書きやすくなった コードを置く場所が不明確
© LY Corporation 20 コスト大/分割不可能な問題 20 バグの検知がしづらい コードを置く場所が不明確 ???
© LY Corporation 21 バグの検知がしづらい 21 単純なミスに実行時になるまで気づきづらい(JavaScriptで書かれているため) • タイプエラー/オブジェクトのプロパティの上書き ユニットテストのメンテナンスが困難
• コードコピー/mockの乱用 バグの検知がしづらい
© LY Corporation 22 22 単純なミスに実行時になるまで気づきづらい(JavaScriptで書かれているため) • タイプエラー/オブジェクトのプロパティの上書き ユニットテストのメンテナンスが困難 •
コードコピー/mockの乱用 バグの検知がしづらい バグの検知がしづらい
© LY Corporation 23 23 TypeScript化 • でも一切TypeScriptがないところから一気に全部対応するのは難しい、、、 バグの検知がしづらい 解決策
© LY Corporation 24 24 TypeScript化 • でも一切TypeScriptがないところから全部一気に対応するのは難しい、、、 導入したルール: 以下のファイルはTypeScriptで書く
• 新しく作るもの/処理を切り出す場合/更新頻度の高いもの バグの検知がしづらい 解決策
© LY Corporation 25 TypeScript化の困難性 25 膨大な量の型づけ • 一個のファイルから呼び出している別ファイルの処理にも芋づる式で型が必要 •
データの変換を重ねていると型をつけるのが大変、、、 バグの検知がしづらい
© LY Corporation 26 TypeScript化の困難性 26 膨大な量の型づけ • 一個のファイルから呼び出している別ファイルの処理にも芋づる式で型が必要 •
データの変換を重ねていると型をつけるのが大変、、、 並列して進む開発 • TS化している途中に、元のJSファイルに修正が入る バグの検知がしづらい
© LY Corporation 27 27 controllers/detail.jsをコピーして、controllers/detail.tsを作成する バグの検知がしづらい 解決策: TypeScript化 (1/6)
コピー controllers/detail.ts controllers/detail.js
© LY Corporation 28 28 バグの検知がしづらい //ts-nocheck controllers/detail.ts 解決策: TypeScript化
(2/6) • controllers/detail.tsの最初の行に// ts-nocheck を追加する
© LY Corporation 29 29 バグの検知がしづらい //ts-nocheck • controllers/detail.tsの最初の行に// ts-nocheck
を追加する • controllers/detail.tsから呼び出しているファイルに型をつける *.d.ts *.ts detail.tsから呼び出されているJSファイル detail.tsから切り出したTSファイル controllers/detail.ts 解決策: TypeScript化 (3/6)
© LY Corporation 30 30 バグの検知がしづらい //ts-nocheck detail.tsから呼び出されているJSファイル detail.tsから切り出したTSファイル controllers/detail.ts
*.d.ts *.ts • // ts-nocheckを外す • 型がつけられていないものは、ひとまずanyにする(TODOコメントつき) 解決策: TypeScript化 (4/6)
© LY Corporation 31 31 開発は日々進んでいくので定期的に追いつき作業を行う • コピーした当初のdetail.jsと最新のdetail.jsのdiffを見比べて追いつき バグの検知がしづらい 追いつき
controllers/detail.ts controllers/detail.js 解決策: TypeScript化 (5/6)
© LY Corporation 32 32 controllers/detail.jsを削除する バグの検知がしづらい 追いつき controllers/detail.ts controllers/detail.js
解決策: TypeScript化 (6/6)
© LY Corporation 33 成果: TypeScript化 33 • 実行時になって、はじめて発覚するエラーが減った •
安心してリファクタを行いやすくなった • ユニットテストで保証する範囲がわかりやすくなった バグの検知がしづらい
© LY Corporation 34 34 単純なミスに実行時になるまで気づきづらい(JavaScriptで書かれているため) • タイプエラー/オブジェクトのプロパティの上書き ユニットテストのメンテナンスが困難 •
コードコピー/mockの乱用 バグの検知がしづらい バグの検知がしづらい
© LY Corporation 35 35 • ユニットテストがメンテナンスできていない • コードコピーで肥大化(1つのテストファイルで27000行!) バグの検知がしづらい
原因
© LY Corporation 36 36 • ユニットテストがメンテナンスできていない • コードコピーで肥大化(1つのテストファイルで27000行!) •
ユニットテストは通るけど、実行時にエラーになる • mockの乱用 • テストケースの不足 バグの検知がしづらい 原因
© LY Corporation 37 37 ユニットテストの実装&方針の変更 • テスト対象のコードを小さくする(リアーキテクトとセット) • パラメタライズドテストの導入
• mockの初期化を行う関数の作成 • 基本はinputに応じたoutputの確認 • 副作用が発生する関数以外は基本的にmockしない バグの検知がしづらい 解決策: ユニットテストの実装&方針の変更
© LY Corporation 38 38 ユニットテストの実装&方針の変更 • テスト対象のコードを小さくする(リアーキテクトとセット) • パラメタライズドテストの導入
• mockの初期化を行う関数の作成 • 基本はinputに応じたoutputの確認 • 副作用が発生する関数以外は基本的にmockしない バグの検知がしづらい 解決策: ユニットテストの実装&方針の変更
© LY Corporation 39 39 バグの検知がしづらい イメージ 解決策: ユニットテストの実装&方針の変更
© LY Corporation 40 40 成果: ユニットテストの実装/方針の変更 バグの検知がしづらい • ユニットテストが書きやすく/読みやすくなった
• ユニットテストの不足箇所がわかりやすくなった • 失敗してほしいテストが失敗するようになった
© LY Corporation 41 41 どのタイミングでどの問題を解決するか • 問題の種類でいつ解決するかを分類 • 大きな問題でも小さく分割できる場合は、少しずつ進める
どこに何を書くべきかのルールが決まっていない リアーキテクトで解決 • コードの置き場所のルールを整備 • リファレンス実装の追加(かなり重要) バグの検知がしづらい • TypeScript化で解決 • ユニットテストの実装&方針の変更で解決 まとめ
© LY Corporation 参考・引用 Yahoo!知恵袋、サービス開始20周年を記念した特集サイトを公開 https://www.lycorp.co.jp/ja/news/release/007875 回答のつかない質問を減らすために https://chiebukuro.yahoo.co.jp/topic/ai/answer.html typescript-eslint https://typescript-eslint.io/
type-fest https://github.com/sindresorhus/type-fest 42
© LY Corporation ご清聴ありがとうございます!
© LY Corporation 44 Appendix: TypeScriptの導入+α 44 optional propertyを避ける 起こり得ない状態を作れないようにする
オブジェクトのプロパティを上書きできないようにする 以下を利用した。大きな配列やオブジェクトの場合は、eslint-disableする場合もある • @typescript-eslint/prefer-readonly • @typescript-eslint/prefer-readonly-parameter-types • type-festのReadonlyDeep 関数の戻り値の型を明示的にする @typescript-eslint/explicit-function-return-typeを使って、意図せぬ関数の戻り値の型の変更を防ぐために、 戻り値の型を書くことを必須にした バグの検知がしづらい