Slide 1

Slide 1 text

Pythonソースコードの 構造可視化 とそれがもたらすもの PyCon JP 2020 #pyconjp_2

Slide 2

Slide 2 text

自己紹介 @yosu 2 years Web App & Crawler 今月まで @ お手伝い @

Slide 3

Slide 3 text

本日お話すること 背景 見えてくるもの 構造 と その重要性 可視化の 必要性

Slide 4

Slide 4 text

背景 見えてくるもの 構造 と その重要性 可視化の 必要性 本日お話すること

Slide 5

Slide 5 text

背景 500ファイル以上あるPythonアプリケーションのPython2/3移行プロジェクト に参加 最初に移行するべき対象を見つけ出すため、簡易的なスクリプトで各パッケージ 間の依存関係を明らかにした。 うまく対象を選び、最初の移行は大きなトラブルもなく無事完了。 他にも、実際に可視化して見ると循環参照のような望ましくない構造や、依存が 多すぎるモジュールなど改善に生かす手がかりが得られることに気づいた。

Slide 6

Slide 6 text

背景 見えてくるもの 構造 と その重要性 可視化の 必要性

Slide 7

Slide 7 text

構造とは?

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

一つの全体を構成する 部分同士の関係性 部分はそれぞれが 役割・責務を持つ

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

屋根 窓 床 天井 階段

Slide 12

Slide 12 text

屋根 窓 床 階段 天井 雨を防ぐ

Slide 13

Slide 13 text

屋根 窓 床 階段 天井 光を通す 雨を防ぐ

Slide 14

Slide 14 text

屋根 窓 床 階段 天井 光を通す 雨を防ぐ 1階と2階 をつなぐ

Slide 15

Slide 15 text

Python アプリケーション の場合

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

PI = 3.1415926535

Slide 19

Slide 19 text

PI = 3.1415926535 def area(radius): return PI * radius ** 2

Slide 20

Slide 20 text

PI = 3.1415926535 class Circle: def __init__(self, radius): self.radius = radius def area(self): return PI * self.radius ** 2

Slide 21

Slide 21 text

import sys from figure import Circle def main(): radius = float(sys.argv[1]) circle = Circle(radius) print(circle.area())

Slide 22

Slide 22 text

area() PI main() Circle figure 参照 参照 参照 所有 所有 呼出し

Slide 23

Slide 23 text

area() PI main() Circle figure 依存 依存 依存 依存 依存 依存

Slide 24

Slide 24 text

area() PI main() Circle figure 名前が 役割・責務 を表す

Slide 25

Slide 25 text

依存関係の 特徴

Slide 26

Slide 26 text

A B 依存先 がないと機能しない ❌

Slide 27

Slide 27 text

A B 依存先 の変化で影響を受ける

Slide 28

Slide 28 text

今回のスコープ

Slide 29

Slide 29 text

全体を捉えるために、 モジュール同士の 依存関係をみていく

Slide 30

Slide 30 text

jig-py ⚒

Slide 31

Slide 31 text

github.com/levii/jig-py

Slide 32

Slide 32 text

イメージ

Slide 33

Slide 33 text

jig-pyの由来 Java製のコードによる設計支援ツール Jig(irofさん作)に由来。 https://github.com/dddjava/jig 三層+ドメインモデルの アーキテクチャを想定し、 様々な可視化ができる。

Slide 34

Slide 34 text

jig-pyはまだ モジュール関係のみ 可視化

Slide 35

Slide 35 text

話を戻して ↩

Slide 36

Slide 36 text

なぜ構造に 着目するか?

Slide 37

Slide 37 text

ソフトウェア だから

Slide 38

Slide 38 text

ソフト =柔軟に変化できる ここに価値がある

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

つまりソフトウェアの優位性は、開発サイクルが短く、 アイデアを出してからプロトタイプを作り、エラーを 発見して修正するまでが短時間かつローコストでできる ことにある。 もし設計プロセスで技術者が絶対にミスを犯さないのな ら、ハードウェアもソフトウェアもコストはさほど変わ らないかもしれない。だが実際にはミスは避けられない のだから、何か特別な理由でもない限り、ソフトウェア のほうが好ましいことになる。 ソフトウェアの優位性

Slide 41

Slide 41 text

つまりソフトウェアの優位性は、開発サイクルが短く、 アイデアを出してからプロトタイプを作り、エラーを 発見して修正するまでが短時間かつローコストでできる ことにある。 もし設計プロセスで技術者が絶対にミスを犯さないのな ら、ハードウェアもソフトウェアもコストはさほど変わ らないかもしれない。だが実際にはミスは避けられない のだから、何か特別な理由でもない限り、ソフトウェア のほうが好ましいことになる。 ソフトウェアの優位性 構造が影響を及ぼす

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

リファクタリングとは ソフトウェアの 外部的振る舞いを保ったままで、 内部の構造を改善していく作業のこと。 リファクタリングを行なえば、以前に書いたコードの 設計が向上することになる。

Slide 44

Slide 44 text

わるい構造

Slide 45

Slide 45 text

よい構造 わるい構造

Slide 46

Slide 46 text

よい構造 わるい構造 リファクタリング

Slide 47

Slide 47 text

よい構造 リファクタリング 変更が危険で やっかい 機能追加・バグフィクス が容易 開発スピードが安定 わるい構造

Slide 48

Slide 48 text

よい構造 リファクタリング 変更が危険で やっかい 機能追加・バグフィクス が容易 開発スピードが安定 わるい構造

Slide 49

Slide 49 text

どうやって 構造を みていくか

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

そのままでは 見えにくい

Slide 54

Slide 54 text

背景 見えてくるもの 構造 と その重要性 可視化の 必要性

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

単純に可視化 してみると

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

うまく可視化する必要

Slide 60

Slide 60 text

Google Earth

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

Python アプリケーション を探索

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

を使って 探索的に可視化する

Slide 76

Slide 76 text

① 初期状態

Slide 77

Slide 77 text

ステップ1: 解析・情報収集 "common.bind" -> "common.group.application"; "common.context.builder" -> "common.context.domain"; "event.application.event_history" -> "framework.paginator"; "event.application.repository" -> "event.domain"; "feedback.application.announcement" -> "common.exceptions"; "feedback.application.idea" -> "framework.logging"; "feedback.domain.idea" -> "event.domain"; "feedback.domain.idea" -> "notify.domain.email_builder"; "framework.paginator" -> "framework.logging"; "integration.bind" -> "event.domain.event_handler_setting"; "main" -> "framework"; "main" -> "task.routes"; "notify.application" -> "common.user.application"; "task.domain.notification" -> "common.system_setting.application"; "test_helpers.feedback" -> "common.user.domain";

Slide 78

Slide 78 text

ステップ2: トップレベルに集約 "common.bind" -> "common.group.application"; "common.context.builder" -> "common.context.domain"; "event.application.event_history" -> "framework.paginator"; "event.application.repository" -> "event.domain"; "feedback.application.announcement" -> "common.exceptions"; "feedback.application.idea" -> "framework.logging"; "feedback.domain.idea" -> "event.domain"; "feedback.domain.idea" -> "notify.domain.email_builder"; "framework.paginator" -> "framework.logging"; "integration.bind" -> "event.domain.event_handler_setting"; "main" -> "framework"; "main" -> "task.routes"; "notify.application" -> "common.user.application"; "task.domain.notification" -> "common.system_setting.application"; "test_helpers.feedback" -> "common.user.domain";

Slide 79

Slide 79 text

ステップ2: トップレベルに集約 "common" -> "common"; "common" -> "common"; "event" -> "framework"; "event" -> "event"; "feedback" -> "common"; "feedback" -> "framework"; "feedback" -> "event"; "feedback" -> "notify"; "framework" -> "framework"; "integration" -> "event"; "main" -> "framework"; "main" -> "task"; "notify" -> "common"; "task" -> "common"; "test_helpers" -> "common";

Slide 80

Slide 80 text

ステップ3: 重複の除去 "common" -> "common"; "common" -> "common"; "event" -> "framework"; "event" -> "event"; "feedback" -> "common"; "feedback" -> "framework"; "feedback" -> "event"; "feedback" -> "notify"; "framework" -> "framework"; "integration" -> "event"; "main" -> "framework"; "main" -> "task"; "notify" -> "common"; "task" -> "common"; "test_helpers" -> "common";

Slide 81

Slide 81 text

"common" -> "common"; "event" -> "framework"; "event" -> "event"; "feedback" -> "common"; "feedback" -> "framework"; "feedback" -> "event"; "feedback" -> "notify"; "framework" -> "framework"; "integration" -> "event"; "main" -> "framework"; "main" -> "task"; "notify" -> "common"; "task" -> "common"; "test_helpers" -> "common"; ステップ3: 重複の除去

Slide 82

Slide 82 text

ステップ4: セルフループの削除 "common" -> "common"; "event" -> "framework"; "event" -> "event"; "feedback" -> "common"; "feedback" -> "framework"; "feedback" -> "event"; "feedback" -> "notify"; "framework" -> "framework"; "integration" -> "event"; "main" -> "framework"; "main" -> "task"; "notify" -> "common"; "task" -> "common"; "test_helpers" -> "common";

Slide 83

Slide 83 text

ステップ4: セルフループの削除 "event" -> "framework"; "feedback" -> "common"; "feedback" -> "framework"; "feedback" -> "event"; "feedback" -> "notify"; "integration" -> "event"; "main" -> "framework"; "main" -> "task"; "notify" -> "common"; "task" -> "common"; "test_helpers" -> "common";

Slide 84

Slide 84 text

ステップ5: ビジュアライズ(初期状態準備完了)

Slide 85

Slide 85 text

② 探索

Slide 86

Slide 86 text

見たくないものを除去

Slide 87

Slide 87 text

見たくないものを除去

Slide 88

Slide 88 text

見たくないものを除去

Slide 89

Slide 89 text

詳しく見たいものを掘り下げる

Slide 90

Slide 90 text

詳しく見たいものを掘り下げる

Slide 91

Slide 91 text

注意

Slide 92

Slide 92 text

前提として 人に優しい粒度で パッケージ(階層)化 されてないと厳しい

Slide 93

Slide 93 text

デモ

Slide 94

Slide 94 text

背景 見えてくるもの 構造 と その重要性 可視化の 必要性

Slide 95

Slide 95 text

特徴的なパターン 依存しかしない: エントリーポイント/ルーター

Slide 96

Slide 96 text

特徴的なパターン 依存しかしない: エントリーポイント/ルーター 他のモジュールの 呼び分けを行う。 機能的な依存をしない ようにすると、変化の 影響を直接受けない ので怖くない。

Slide 97

Slide 97 text

特徴的なパターン 依存されるが他への依存が少ない(しない): アプリケーション基盤、共通部品

Slide 98

Slide 98 text

特徴的なパターン 依存されるが他への依存が少ない(しない): アプリケーション基盤、共通部品 変化させにくい部分。 必要とされる概念を きちんとモデリング、 一度作れば変化しなく て済むようにする。

Slide 99

Slide 99 text

特徴的なパターン 依存されるし、他への依存も多い: ビジネスロジック/コアドメイン

Slide 100

Slide 100 text

特徴的なパターン 依存されるし、他への依存も多い: ビジネスロジック/コアドメイン アプリケーションの 中核となる部分。 環境やビジネスの変 化に応じて変化して いく。

Slide 101

Slide 101 text

変 更 頻 度 依存されている数 多 い 少 な い 多い 少ない

Slide 102

Slide 102 text

変 更 頻 度 依存されている数 多 い 少 な い 多い 少ない アプリケーション基盤 エントリーポ イント ビジネス ロジック

Slide 103

Slide 103 text

変 更 頻 度 依存されている数 多 い 少 な い 多い 少ない アプリケーション基盤 ビジネス ロジック エントリーポ イント ここを 他と隔離しつつ きちんと保つ ことが重要

Slide 104

Slide 104 text

☠不吉な匂い

Slide 105

Slide 105 text

相互依存 / 循環参照 A B

Slide 106

Slide 106 text

相互依存 / 循環参照 A B

Slide 107

Slide 107 text

相互依存 / 循環参照 A B

Slide 108

Slide 108 text

相互依存 / 循環参照 A B

Slide 109

Slide 109 text

相互依存 / 循環参照 A B

Slide 110

Slide 110 text

相互依存 / 循環参照 A B

Slide 111

Slide 111 text

相互依存 / 循環参照 A B

Slide 112

Slide 112 text

相互依存 / 循環参照 A B

Slide 113

Slide 113 text

相互依存 / 循環参照

Slide 114

Slide 114 text

相互依存 / 循環参照

Slide 115

Slide 115 text

相互依存 / 循環参照

Slide 116

Slide 116 text

相互依存 / 循環参照

Slide 117

Slide 117 text

相互依存 / 循環参照

Slide 118

Slide 118 text

たいていは 責務違反

Slide 119

Slide 119 text

名前がない 隠れた役割を持つ

Slide 120

Slide 120 text

まとめ

Slide 121

Slide 121 text

まとめ 1. 構造とは一つの全体を構成する部分の関係・役割 2. 構造のあり方がソフトウェアの柔軟性に影響を及ぼす   (リファクタリングはよい構造を作るための活動) 3. うまく可視化することで構造を捉える 4. 構造の特徴・パターンから中核となる場所を見つけ、そこを きちんと保つことが重要