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
430
自分がやった設計反省会 / Evaluation of my Architecture
社内勉強会用
数年前は最適な選択をしたつもりだったが、時が経つにつれツラミが増してきた設計について反省する会。
※ 誰かが悪いとかそういうのではなく、反省して次に活かそうという話です
ダーシノ
July 21, 2022
Tweet
Share
More Decks by ダーシノ
See All by ダーシノ
存在感が薄い?!意外とがんばってるさくらインターネットFEチーム / Frontend Conference 2024
bcrikko
1
3.7k
フロントエンドの複雑さに立ち向かう / Tackling Complexity of Front-end Software with DDD and Clean Architecture
bcrikko
19
13k
社内勉強会やっていきガイド / Tips for Sustainable Study Groups
bcrikko
4
2k
加速するコンポーネント設計入門 / Component Design as an Accelerator
bcrikko
9
6.4k
コンポーネント指向時代のmargin戦略 / Rethinking the relationship between Components and Margins
bcrikko
1
1.1k
知ってトクするDevToolsの使い方 / DevTools Tips you should know
bcrikko
1
510
伝わるバグ報告 / How to write a better bug report
bcrikko
2
650
Sassの新しいモジュールシステム / Introducing New Sass Module System
bcrikko
0
630
決断力を消耗しないSass開発環境構築 / Set up Sass development environment
bcrikko
0
520
Other Decks in Programming
See All in Programming
Immutable ActiveRecord
megane42
0
130
Formの複雑さに立ち向かう
bmthd
1
720
Java Webフレームワークの現状 / java web framework at burikaigi
kishida
9
2.2k
SpringBoot3.4の構造化ログ #kanjava
irof
2
970
チームリードになって変わったこと
isaka1022
0
190
第3回 Snowflake 中部ユーザ会- dbt × Snowflake ハンズオン
hoto17296
4
360
Flutter × Firebase Genkit で加速する生成 AI アプリ開発
coborinai
0
150
2024年のkintone API振り返りと2025年 / kintone API look back in 2024
tasshi
0
210
技術を根付かせる / How to make technology take root
kubode
1
240
“あなた” の開発を支援する AI エージェント Bedrock Engineer / introducing-bedrock-engineer
gawa
11
1.8k
一休.com のログイン体験を支える技術 〜Web Components x Vue.js 活用事例と最適化について〜
atsumim
0
110
Amazon ECS とマイクロサービスから考えるシステム構成
hiyanger
2
490
Featured
See All Featured
Producing Creativity
orderedlist
PRO
343
39k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
132
33k
GitHub's CSS Performance
jonrohan
1030
460k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
31
2.1k
Building an army of robots
kneath
302
45k
Unsuck your backbone
ammeep
669
57k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.4k
We Have a Design System, Now What?
morganepeng
51
7.4k
Typedesign – Prime Four
hannesfritz
40
2.5k
Documentation Writing (for coders)
carmenintech
67
4.6k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
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%くらいだよな、と。