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
自分がやった設計反省会 / Evaluation of my Architecture
Search
ダーシノ
July 21, 2022
Programming
0
370
自分がやった設計反省会 / Evaluation of my Architecture
社内勉強会用
数年前は最適な選択をしたつもりだったが、時が経つにつれツラミが増してきた設計について反省する会。
※ 誰かが悪いとかそういうのではなく、反省して次に活かそうという話です
ダーシノ
July 21, 2022
Tweet
Share
More Decks by ダーシノ
See All by ダーシノ
存在感が薄い?!意外とがんばってるさくらインターネットFEチーム / Frontend Conference 2024
bcrikko
1
3k
フロントエンドの複雑さに立ち向かう / Tackling Complexity of Front-end Software with DDD and Clean Architecture
bcrikko
18
11k
社内勉強会やっていきガイド / Tips for Sustainable Study Groups
bcrikko
4
1.9k
加速するコンポーネント設計入門 / Component Design as an Accelerator
bcrikko
9
6.1k
コンポーネント指向時代のmargin戦略 / Rethinking the relationship between Components and Margins
bcrikko
1
990
知ってトクするDevToolsの使い方 / DevTools Tips you should know
bcrikko
1
460
伝わるバグ報告 / How to write a better bug report
bcrikko
2
610
Sassの新しいモジュールシステム / Introducing New Sass Module System
bcrikko
0
580
決断力を消耗しないSass開発環境構築 / Set up Sass development environment
bcrikko
0
480
Other Decks in Programming
See All in Programming
全部見せます! クラシルリワードのSwiftTesting移行プロジェクト
uetyo
0
200
Lessons by WebAssembly app in production on CDN Edge Computing Service
tetsuharuohzeki
0
200
AndroidアプリのUIバリエーションをあの手この手で確認する / Check UI variations of Android apps by various means
tkmnzm
1
130
Prolog入門
qnighy
4
1k
Android開発以外のAndroid開発経験の活かしどころ
konifar
2
840
Scala アプリケーションのビルドを改善してデプロイ時間を 1/4 にした話 | How I improved the build of my Scala application and reduced deployment time by 4x
nomadblacky
1
170
実践!難読化ガイド
mitchan
0
140
Modular Monolith Go Server with GraphQL Federation + gRPC
110y
1
580
労務ドメインを快適に開発する方法 / How to Comfortably Develop in the Labor Domain
yuki21
1
250
私のEbitengineの第一歩
qt_luigi
0
440
2024 컴포즈 정원사
jisungbin
0
150
Rustではじめる負荷試験
skanehira
5
1.2k
Featured
See All Featured
It's Worth the Effort
3n
182
27k
Robots, Beer and Maslow
schacon
PRO
157
8.2k
Visualization
eitanlees
142
15k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
157
15k
Adopting Sorbet at Scale
ufuk
73
8.9k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
23
1.7k
Building a Scalable Design System with Sketch
lauravandoore
458
32k
Done Done
chrislema
180
16k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
36
1.7k
ParisWeb 2013: Learning to Love: Crash Course in Emotional UX Design
dotmariusz
109
6.9k
Intergalactic Javascript Robots from Outer Space
tanoku
268
26k
Become a Pro
speakerdeck
PRO
22
4.9k
Transcript
自分でやった設計反省会 その節は、誠に申し訳ございませんでした。 ダーシノ / @bc_rikko
祇 園 精 舎 の 鐘 の 声 諸
行 無 常 の 響 き あ り 娑 羅 双 樹 の 花 の 色 盛 者 必 衰 の 理 を あ ら は す
失敗を共有する理由 成功パターンはさまざまな要因が重なり再現性が低い 失敗パターンはだいたい似ていて再現性が高い
目次 TypeScript/JavaScript Enumの濫用 型定義の誤った共通化 大きすぎるライブラリ(lodash)の導入 フレームワーク 中途半端なコンポーネント化 SFC内はJavaScriptで書く
TypeScript/JavaScript
Enumの濫用
当時の背景 コード内にマジックナンバーや文字列リテラルがあった JavaScript → TypeScript の移行期だった if文、switch文などでtypoに気づけない 定数クラスの代わりにString Enumsを採用した
何が悪かったのか 定数を扱うために String Enums を採用 冗長的で Enum 本来の使い方ではなかった Enum名がつくためコードが長くなった enum
MyStatus { Waiting = 'waiting', Running = 'running', Finished = 'finished' } if (res.data.status === MyStatus.Running) { // ... }
どうすればよかったのか 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) { /***/ }
型定義の誤った共通化
当時の背景 JavaScriptで実装されたプロジェクトをTypeScriptに移行しようとして いた APIレスポンスの型定義を作りたかった ID , Name , Description ,
... などはほとんど同じだった コード量を減らすために共通化した
何が悪かったのか 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 が必要
どうすればよかったのか 共通化するときは本当に同じモノなのか検討する Union Types を使う( Tagged Union Types も有効) type
ItemResponse = { id: string scope: 'public' | 'private' } type StatusResponse = { id: string status: { availability: MyAvailable } } const res = await fetch<StatusResponse>(`/status/${id}`) if (res.status.availability === 'available') { /** */ }
大きすぎるライブラリの導入
当時の背景 もともと使われていた Prototype.js の代用を探していた IE11やSafariの一部ブラウザで使えないコレクション操作メソッドがあった babel導入前でpolyfillという選択肢がなかった polyfillではなくlodashの導入を選択した
何が悪かったのか 大きすぎてバンドルサイズがモリモリ増えた 今となっては lodash がなくても実装できるようになった 脆弱性報告がときどき来た import * as _
from 'lodash' _.includes(list, 'xxx') _.chain(list).map(...).flat(...).filter(...).values() _.get(item, 'a.b.c')
どうすればよかったのか 「あると便利だけどなくてもなんとかなる」ライブラリは徹底排除 標準仕様を優先する 過去のコードの真似をせず、最新の仕様で考える list.includes('xxx') list.map(...).flat(2).filter(...) item.a?.b?.c
フレームワーク
中途半端なコンポーネント化
当時の背景 Atomic Design の厳密性より、スピードと柔軟性を求めていた コンポーネント粒度が細かいほどバケツリレーになりがちだった よく使うコンポーネント以外は各ページで実装する
何が悪かったのか コンポーネント化する基準が曖昧なままチームメンバーが増えた 同じスタイルを異なるページで使うようになった <!-- Foo.vue --> <div class="item">...</div> <p> Lorem
ipsum dolor sit amet <span class="mute">consectetur</span> </p> <style> .mute { /***/ } </style> <!-- Bar.vue --> <header class="header">...</header> <p> consectetur adipiscing elit <span class="mute">sed do eiusmod</span> </p> <style> /** Foo.vueの .mute と同じスタイル */ .mute { /***/ } </style>
どうすればよかったのか 明確なガイドラインを用意する Atomic Design を採用する痛みはあれどプロジェクトは安定化する Atomic Design でなくてもチーム全体で合意がとれたガイドラインを用意す る <!--
Foo.vue --> <Item>...</Item> <Message> Lorem ipsum dolor sit amet <MuteText class="mute">...</MuteText> </Message> <!-- Bar.vue --> <Header>...</Header> <Message> consectetur adipiscing elit <MuteText class="mute">...</MuteText> </Message>
VueのSFCはJavaScriptで書く
当時の背景 Nuxt@1 系(
[email protected]
)の頃は、TypeScritpのサポートが十分でなかった Class Components 化するよりはJavaScriptで書いたほうが悩みが少な いと思った JSDoc
である程度型をつけられると思った SFC内はJavaScriptで実装した
何が悪かったのか Store 層はTypeScript化して dispatchs / getters にも型をつけた mapヘルパー や computed
を使うことで Store 層の型が全部死んだ JSDoc を書かなかった export default { computed: { items() { return this.$store.getters['items'] || [] } }, methods: { doSomething() { const items = this.items // any } } }
どうすればよかったのか TypeScriptのサポートが充実してきたころにJS→TSに移行する mapヘルパー の利用や getters を computed でラップしない export default
Vue.extend({ methods: { doSomething(): Promise<void> { const items = this.$store.getters['items'] // ^? Item[] } } })
まとめポエム 誰も悪くない。 「最良の方法」だと信じていたし、実際そうだった。 でも、時間が経つにつれ痛みが、 It's so painful. そして、過去の自分の設計を振り返ってこう思う。 お前のいう「絶対」って60%くらいだよな、と。