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
460
自分がやった設計反省会 / Evaluation of my Architecture
社内勉強会用
数年前は最適な選択をしたつもりだったが、時が経つにつれツラミが増してきた設計について反省する会。
※ 誰かが悪いとかそういうのではなく、反省して次に活かそうという話です
ダーシノ
July 21, 2022
Tweet
Share
More Decks by ダーシノ
See All by ダーシノ
存在感が薄い?!意外とがんばってるさくらインターネットFEチーム / Frontend Conference 2024
bcrikko
1
3.9k
フロントエンドの複雑さに立ち向かう / Tackling Complexity of Front-end Software with DDD and Clean Architecture
bcrikko
19
15k
社内勉強会やっていきガイド / Tips for Sustainable Study Groups
bcrikko
4
2k
加速するコンポーネント設計入門 / Component Design as an Accelerator
bcrikko
9
6.6k
コンポーネント指向時代のmargin戦略 / Rethinking the relationship between Components and Margins
bcrikko
1
1.2k
知ってトクするDevToolsの使い方 / DevTools Tips you should know
bcrikko
1
530
伝わるバグ報告 / How to write a better bug report
bcrikko
2
670
Sassの新しいモジュールシステム / Introducing New Sass Module System
bcrikko
0
650
決断力を消耗しないSass開発環境構築 / Set up Sass development environment
bcrikko
0
540
Other Decks in Programming
See All in Programming
設計の本質:コード、システム、そして組織へ / The Essence of Design: To Code, Systems, and Organizations
nrslib
10
3.7k
ウォンテッドリーの「ココロオドル」モバイル開発 / Wantedly's "kokoro odoru" mobile development
kubode
1
270
ASP.NETアプリケーションのモダナイゼーションについて
tomokusaba
0
260
カウシェで Four Keys の改善を試みた理由
ike002jp
1
130
LRパーサーはいいぞ
ydah
6
1.2k
Boost Your Performance and Developer Productivity with Jakarta EE 11
ivargrimstad
0
820
エンジニア向けCursor勉強会 @ SmartHR
yukisnow1823
3
12k
読書シェア会 vol.4 『ダイナミックリチーミング 第2版』
kotaro666
0
110
SwiftDataのカスタムデータストアを試してみた
1mash0
0
150
flutter_kaigi_mini_4.pdf
nobu74658
0
150
Beyond_the_Prompt__Evaluating__Testing__and_Securing_LLM_Applications.pdf
meteatamel
0
110
KANNA Android の技術的課題と取り組み
watabee
0
200
Featured
See All Featured
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
Become a Pro
speakerdeck
PRO
28
5.3k
Making Projects Easy
brettharned
116
6.2k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
2.9k
Embracing the Ebb and Flow
colly
85
4.7k
Build your cross-platform service in a week with App Engine
jlugia
230
18k
The Art of Programming - Codeland 2020
erikaheidi
54
13k
Adopting Sorbet at Scale
ufuk
76
9.3k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Site-Speed That Sticks
csswizardry
6
540
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
105
19k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
19
1.2k
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%くらいだよな、と。