Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
自分でやった設計反省会 その節は、誠に申し訳ございませんでした。 ダーシノ / @bc_rikko
Slide 2
Slide 2 text
祇 園 精 舎 の 鐘 の 声 諸 行 無 常 の 響 き あ り 娑 羅 双 樹 の 花 の 色 盛 者 必 衰 の 理 を あ ら は す
Slide 3
Slide 3 text
失敗を共有する理由 成功パターンはさまざまな要因が重なり再現性が低い 失敗パターンはだいたい似ていて再現性が高い
Slide 4
Slide 4 text
目次 TypeScript/JavaScript Enumの濫用 型定義の誤った共通化 大きすぎるライブラリ(lodash)の導入 フレームワーク 中途半端なコンポーネント化 SFC内はJavaScriptで書く
Slide 5
Slide 5 text
TypeScript/JavaScript
Slide 6
Slide 6 text
Enumの濫用
Slide 7
Slide 7 text
当時の背景 コード内にマジックナンバーや文字列リテラルがあった JavaScript → TypeScript の移行期だった if文、switch文などでtypoに気づけない 定数クラスの代わりにString Enumsを採用した
Slide 8
Slide 8 text
何が悪かったのか 定数を扱うために String Enums を採用 冗長的で Enum 本来の使い方ではなかった Enum名がつくためコードが長くなった enum MyStatus { Waiting = 'waiting', Running = 'running', Finished = 'finished' } if (res.data.status === MyStatus.Running) { // ... }
Slide 9
Slide 9 text
どうすればよかったのか Literal Union Types を使う マジックナンバーなど意味が伝わらりづらいときだけ Enum を使う as const でもOK // Literal Union Types type MyStatus = 'waiting' | 'running' | 'finished'; if (status === 'running') { /***/ } // String Enums enum StatusCode { OK = 200, Forbidden = 403, NotFound = 404 } if (res.code === StatusCode.NotFound) { /***/ }
Slide 10
Slide 10 text
型定義の誤った共通化
Slide 11
Slide 11 text
当時の背景 JavaScriptで実装されたプロジェクトをTypeScriptに移行しようとして いた APIレスポンスの型定義を作りたかった ID , Name , Description , ... などはほとんど同じだった コード量を減らすために共通化した
Slide 12
Slide 12 text
何が悪かったのか APIの仕様上、エンドポイントによりスキーマが変わる ひとつにまとめたため、 Optional Properties が大量発生した interface CommonResponse { id: string scope?: 'public' | 'private' // GET /item のときだけ有効 status?: { availability: string } // GET /status のときだけ有効 } const res: CommonResponse = await fetch(`/item/${id}`) console.log(`scope: ${res.scope}`) // scope: public // Cannot read properties of undefined if (res.status.availability === 'xxx') { /** */ } if (res.status?.availability === 'xxx') { /** */ } // Optional Chaining が必要
Slide 13
Slide 13 text
どうすればよかったのか 共通化するときは本当に同じモノなのか検討する Union Types を使う( Tagged Union Types も有効) type ItemResponse = { id: string scope: 'public' | 'private' } type StatusResponse = { id: string status: { availability: MyAvailable } } const res = await fetch(`/status/${id}`) if (res.status.availability === 'available') { /** */ }
Slide 14
Slide 14 text
大きすぎるライブラリの導入
Slide 15
Slide 15 text
当時の背景 もともと使われていた Prototype.js の代用を探していた IE11やSafariの一部ブラウザで使えないコレクション操作メソッドがあった babel導入前でpolyfillという選択肢がなかった polyfillではなくlodashの導入を選択した
Slide 16
Slide 16 text
何が悪かったのか 大きすぎてバンドルサイズがモリモリ増えた 今となっては lodash がなくても実装できるようになった 脆弱性報告がときどき来た import * as _ from 'lodash' _.includes(list, 'xxx') _.chain(list).map(...).flat(...).filter(...).values() _.get(item, 'a.b.c')
Slide 17
Slide 17 text
どうすればよかったのか 「あると便利だけどなくてもなんとかなる」ライブラリは徹底排除 標準仕様を優先する 過去のコードの真似をせず、最新の仕様で考える list.includes('xxx') list.map(...).flat(2).filter(...) item.a?.b?.c
Slide 18
Slide 18 text
フレームワーク
Slide 19
Slide 19 text
中途半端なコンポーネント化
Slide 20
Slide 20 text
当時の背景 Atomic Design の厳密性より、スピードと柔軟性を求めていた コンポーネント粒度が細かいほどバケツリレーになりがちだった よく使うコンポーネント以外は各ページで実装する
Slide 21
Slide 21 text
何が悪かったのか コンポーネント化する基準が曖昧なままチームメンバーが増えた 同じスタイルを異なるページで使うようになった
...
Lorem ipsum dolor sit amet
consectetur
.mute { /***/ } ...
consectetur adipiscing elit
sed do eiusmod
/** Foo.vueの .mute と同じスタイル */ .mute { /***/ }
Slide 22
Slide 22 text
どうすればよかったのか 明確なガイドラインを用意する Atomic Design を採用する痛みはあれどプロジェクトは安定化する Atomic Design でなくてもチーム全体で合意がとれたガイドラインを用意す る ... Lorem ipsum dolor sit amet ... ... consectetur adipiscing elit ...
Slide 23
Slide 23 text
VueのSFCはJavaScriptで書く
Slide 24
Slide 24 text
当時の背景 Nuxt@1 系(
[email protected]
)の頃は、TypeScritpのサポートが十分でなかった Class Components 化するよりはJavaScriptで書いたほうが悩みが少な いと思った JSDoc である程度型をつけられると思った SFC内はJavaScriptで実装した
Slide 25
Slide 25 text
何が悪かったのか Store 層はTypeScript化して dispatchs / getters にも型をつけた mapヘルパー や computed を使うことで Store 層の型が全部死んだ JSDoc を書かなかった export default { computed: { items() { return this.$store.getters['items'] || [] } }, methods: { doSomething() { const items = this.items // any } } }
Slide 26
Slide 26 text
どうすればよかったのか TypeScriptのサポートが充実してきたころにJS→TSに移行する mapヘルパー の利用や getters を computed でラップしない export default Vue.extend({ methods: { doSomething(): Promise { const items = this.$store.getters['items'] // ^? Item[] } } })
Slide 27
Slide 27 text
まとめポエム 誰も悪くない。 「最良の方法」だと信じていたし、実際そうだった。 でも、時間が経つにつれ痛みが、 It's so painful. そして、過去の自分の設計を振り返ってこう思う。 お前のいう「絶対」って60%くらいだよな、と。