Slide 1

Slide 1 text

を支える技術と怖い話 2014.12.18 - EBISTA #1 @ UZABASE, Inc.

Slide 2

Slide 2 text

アジェンダ 自己紹介 サービスの概要 サービスの特徴 NewsPicks を支える技術 本当にあった怖い話 まとめ

Slide 3

Slide 3 text

文字 拓郎 TAKURO MONJI @monzou 経歴 ・金融機関のデリバティブトレーディングシステム開発 ・リッチクライアント + 大規模分散計算 ・Web 経験すくなめ(去年から) ・2014 年 9 月 UZABASE 入社(3 ヶ月ほど経ちました) UZABASE 入社後 ・NewsPicks の開発担当(サーバーサイドと Web がメイン) ・今日のイベントの手配あれこれ ・畑違いの分野から来たので色々と新鮮です

Slide 4

Slide 4 text

アジェンダ 自己紹介 サービスの概要 サービスの特徴 NewsPicks を支える技術 本当にあった怖い話 まとめ

Slide 5

Slide 5 text

ご存知ですよね?

Slide 6

Slide 6 text

あの naoya 氏も絶賛

Slide 7

Slide 7 text

NewsPicks 経済に特化したニュースアプリ 世界中のビジネスマンが新しいビジネス情報を発見するための 情報インフラ(世界一の経済メディア)を目指している ・iPhone ・iPad ・Android ・Web などマルチチャネル で展開 ・著名人 ・有識者 ・意思決定者層 などによる 記事に対する コメント

Slide 8

Slide 8 text

サービスが提供するコンテンツ Contents 無料コンテンツ 有料コンテンツ ・様々な経済情報をワンストップで提供 ・著名人/有識者/意思決定者層によるキュレーション ・ユーザーのコメントにより多角的な視座を提供 自社編集部による 独自記事

Slide 9

Slide 9 text

サービスが提供するコンテンツ Contents 無料コンテンツ 有料コンテンツ ・様々な経済情報をワンストップで提供 ・著名人/有識者/意思決定者層によるキュレーション ・ユーザーのコメントにより多角的な視座を提供 自社編集部による 独自記事 有料課金モデル

Slide 10

Slide 10 text

サービスが提供するコンテンツ Contents 無料コンテンツ 有料コンテンツ ・様々な経済情報をワンストップで提供 ・著名人/有識者/意思決定者層によるキュレーション ・ユーザーのコメントにより多角的な視座を提供 自社編集部による 独自記事 Picker 有料課金モデル

Slide 11

Slide 11 text

サービスが提供するコンテンツ Contents 無料コンテンツ 有料コンテンツ ・様々な経済情報をワンストップで提供 ・著名人/有識者/意思決定者層によるキュレーション ・ユーザーのコメントにより多角的な視座を提供 Platform 有識者による 選別・コメント 自社編集部による 独自記事 Picker 有料課金モデル

Slide 12

Slide 12 text

サービスの成長 ユーザー数は 30 万人を突破 0 100,000 200,000 300,000 400,000 JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC 編集部 立ち上げ Web 版 リリース オリジナル 連載開始

Slide 13

Slide 13 text

開発チームの成長 最近ようやくチームっぽく … 0 100,000 200,000 300,000 400,000 JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC 3人前後の 時期が続く 若干増員 パートタイム 含めて 10人弱に

Slide 14

Slide 14 text

アジェンダ 自己紹介 サービスの概要 サービスの特徴 NewsPicks を支える技術 本当にあった怖い話 まとめ

Slide 15

Slide 15 text

特徴 ① 自社コンテンツ 社内に編集部を設置し, オリジナルコンテンツを提供 3 年後には編集部だけで 100 名体制へ 世界一の経済メディアを目指す ・単なるニュースのキュレーションアプリでなく経済メディアへ ・独自記事だけでなく記事の編成なども行う 編集部用の社内システムも構築・運用 ・独自記事入稿・効果測定 ・おすすめニュースの編成 ・おすすめユーザーの編成

Slide 16

Slide 16 text

特徴 ② ヒトの手による価値向上 記事編成・レコメンド ・編集部 + アナリスト + エンジニアのコラボレーション ・社内システムによってオススメ記事やオススメユーザーを管理 ・ヒトとアルゴリズムの融合によるレコメンド 有料会員向けのイベントや NewsPicks Paper の配布 ・有料会員限定のリアルイベントの開催 ・NewsPicks Paper などの特別媒体を配布

Slide 17

Slide 17 text

やる気に満ちた経営陣と荒ぶる LINE

Slide 18

Slide 18 text

やる気に満ちた経営陣と荒ぶる LINE 編集長です

Slide 19

Slide 19 text

やる気に満ちた経営陣と荒ぶる LINE 編集長です 社長です

Slide 20

Slide 20 text

やる気に満ちた経営陣と荒ぶる LINE 編集長です 社長です 職人の手による 温かみのある手動プッシュ通知の様子です

Slide 21

Slide 21 text

特徴 ③ SPEEDA の資産を活用 SPEEDA ・UZABASE が提供する企業・産業分析用の情報プラットフォーム ・SPEEDA の膨大な資産を NewsPicks に活用 具体的な活用内容 ・SPEEDA で利用している記事分類アルゴズム(機械学習)の流用 ・社内アナリストによる質の高いコメントや分析記事 ・今後 SPEEDA のデータベースを活用したより高度な連携も予定

Slide 22

Slide 22 text

アジェンダ 自己紹介 サービスの概要 サービスの特徴 NewsPicks を支える技術 本当にあった怖い話 まとめ

Slide 23

Slide 23 text

インフラ構成の概要 Route 53 Internet Mobile PC Elastic IP ELB Incoming APP (API / Contents/ Web) Batch Elasticsearch RDS (MySQL) ElastiCache (Redis) SQS Dynamo DB SES SNS CloudWatch Redshift

Slide 24

Slide 24 text

インフラ構成の特徴 全面的に AWS に依存 ・人数も少ないのでフルマネージドな環境が魅力的 複数のストレージ ・RDS (MySQL) → ユーザーなどのマスタデータなど ・ElastiCache (Redis) → タイムラインやランキングデータなど ・Dynamo DB → 記事やコメントなどのトランザクションデータなど ・Memcached → キャッシュ(一部) ・Elasticsearch → 全文検索・重複記事チェックなど SQS を介してバックエンドを分散 ・突発的な負荷に備えて非同期に処理 ・各バックエンドサービスは SQS からメッセージを消費

Slide 25

Slide 25 text

フロントエンドの構成 ELB SQS Elasticsearch RDS (MySQL) ElastiCache (Redis) Dynamo DB Nginx Java contents.newspicks.com ストレージ Nginx Java Nginx Java api.newspicks.com Read / Write バックエンドに 非同期タスクを登録 全文検索 重複判定 Java + Spring + Tomcat REST (JSON) API を提供

Slide 26

Slide 26 text

いまどき Java ですか? 最近の Java はかなりモダンになってきているので問題ない ・静的型付けの安心感 ・Java 8 + Lombok の軽快感 @Builder @Value public class SearchCondition { final String query; final Integer offset; final Integer limit; final Order order; } SearchCondition condition = SearchCondition.builder() .query(“uzabase”) .order(Order.DESC) .limit(10) .build(); List picks = service.findByCondition(condition); List pickComments = picks.stream() .map(Pick::getComment) .filter(comment -> !isNullOrEmpty(comment)) .collect(Collectors.toList());

Slide 27

Slide 27 text

バックエンドの構成 SQS Elasticsearch RDS (MySQL) ElastiCache (Redis) Dynamo DB CRON Java 提携サプライヤ/ 独自記事取込 Java ランキング 計算 Java コメント スコア計算 Java 記事スコア計算 カテゴリ分類 Java タイムラインの 生成/伝播 Java 検索 インデックス更新 ストレージ Read / Write 定期実行 更新 SQS Subscribe バックエンドサービス群 他にも様々な サービスが存在します

Slide 28

Slide 28 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue 機械学習 エンジン ユーザー毎の タイムライン

Slide 29

Slide 29 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 機械学習 エンジン ユーザー毎の タイムライン

Slide 30

Slide 30 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 ② Poll 機械学習 エンジン ユーザー毎の タイムライン

Slide 31

Slide 31 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 ③ Enqueue ③ Save ② Poll 機械学習 エンジン ユーザー毎の タイムライン

Slide 32

Slide 32 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 ③ Enqueue ③ Save ④ Subscribe ② Poll 機械学習 エンジン ユーザー毎の タイムライン

Slide 33

Slide 33 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 ③ Enqueue ③ Save ④ Subscribe ⑤ Calculate Score ② Poll 機械学習 エンジン ユーザー毎の タイムライン

Slide 34

Slide 34 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 ③ Enqueue ③ Save ④ Subscribe ⑤ Calculate Score ② Poll 機械学習 エンジン ユーザー毎の タイムライン ⑥ Fetch Data

Slide 35

Slide 35 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 ③ Enqueue ③ Save ⑦ Enqueue ④ Subscribe ⑤ Calculate Score ⑦ Update ② Poll 機械学習 エンジン ユーザー毎の タイムライン ⑥ Fetch Data

Slide 36

Slide 36 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 ③ Enqueue ③ Save ⑦ Enqueue ④ Subscribe ⑤ Calculate Score ⑦ Update ② Poll ⑧ Subscribe 機械学習 エンジン ユーザー毎の タイムライン ⑥ Fetch Data

Slide 37

Slide 37 text

例:記事取込時の処理フローのイメージ Worker が非同期に連携して記事取込 → 分類 → タイムライン伝播 タイムラインは当初 Dynamo DB で実装していたが, パフォーマンスに難があり Redis を利用して push 型のタイムラインを生成することに … ElastiCache (Redis) Dynamo DB Article Feed Service Categorize Service Propagate Service Vowpal Wabbit Scikit Learn etc … Java Java Java CPP Categorize Queue Propagate Queue ① RSS etc の更新 ③ Enqueue ③ Save ⑦ Enqueue ④ Subscribe ⑤ Calculate Score ⑦ Update ② Poll ⑧ Subscribe ⑨ Update 機械学習 エンジン ユーザー毎の タイムライン ⑥ Fetch Data

Slide 38

Slide 38 text

利用目的 ・コストのかかる計算処理を分散 → ピーク時のスループット向上 ・機械学習エンジンなどのバックエンドを分離 → 独立したサービスを育てる 特徴とメリット ・可用性・拡張性が担保された分散キュー ・ふつう MQ を自前で運用しようと思うと結構大変だけど何も考えなくて良い 注意点 ・キューの処理順は担保されていない ・複数回同一のメッセージを Receive することがある → SQS を利用するバックエンドサービスはႈ等に実装すること! SQS (Amazon Simple Queue Service)

Slide 39

Slide 39 text

アジェンダ 自己紹介 サービスの概要 NewsPicks を支える組織 NewsPicks を支える技術 本当にあった怖い話 まとめ

Slide 40

Slide 40 text

(((( ;゚д゚)) 3 ヶ月の振り返りも兼ねて 本当にあった怖い話をします

Slide 41

Slide 41 text

荒ぶる Redis - 迫る X デー 問題 ・オンラインでのタイムライン書き込みが遅延 → タイムラインが更新されない ・夜間バッチでの古いタイムラインの切り詰め処理が遅延 → 深夜アラート → このままユーザーが増えたら死んでしまう … 迫る X デーに震える日々

Slide 42

Slide 42 text

荒ぶる Redis - 迫る X デー 問題 ・オンラインでのタイムライン書き込みが遅延 → タイムラインが更新されない ・夜間バッチでの古いタイムラインの切り詰め処理が遅延 → 深夜アラート → このままユーザーが増えたら死んでしまう … 迫る X デーに震える日々 原因 ・push 型のタイムラインを形成しているため, 大量更新が発生 ・もともとタイムライン用の Redis は 1 台 ・Redis はイベントループモデルなので 1 コアで処理する → CPU 使用率高騰

Slide 43

Slide 43 text

荒ぶる Redis - 迫る X デー 問題 ・オンラインでのタイムライン書き込みが遅延 → タイムラインが更新されない ・夜間バッチでの古いタイムラインの切り詰め処理が遅延 → 深夜アラート → このままユーザーが増えたら死んでしまう … 迫る X デーに震える日々 原因 ・push 型のタイムラインを形成しているため, 大量更新が発生 ・もともとタイムライン用の Redis は 1 台 ・Redis はイベントループモデルなので 1 コアで処理する → CPU 使用率高騰 解決 ・ElastiCache に移行し, SPOF となっていた Redis の台数を増やす ・ユーザーパーティショニングによる垂直分散 ・タイムラインを更新するバックエンドサービスをスケールアウト

Slide 44

Slide 44 text

Redis - タイムラインの垂直分散 BEFORE ELB APP BAT Redis (slave) Redis (master) replication

Slide 45

Slide 45 text

Redis - タイムラインの垂直分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で 全ユーザーの タイムラインを更新 replication

Slide 46

Slide 46 text

Redis - タイムラインの垂直分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で 全ユーザーの タイムラインを更新 replication 自前で レプリケーション & バックアップ

Slide 47

Slide 47 text

Redis - タイムラインの垂直分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で 全ユーザーの タイムラインを更新 replication 自前で レプリケーション & バックアップ BAT サーバーは 1 台で処理

Slide 48

Slide 48 text

Redis - タイムラインの垂直分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で 全ユーザーの タイムラインを更新 replication 自前で レプリケーション & バックアップ AFTER ELB APP BAT slave master replication ElastiCache (Redis) slave master replication ElastiCache (Redis) slave master replication ElastiCache (Redis) USER ID 1-10 万 USER ID 10 -20 万 USER ID 20 -30 万 BAT サーバーは 1 台で処理

Slide 49

Slide 49 text

Redis - タイムラインの垂直分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で 全ユーザーの タイムラインを更新 replication 自前で レプリケーション & バックアップ AFTER ELB APP BAT slave master replication ElastiCache (Redis) slave master replication ElastiCache (Redis) slave master replication ElastiCache (Redis) USER ID 1-10 万 USER ID 10 -20 万 USER ID 20 -30 万 BAT サーバーは 1 台で処理 ElastiCache に移行 ユーザー ID で パーティショニング

Slide 50

Slide 50 text

Redis - タイムラインの垂直分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で 全ユーザーの タイムラインを更新 replication 自前で レプリケーション & バックアップ Redis 分散にあわせて BAT サーバーも 複数台に増やして 高速化を図る AFTER ELB APP BAT slave master replication ElastiCache (Redis) slave master replication ElastiCache (Redis) slave master replication ElastiCache (Redis) USER ID 1-10 万 USER ID 10 -20 万 USER ID 20 -30 万 BAT サーバーは 1 台で処理 ElastiCache に移行 ユーザー ID で パーティショニング

Slide 51

Slide 51 text

荒ぶる Redis - 次なる X デーに備えて 問題 ・タイムライン以外の Redis は 1 台(SPOF) ・ピーク時に Read / Write が 1 台に集中して遅延 → 募る不安 … 次の X デーパーティーの会場はココですか?

Slide 52

Slide 52 text

荒ぶる Redis - 次なる X デーに備えて 問題 ・タイムライン以外の Redis は 1 台(SPOF) ・ピーク時に Read / Write が 1 台に集中して遅延 → 募る不安 … 次の X デーパーティーの会場はココですか? 解決 ・ElastiCache に移行し, SPOF となっていた Redis の台数を増やす ・読み込み処理はリードレプリカに接続して負荷分散 ・アプリ側で問題が起きないような仕組みを導入  → レプリタイミングによって先祖返りしそうなのでスティッキーに  → リードレプリカが落ちた場合に備えてフェイルオーバーするように

Slide 53

Slide 53 text

Redis - Read / Write 水平分散 BEFORE ELB APP BAT Redis (slave) Redis (master) replication 自前で レプリケーション & バックアップ Read / Write Read / Write

Slide 54

Slide 54 text

Redis - Read / Write 水平分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で全ての 書き込み/読み込みを 処理 replication 自前で レプリケーション & バックアップ Read / Write Read / Write

Slide 55

Slide 55 text

Redis - Read / Write 水平分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で全ての 書き込み/読み込みを 処理 replication 自前で レプリケーション & バックアップ Read / Write Read / Write AFTER ELB APP BAT slave master ElastiCache (Redis) Write slave slave replication replication Read Read

Slide 56

Slide 56 text

Redis - Read / Write 水平分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で全ての 書き込み/読み込みを 処理 replication 自前で レプリケーション & バックアップ Read / Write Read / Write AFTER ELB APP BAT slave master ElastiCache (Redis) Write slave slave replication replication Read Read 書き込みは マスターに

Slide 57

Slide 57 text

Redis - Read / Write 水平分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で全ての 書き込み/読み込みを 処理 replication 自前で レプリケーション & バックアップ Read / Write Read / Write AFTER ELB APP BAT slave master ElastiCache (Redis) Write slave slave replication replication Read Read 読み込みは リードレプリカから (スティッキー) 書き込みは マスターに

Slide 58

Slide 58 text

Redis - Read / Write 水平分散 BEFORE ELB APP BAT Redis (slave) Redis (master) 1台で全ての 書き込み/読み込みを 処理 replication 自前で レプリケーション & バックアップ Read / Write Read / Write AFTER ELB APP BAT slave master ElastiCache (Redis) Write slave slave replication replication Read Read 読み込みは リードレプリカから (スティッキー) 書き込みは マスターに リードレプリカが 落ちた場合は フェイルオーバー

Slide 59

Slide 59 text

荒ぶる Phantom - 無数のクロールエラー 問題 ・Phantom JS が暴走して大量のクロールエラーが発生 ・正しくインデックスが作成されず, 検索からユーザーが流入しない → 編集部が良い記事を書いても検索から流入しない → 新規ユーザーを獲得出来ない

Slide 60

Slide 60 text

荒ぶる Phantom - 無数のクロールエラー 問題 ・Phantom JS が暴走して大量のクロールエラーが発生 ・正しくインデックスが作成されず, 検索からユーザーが流入しない → 編集部が良い記事を書いても検索から流入しない → 新規ユーザーを獲得出来ない 原因 ・夏に Web 版をリリースしたが, Angular なので SEO 対策されていなかった ・node + node-phantom でスナップショットを返すようにしたら暴走した

Slide 61

Slide 61 text

荒ぶる Phantom - 無数のクロールエラー 問題 ・Phantom JS が暴走して大量のクロールエラーが発生 ・正しくインデックスが作成されず, 検索からユーザーが流入しない → 編集部が良い記事を書いても検索から流入しない → 新規ユーザーを獲得出来ない 原因 ・夏に Web 版をリリースしたが, Angular なので SEO 対策されていなかった ・node + node-phantom でスナップショットを返すようにしたら暴走した 解決 ・node-phantom の使用を止めた ・child_process で直接 Phantom を呼び出すようにした

Slide 62

Slide 62 text

荒ぶる Phantom - 無数のクロールエラー 激増するクロールエラー BEFORE

Slide 63

Slide 63 text

荒ぶる Phantom - 無数のクロールエラー 激増するクロールエラー BEFORE クロールエラーが減り始める AFTER

Slide 64

Slide 64 text

互換性肥満 - 何でもアリのモデル 問題 ・何のために使用されているのか分からない謎のプロパティが大量に存在 ・同じプロパティでも経路によって全く異なる値が設定される → 修正するのが怖い/すぐデグレする → 開発スピードの低下

Slide 65

Slide 65 text

互換性肥満 - 何でもアリのモデル 問題 ・何のために使用されているのか分からない謎のプロパティが大量に存在 ・同じプロパティでも経路によって全く異なる値が設定される → 修正するのが怖い/すぐデグレする → 開発スピードの低下 原因 ・API の後方互換性を保つために同じモデルがひたすら拡張されていた ・全く異なる API でも同じモデルが使い回されていた ・業務レイヤのモデルが API の I/O と共有されていた

Slide 66

Slide 66 text

互換性肥満 - 何でもアリのモデル 問題 ・何のために使用されているのか分からない謎のプロパティが大量に存在 ・同じプロパティでも経路によって全く異なる値が設定される → 修正するのが怖い/すぐデグレする → 開発スピードの低下 原因 ・API の後方互換性を保つために同じモデルがひたすら拡張されていた ・全く異なる API でも同じモデルが使い回されていた ・業務レイヤのモデルが API の I/O と共有されていた 解決 ・業務レイヤと Web レイヤをきちんと分離 ・カジュアルに API のバージョンアップが出来る仕組みをつくる

Slide 67

Slide 67 text

カジュアルな API のバージョンアップ @Logging @Controller @RequiredArgsConstructor(onConstructor = @_(@Inject)) @RequestMapping("/news") public class NewsController extends ControllerBase { private final NewsFacade facade; @RequestMapping(value = "/{id}/picks", method = GET, produces = ContentTypes.JSON, headers = Headers.API_VERSION_2) @ResponseBody public PageableCollectionDto getPicks( @PathVariable Long id, @ModelAttribute PickSearchParams params) { return getPicks(id, params).map(PickViewDtoV2.mapper()); } @RequestMapping(value = "/{id}/picks", method = GET, produces = ContentTypes.JSON, headers = Headers.API_VERSION_3) @ResponseBody public PageableCollectionDto getPicks( @PathVariable Long id, @ModelAttribute PickSearchParams params) { return getPicks(id, params).map(PickViewDtoV3.mapper()); } private PageableCollectionDto getPicks(Long id, PickSearchParams params) { return facade.getPicks(id, params.getSorting(), params.getPage()); } } /api/v2 はきっと来ない エンドポイント毎に 非互換なバージョンアップを可能にしたい

Slide 68

Slide 68 text

他にも色々ありました ・虫喰いレイヤー:浸食されたドメインモデル ・依存性肥満:ひたすら共有されるコンポーネント etc, etc ... とはいえサービスが急成長する中, 限られたリソースで 全てを理想的にこなすことは難しい … → 攻めと守りのバランス → サービスの成長速度を維持しつつ, 直すべきところは直す

Slide 69

Slide 69 text

アジェンダ 自己紹介 サービスの概要 サービスの特徴 NewsPicks を支える技術 本当にあった怖い話 まとめ

Slide 70

Slide 70 text

まとめ ヒトと技術を融合して良いサービスをつくる ・編集部 + アナリスト + エンジニアの融合で価値をつくる ・エンジニアもビジネスにコミットする 当たり前のことを当たり前にやってカオスに立ち向かう ・攻めと守りのバランスを考えつつ … ・サービスを成長させるために必要なエンジニアリングを

Slide 71

Slide 71 text

今後の構想 CMP(コンテンツマーケティングプラットフォーム) ・記事入稿・バズトラッキング ・アドネットワーク よりユーザーに価値を届けるために ・自社編集部を拡大してコンテンツを強化・クオリティメディアへ ・ヒト + アルゴリズムの強化によって「より良い情報」を届けられるように

Slide 72

Slide 72 text

まだまだカオスの職場です 「世界一の経済メディア」を一緒に作りたい人は是非ご連絡を! 一緒に仕事してくれるエンジニアを募集しています!

Slide 73

Slide 73 text

ありがとうございました