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
型だけでバグを減らそう! Kotlinの型パワーを使った実践タイプセーフエンジニ...
Search
YuitoSato
December 10, 2022
Technology
2
1.5k
型だけでバグを減らそう! Kotlinの型パワーを使った実践タイプセーフエンジニアリング
Kotlin Fest 2022の登壇内容のスライドです。
YuitoSato
December 10, 2022
Tweet
Share
More Decks by YuitoSato
See All by YuitoSato
Railway Oriented Programming を オニオンアーキテクチャに適用する by kotlin-result / Railway Oriented Programming in Onion Architecture by kotlin-result
yuitosato
3
560
リファクタリングへの耐性が高いモデルベースの統合テストの紹介 / Model-Base Integration Test for Refactoring
yuitosato
7
2.3k
Expressing Business Logic with Types: Functional DDD for OOP
yuitosato
1
79
ビジネスロジックを「型」で表現するOOPのための関数型DDD / Functional And Type-Safe DDD for OOP
yuitosato
41
26k
Java21とKotlinの代数的データ型 & パターンマッチの紹介と本当に嬉しい使い方 / Algebraic Data Type in Java and Kotlin: Happy Use of Pattern Match
yuitosato
14
4.4k
ログラスの継続的ライブラリアップデートのWhyとHow / Why and How to Update Libraries Continuously in Loglass
yuitosato
0
380
リプレイス「後」が大事!Reactフルリプレイスから2年で良かったこと・その後大事なこと / The Important Point After The Framework Replacement
yuitosato
3
980
B2B SaaSあるある! 一括処理のエラーハンドリングをKotlinで関数型的に処理する / Kotlin Functional Multi Error Handling
yuitosato
2
480
Kotlinサーバサイドで頑張る「単体テストの考え方_使い方」 / "Unit Testing Principles, Practices, and Patterns" by Kotlin
yuitosato
3
920
Other Decks in Technology
See All in Technology
DMMブックスへのTipKit導入
ttyi2
1
110
Alignment and Autonomy in Cybozu - 300人の開発組織でアラインメントと自律性を両立させるアジャイルな組織運営 / RSGT2025
ama_ch
1
2.4k
PaaSの歴史と、 アプリケーションプラットフォームのこれから
jacopen
7
1.5k
東京Ruby会議12 Ruby と Rust と私 / Tokyo RubyKaigi 12 Ruby, Rust and me
eagletmt
3
870
AWSの生成AIサービス Amazon Bedrock入門!(2025年1月版)
minorun365
PRO
7
470
Goで実践するBFP
hiroyaterui
1
120
AIアプリケーション開発でAzure AI Searchを使いこなすためには
isidaitc
0
100
Formal Development of Operating Systems in Rust
riru
1
420
.NET 最新アップデート ~ AI とクラウド時代のアプリモダナイゼーション
chack411
0
200
Docker Desktop で Docker を始めよう
zembutsu
PRO
0
170
自社 200 記事を元に整理した読みやすいテックブログを書くための Tips 集
masakihirose
2
330
生成AIのビジネス活用
seosoft
0
110
Featured
See All Featured
Side Projects
sachag
452
42k
Typedesign – Prime Four
hannesfritz
40
2.5k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
230
52k
Designing for Performance
lara
604
68k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
3
180
Product Roadmaps are Hard
iamctodd
PRO
50
11k
How STYLIGHT went responsive
nonsquared
96
5.3k
The Power of CSS Pseudo Elements
geoffreycrofte
74
5.4k
4 Signs Your Business is Dying
shpigford
182
22k
GraphQLの誤解/rethinking-graphql
sonatard
68
10k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
3
240
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
7
570
Transcript
1 ©2022 Loglass Inc. 型だけでバグを減らそう! Kotlinの型パワーを使った実践タイプセーフ エンジニアリング 2022.12.10 佐藤有斗(Yuiiitoto) 株式会社ログラス
2 ©2022 Loglass Inc. 自己紹介 佐藤有斗(Yuiiitoto) ログラス エンジニア # React
# 組織・アジャイル # Kotlin
3 ©2022 Loglass Inc. ログラスについて(5秒) 企業価値を向上する 経営管理クラウド
4 ©2022 Loglass Inc. 目次 • なぜ型を使いこなすことでバグが減るのか? • 型によってバグが減らせるパターン 1.
標準の型をラップする 2. 認可処理などの特定の処理をパスしたことを型で示す 3. 型でデータの不整合をなくす • まとめ • 最後に: タイプセーフなポストモーテム
5 ©2022 Loglass Inc. 目次 • なぜ型を使いこなすことでバグが減るのか? • 型によってバグが減らせるパターン 1.
標準の型をラップする 2. 認可処理などの特定の処理をパスしたことを型で示す 3. 型でデータの不整合をなくす • まとめ • 最後に: タイプセーフなポストモーテム
6 ©2022 Loglass Inc. なぜ型を使いこなすことでバグが減るのか コンパイル時に実装ミスに気付けるから
7 ©2022 Loglass Inc. コンパイル時に実装ミスに気づくとは?
8 ©2022 Loglass Inc. ミドルネームにnullを渡す
9 ©2022 Loglass Inc. ぬるぽ、ガッ! Λ_Λ \\ ( ・∀・) | | ガッ と )
| | Y /ノ 人 / ) < >_Λ∩ _/し' //. V`Д´)/ (_フ彡 / ←>>1 NullPointerExceptionが起きる
10 ©2022 Loglass Inc. と思いきやKotlinはコンパイルで落ちる
11 ©2022 Loglass Inc. Kotlinではデフォルトで 引数や変数にnullを渡すことができない KotlinのNull Safety
12 ©2022 Loglass Inc. Kotlinではデフォルトで 引数や変数にnullを渡すことができない KotlinのNull Safety
13 ©2022 Loglass Inc. 実装ミスをコンパイル時に気づけると何がいいのか? • 実行時に気付くバグと比較して ... ◦ 全ての分岐を実行しなくてもよく
必ず実装ミスに気づくことができる ◦ テストカバレッジ100%は現実的ではない
14 ©2022 Loglass Inc. では、なぜKotlin? • Kotlinは型に関する機能が豊富 ◦ Generics ◦
型推論 ◦ Smart Cast ◦ 代数的データ型 ◦ 高階関数やラムダ ◦ Delegation などなど
15 ©2022 Loglass Inc. 要するに、、 Kotlinを駆使して、実装ミスをコンパイルで 気付けるようにしよう!!!
16 ©2022 Loglass Inc. 型によってバグが減らせるパターン • なぜ型を使いこなすことでバグが減るのか? • 型によってバグが減らせるパターン 1.
標準の型をラップする 2. 認可処理などの特定の処理をパスしたことを型で示す 3. 型でデータの不整合をなくす • まとめ • 最後に: タイプセーフなポストモーテム
17 ©2022 Loglass Inc. 標準の型をラップする • なぜ型を使いこなすことでバグが減るのか? • 型によってバグが減らせるパターン 1.
標準の型をラップする 2. 認可処理などの特定の処理をパスしたことを型で示す 3. 型でデータの不整合をなくす • まとめ • 最後に: タイプセーフなポストモーテム
18 ©2022 Loglass Inc. 標準の型をラップする • よくあるミス: 引数のIDを取り間違えてしまった
19 ©2022 Loglass Inc. 標準の型をラップする • よくあるミス: 引数のIDを取り間違えてしまった
20 ©2022 Loglass Inc. Stringをラップする
21 ©2022 Loglass Inc. TaskIdとUserIdを区別することでコンパイル時に実装ミスに気付ける
22 ©2022 Loglass Inc. TaskIdとUserIdを区別することでコンパイル時に実装ミスに気付ける
23 ©2022 Loglass Inc. ちなみに: ラップすることによるオーバーヘッドを避ける • Stringをクラスでラップするとヒープ領域のオーバーヘッドが増える • inline
classesという機能を使うことで String型として扱えてスタック領域だけの割り当てで済む
24 ©2022 Loglass Inc. ここで一つ疑問が 全てのIDに別々の型をつけるの面倒 ではないか?
25 ©2022 Loglass Inc. タイプセーフにしたいが、型の増加を抑制したい => Genericsを使おう! Genericsでさらに汎用的にする
26 ©2022 Loglass Inc. Genericsでさらに汎用的にする
27 ©2022 Loglass Inc. Genericsでさらに汎用的にする
28 ©2022 Loglass Inc. Genericsでさらに汎用的にする • 型を増やさずに、タスク IDとユーザーIDを区別できた!
29 ©2022 Loglass Inc. Genericsでさらに汎用的にする • 型を増やさずに、タスク IDとユーザーIDを区別できた!
30 ©2022 Loglass Inc. Genericsでさらに汎用的にする • 型を増やさずに、タスク IDとユーザーIDを区別できた!
31 ©2022 Loglass Inc. Genericsでさらに汎用的にする • 型を増やさずに、タスク IDとユーザーIDを区別できた!
32 ©2022 Loglass Inc. Genericsでさらに汎用的にする • 型を増やさずに、タスク IDとユーザーIDを区別できた!
33 ©2022 Loglass Inc. 標準の型をラップする : まとめ 標準の型をラップして取り間違いを防ぐ 型の増加をGenericsで抑制する
34 ©2022 Loglass Inc. 認可処理などの特定の処理をパスしたことを型で示す • なぜ型を使いこなすことでバグが減るのか? • 型によってバグが減らせるパターン 1.
標準の型をラップする 2. 認可処理などの特定の処理をパスしたことを型で示す 3. 型でデータの不整合をなくす • まとめ • 最後に: タイプセーフなポストモーテム
35 ©2022 Loglass Inc. 認可処理などの特定の処理をパスしたことを型で示す • よくあるミス: 認可されていないIDで参照をしてしまった
36 ©2022 Loglass Inc. 認可処理などの特定の処理をパスしたことを型で示す • よくあるミス: 認可されていないIDで参照をしてしまった
37 ©2022 Loglass Inc. 認可処理などの特定の処理をパスしたことを型で示す • よくあるミス: 認可されていないIDで参照をしてしまった
38 ©2022 Loglass Inc. 「認可された」ID型を導入する • 認可されたID型として AuthorizedTaskId を定義する
39 ©2022 Loglass Inc. 「認可された」ID型を導入する • TaskAuthChecker だけが返す特別な型とする
40 ©2022 Loglass Inc. 「認可された」ID型を導入する • TaskAuthChecker だけが返す特別な型とする
41 ©2022 Loglass Inc. 「認可された」ID型を導入する • findTaskByIdはAuthorizedTaskIdしか受け付けないように修正
42 ©2022 Loglass Inc. 「認可された」ID型を導入する • findTaskByIdはAuthorizedTaskIdしか受け付けないように修正
43 ©2022 Loglass Inc. 「認可された」ID型を導入する • 認可処理を通過していない IDはコンパイル時に落ちるようになる
44 ©2022 Loglass Inc. 「認可された」ID型を導入する • 認可処理を通過していない IDはコンパイル時に落ちるようになる
45 ©2022 Loglass Inc. 「認可された」ID型を導入する • 認可処理を通過していない IDはコンパイル時に落ちるようになる
46 ©2022 Loglass Inc. 「認可された」ID型を導入する • 認可処理を通過していない IDはコンパイル時に落ちるようになる
47 ©2022 Loglass Inc. しかし油断してはいけない • AuthorizedTaskIdのコンストラクタが公開されている! ここに public が潜んでいる
48 ©2022 Loglass Inc. しかし油断してはいけない • AuthorizedTaskIdのコンストラクタが公開されている!
49 ©2022 Loglass Inc. しかし油断してはいけない • 不正に認可されていない AuthorizedTaskIdが作成されてしまう
50 ©2022 Loglass Inc. しかし油断してはいけない • 不正に認可されていない AuthorizedTaskIdが作成されてしまう
51 ©2022 Loglass Inc. 不正にインスタンスを作成されないためには? data classを使ってコンストラクタの可視性を コントロールする
52 ©2022 Loglass Inc. コンストラクタの可視性を data classで制御する
53 ©2022 Loglass Inc. コンストラクタの可視性を data classで制御する • インスタンスを作成できない interface
AuthorizedTaskIdを定義する sealed を使えば同ファイルからしか継承できない
54 ©2022 Loglass Inc. コンストラクタの可視性を data classで制御する • コンストラクタ機能だけの data
classをprivateで定義する private classは同ファイルからしか参照できない
55 ©2022 Loglass Inc. コンストラクタの可視性を data classで制御する • TaskAuthCheckerからのみprivate data
classにアクセスする
56 ©2022 Loglass Inc. コンストラクタの可視性を data classで制御する • 別ファイルから AuthorizedTaskId
インスタンスが生成できなくなった!
57 ©2022 Loglass Inc. コンストラクタの可視性を data classで制御する • 別ファイルから AuthorizedTaskId
インスタンスが生成できなくなった!
58 ©2022 Loglass Inc. コンストラクタの可視性を data classで制御する • 別ファイルから AuthorizedTaskId
インスタンスが生成できなくなった!
59 ©2022 Loglass Inc. コンストラクタをprivateにするのではダメ? • この場合のprivateは同じクラス ファイル のみアクセス可能という意味なので NG
60 ©2022 Loglass Inc. 認可処理などの特定の処理をパスしたことを型で示す : まとめ 処理が通過したことを型で示す コンストラクタを非公開にして不正にインスタンスを作らせない
61 ©2022 Loglass Inc. 型でデータの不整合をなくす • なぜ型を使いこなすことでバグが減るのか? • 型によってバグが減らせるパターン 1.
標準の型をラップする 2. 認可処理などの特定の処理をパスしたことを型で示す 3. 型でデータの不整合をなくす • まとめ • 最後に: タイプセーフなポストモーテム
62 ©2022 Loglass Inc. 型でデータの不整合をなくす • よくあるミス: あり得ないデータを作成してしまった
63 ©2022 Loglass Inc. 型でデータの不整合をなくす • タスクには以下のステータスがある • 完了ステータスのときのみ完了時刻を持つ
64 ©2022 Loglass Inc. 型でデータの不整合をなくす • しかし間違えて着手時に完了時刻を入れてしまった • →着手中なのに完了時刻を持つという不整合データが誕生
65 ©2022 Loglass Inc. 型でデータの不整合をなくす • しかし間違えて着手時に完了時刻を入れてしまった • →着手中なのに完了時刻を持つという不整合データが誕生
66 ©2022 Loglass Inc. データ不整合をなくすには? sealed classを使って データの不整合が起こりえない型を定義する
67 ©2022 Loglass Inc. sealed class を導入する • 完了の時のみ完了時刻をもつというデータ構造を sealed
classで定義
68 ©2022 Loglass Inc. sealed class を導入する • sealed class
+ object, data classのパターンはEnumのように扱える
69 ©2022 Loglass Inc. sealed class を導入する • Enumだとこうなる •
objectはEnumとほぼ同じ扱いで、data classはEnum + 構造体というような振る舞い
70 ©2022 Loglass Inc. sealed class を導入する • Task型からnullableなcompletedAtプロパティが消える
71 ©2022 Loglass Inc. sealed class を導入する • 着手中時に完了時刻を渡せなくなった
72 ©2022 Loglass Inc. sealed class を導入する • 着手中時に完了時刻を渡せなくなった
73 ©2022 Loglass Inc. sealed class を導入する • 完了時には完了時刻の入力を強制できる
74 ©2022 Loglass Inc. sealed class を導入する • 完了時には完了時刻の入力を強制できる
75 ©2022 Loglass Inc. OSS: kotlin-resultの事例 • 成功値かエラー値かどちらかの値をとる Result型を提供するOSS kotlin-result
: https://github.com/michaelbull/kotlin-result
76 ©2022 Loglass Inc. OSS: kotlin-resultの事例 • 成功値かエラー値かどちらかの値をとる Result型を提供するOSS kotlin-result
: https://github.com/michaelbull/kotlin-result
77 ©2022 Loglass Inc. OSS: kotlin-resultの事例 • NGパターン: nullableな value
と errorを持っているわけではないことに注意
78 ©2022 Loglass Inc. 型でデータ不整合をなくす : まとめ sealed classを使って データの不整合が起こりえない型を定義する
79 ©2022 Loglass Inc. まとめ • なぜ型を使いこなすことでバグが減るのか? • 型によってバグが減らせるパターン 1.
標準の型をラップする 2. 認可処理などの特定の処理をパスしたことを型で示す 3. 型でデータの不整合をなくす • まとめ • 最後に: タイプセーフなポストモーテム
80 ©2022 Loglass Inc. まとめ • なぜ型を使いこなすことでバグが減るのか? ◦ →コンパイル時に実装ミスを検出できるから ◦
→Kotlinの型パワーを使ってコンパイル時により多くのミスを検出する
81 ©2022 Loglass Inc. まとめ • 型によってバグが減らせるパターン 1. 標準の型をラップする →
genericsで型の増加を抑制しよう 2. 認可処理などの特定の処理をパスしたことを型で示す → コンストラクタの可視性を data classを使ってコントロールしよう 3. 型でデータの不整合をなくす → sealed classでデータの不整合をなくそう
82 ©2022 Loglass Inc. 最後に: タイプセーフなポストモーテム • なぜ型を使いこなすことでバグが減るのか? • 型によってバグが減らせるパターン
1. 標準の型をラップする 2. 認可処理などの特定の処理をパスしたことを型で示す 3. 型でデータの不整合をなくす • まとめ • 最後に: タイプセーフなポストモーテム
83 ©2022 Loglass Inc. 最後に: タイプセーフなポストモーテム 情報漏洩が起きました!再発防止策は?
84 ©2022 Loglass Inc. 最後に: タイプセーフなポストモーテム - レビュワーを2人に増やす - 偉い人のチェックを増やす
- GitHubのPRテンプレにチェック項目を足 す - テスト工数を2倍にする etc…
85 ©2022 Loglass Inc. 最後に: タイプセーフなポストモーテム それ、型で解決できませんか?
86 ©2022 Loglass Inc. 最後に: タイプセーフなポストモーテム • 障害のたびにテスト工数を増やしたり、「儀式」を追加するとスピードが落ちる • 最優先で考えるべき再発防止策はコンパイル時に障害のタネを気付けるようにすること
• Kotlinはそのための機能をたくさん有している
87