品質と開発速度を
両立させるために
捨てたものと守ったもの
Inside Frontend 2019
Abema Towers, Shibuya 2019.05.18
自己紹介
About US
CyberAgent, Inc.�CATS (CyberAgent Advanced Technology Studio)
Tsuyoshi Wada�Twitter: wadackel�GitHub: tsuyoshiwada
Soichi Masuda�Twitter: masuP9�GitHub: masuP9
はじめに
Introduction
今日お話すること
WinTicket とは
競輪ドメインに求められる特性
競輪場来場者の年齢構成
「競輪場来場者」に対するアンケート調査 平成 23 年度 競輪定点観測調査 / 財団法人JKA 2012を元に桝田が作成
60代
70代
50代
40代
30代
20代
高い利用体験
Performance
Accessibility
Web チームの開発メンバー
リリース前
8人
リリース後
※完全に分業制で、Webフロントエンド、サーバー、iOS、Android、デザインがチームとして分割されている。
5人
初期の開発規模
80〜100 P
想定ページ数
チーム状況
0 人
競輪経験者
求められるもの
ドメイン知識
LIVE 動画
決済
「競輪」
ナニソレ美味しいの?
たしかにレース観れると
嬉しいよナァー...
クレカ、銀行、Apple Pay
使えるんだ...!?
工数見積もり
見積もり12 ヶ月以上
割くことのできるスケジュール
見積もり12 ヶ月以上
4 ヶ月
/(^o^)\
スケジュール
見積もり12 ヶ月以上
4 ヶ月
この差を埋める戦略が必要
8 ヶ月
開発速度と品質の両立を
考える必要があった
ちなみに
最終的な開発期間
開発内外で様々な要因が重なり結果として7ヶ月(2019年4月にリリース)
見積もり12 ヶ月以上
7 ヶ月
最終的な開発規模
TypeScript + CSS
SPA として動作するページ種別
92 P
ページ種別
15 万行
コード量
品質を担保するための戦略
Strategy
Performance
Server Side Rendering (SSR)
メディア特性
アプリケーション特性
Server Side Rendering (SSR)
WinTicket では表示改善を図りメディア特性を最大限活かすための SSR
メディア特性
アプリケーション特性
React
HTTP Framework には Fastify、View Framework には React を採用
Fastify
Fastly を使ったキャッシュの活用
CDN として HTML やアセットの配信を行う。
レスポンス速度向上
オリジンへのリクエスト
ユーザー固有の情報を扱う場合は
プレースホルダーを表示しつつ遅延読み込み
if (req.http.Cookie:token ~ "^.+$") {
declare local var.DecodedPayload STRING;
declare local var.TokenExpiration STRING;
set var.DecodedPayload = digest.base64_decode(regsub(req.http.Cookie:token, "^[^\.]+\.([^\.]+)\.[^\.]+$", "\1"));
set var.TokenExpiration = regsub(var.DecodedPayload, {"^.*?"exp"\s*:\s*(\d+).*?$"}, "\1");
if (time.is_after(std.integer2time(std.atoi(var.TokenExpiration)), now)) {
set req.http.X-Valid-Expiration-Token = "true";
} else {
set req.http.X-Valid-Expiration-Token = "false";
}
} else {
set req.http.X-Valid-Expiration-Token = "false";
}
if (req.http.Cookie:token ~ "^.+$") {
declare local var.DecodedPayload STRING;
declare local var.TokenExpiration STRING;
set var.DecodedPayload = digest.base64_decode(regsub(req.http.Cookie:token, "^[^\.]+\.([^\.]+)\.[^\.]+$", "\1"));
set var.TokenExpiration = regsub(var.DecodedPayload, {"^.*?"exp"\s*:\s*(\d+).*?$"}, "\1");
if (time.is_after(std.integer2time(std.atoi(var.TokenExpiration)), now)) {
set req.http.X-Valid-Expiration-Token = "true";
} else {
set req.http.X-Valid-Expiration-Token = "false";
}
} else {
set req.http.X-Valid-Expiration-Token = "false";
}
その他の Fastly 活用
Performance Budget
SpeedCurve を使った Synthetic Monitoring
設定した Budget 超過したら
Slack へ通知
数値がたまに跳ね上がるとめちゃ焦る
Resource Budget
"resource-budget": [
{
"path": "dist/public/bootstrap.*.js",
"limit": "100KB"
},
{
"path": "dist/public/vendors.*.js",
"limit": "120KB"
},
{
"path": "dist/public/pages-*.js",
"ignore": [
"dist/public/pages-keirin-cups-races-*"
],
"limit": "80KB"
},
{
"path": "dist/public/pages-keirin-cups-races-*.js",
"limit": "125KB"
}
]
Code Splitting & Dynamic Import の活用
Development
型とテストで変更に強いコードベース
TypeScript
Visual Regression Testing
Unit Testing
Visual Regression Testing
1コンポーネントにつき
最大 36 の表示パターンを検証
Protocol Buffers を元にした型定義の使用
全体を通して
あたりまえを積み重ねることを意識
これからの開発 / 改善に耐えうる
安定した開発基盤を整えた
リリースは最優先だが
リリースがゴールではない
Accessbility
他チームとの連携が大事
自分たちの負債は返しやすいが
他チームに返してもらうのは大変
屋外 + 年齢層高め
クッキリ + 大きく
デザイナーとの密なコミュニケーション
プロジェクトの初期から守りたい点を伝える
3 : 1
5.8 : 1
代替テキストの確保
{
"id": "register_campaign",
"link": "https://support.winticket.jp/~~~",
"image": "https://hayabusa.io/winticket/",
"description": "登録完了で全員必ずもらえる!1,000円分ポイントプレゼント"
}
実装方針の策定
Must
Usual
※動画を除く
近日公開
予定
捨てたもの
〜 Performance / Development 編 〜
Compromise
全ページキャッシュ
# `/my` 以下、以外の場合は Cookie を削除
# オリジンが直接ユーザー情報を扱う
if (!req.url ~ "^/my") {
unset req.http.Cookie;
}
慣れたアーキテクチャ以外への挑戦
負債をある程度許容する
ただし、負債は局所化させる
結果
〜 Performance / Development 編 〜
Results
Performance
2019/5/12 20:00 ~ 20:30 頃に計測したトップページの Google Chrome の Audits Simulated Fast 3G, 4x CPU Slowdown スコア
WinTicket
競合O
競合K
競合k
属人性についての結果と反省
属人性を最小化するために
これから
〜 Performance / Development 編 〜
Vision
LightWallet の導入
[
{
"resourceSizes": [
{
"resourceType": "script",
"budget": 300
},
{
"resourceType": "image",
"budget": 100
},
{
"resourceType": "third-party",
"budget": 200
},
{
"resourceType": "total",
"budget": 1000
}
],
"resourceCounts": [
{
"resourceType": "third-party",
"budget": 10
},
{
"resourceType": "total",
"budget": 50
}
]
}
]
Runtime Performance の改善
prefetch / preload の機構を追加
tsc から Babel への移行
捨てたもの
〜 Accessibility 編 〜
Compromise
Must
Usual
※動画を除く
Must
Usual
※動画を除く
要素選択の徹底
レビューにかかるコスト、意思決定にかかるスピードから要素選択の徹底を諦めた
やらないわけではなくて徹底を諦めた
完璧なフォーカスマネジメント
結果
〜 Accessibility 編 〜
Results
70%
コントラスト比 AA 4.5 :1 を達成できているページ
キーカラーを抑えるとカバレッジが高くなる
キーボードで
全部操作できる...ハズ
多重モーダルにも負けなかった
Auditsもハイスコア
93
WinTicket
72
D
60
C
49
K
45
K
37
O
2019/5/12 15:20 ~ 15:30 頃に計測したトップページの Google Chrome の Audits Accessibility / Desktop のスコア
これから
〜 Accessibility 編 〜
Vision
WCAG Test is going on...
品質テストの自動化
Storybook を用いたテストの自動化
Development policy - Accessibility の展開
まとめ
Conclusion
Abraham Lincoln�(1809 - 1865)
If I had six hours to chop down a tree, I'd spend the first four hours sharpening the axe.
“木を切り倒すのに6時間もらえるなら、私は最初の4時間を斧を研ぐことに費やしたい。”
ご清聴ありがとうございました
👋 thanks!