Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ソフトウェアを作る際に意識してほしい責務と依存性

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for iwaseshi iwaseshi
December 25, 2021

 ソフトウェアを作る際に意識してほしい責務と依存性

Avatar for iwaseshi

iwaseshi

December 25, 2021
Tweet

More Decks by iwaseshi

Other Decks in Technology

Transcript

  1. 「良い」ソフトウェアがもたらすもの • 企業はビジネスで優位に立つために金をかける。 • 金をかける理由は将来儲けるため。 • ソフトウェア開発もお金を得るための手段。 • 多くのユーザが利用し、自社に利益をもたらす。 •

    クライアント企業が新たな事業で利益を得た。 • 企業のオペレーションが変わり、コストが削減された。 …等 4 お金です。 「良い」ソフトウェアはビジネスで価値を生む。
  2. 「変更を怠った」ソフトウェアの末路 • 価値を提供しないソフトウェア • ユーザからのFBや市場のニーズに対応できなかった。 • 運用コストが高く、価値のない機能を提供し続けていた。 • 価値を逃したソフトウェア •

    特定のニーズに特化させることができなかった。 • ほとんどの価値が他のソフトウェアの劣化となってしまった。 • 対応したころには、そのニーズがなくなっていた。 • 対応に莫大なコストがかかり、費用対効果が薄い(ない)。 5 果たしてこんなソフトウェア使いたいですか?運用できますか? お金を生み出していますか?投資した分だけお金を取り戻せているのだろうか?
  3. クリーンなコードは変更のしやすさを担保する • Cohesive:凝集性のあるコードは副作用を減らす • Loosely coupled :疎結合なコードはテストが容易である • Encapsulated :カプセル化されたコードは簡単に拡張できる

    • Assertive :断定的なコードによってソフトウェアがモジュール化される • Nonredundant :非冗長なコードは保守の問題を減らす 7 クリーンなコードを書くこと。 凝集性があり、疎結合で、カプセル化されており、断定的で、非冗長なコードのこと。 これらの要素は、どれも変更の容易さに収束する。
  4. 奇麗なものは俯瞰しやすく、問題に気づきやすい • 凝集性のあるコードは理解もバグを見つけるのも簡単だ。 それぞれのモジュールは1つの事しか扱っていないからだ。 • 疎結合なコードは呼び出し元を選ばず、再利用やテストが容易だ。 テストが容易であれば、その変更が正しいことが速く証明される • カプセル化されたコードは複雑さを管理し、呼び出し元が呼び出し先の実装の詳細を知らなくても維持ができる。 あとからの変更も簡単だ。

    • 断定定期なコードは何ができて何ができないか、何をしないかがハッキリしている。 • 非冗長なコードはバグ修正や変更を1か所で1回やればいい。 8 変更すべき箇所や、問題となっているものがわかりやすい。 奇麗にすればするほどコードの内部品質が私たちを導いてくれる。 もう全人類読んでくれ
  5. • 開発のゴールを勘違いしている。 • 開発にゴールはない。リリースしてからがスタート • そもそも後から直す暇なんかない。 • 市場やユーザのニーズは変わり続けるから • コードの品質を高く保っていた「にも関わらず」速くコード書けるエンジニアは存在しない。

    • 品質を高く保っていたからこそ速いエンジニア 10 スピードか内部品質か ソフトウェアだから片方を犠牲にしてもいい良いというのは間違い。 ソフトウェアも飲食店などと変わらず、速く高品質なものがビジネスに価値を生む。 詳しくはt_wada氏の「質とスピード」を読んでほしい。てか読め。
  6. 依存性を意識したモジュール設計の例 • 以下は悪例。設計書の記述をそのまま一行一行ロジックに落とした方が、確かに直感的ではある 13 Customer customer = customerRepository.getCustomer(id); if(customer.getStatus() ==

    ACCOUNT_LOCK || customer.getStatus() == TEM_LOCK || customer.getStatus() == ANTI_SOCIAL) { {無効な顧客用の処理} return; } Account account = accountRepository.getAccount(customer.getAccountId()); if(account.getStatus() == xxxx || …etc ) { {無効な口座用の処理} return; } If … 実装 • 実装の中で行われている顧客の状態の検証は、機能を満たすための検証とビジネス上のルールが混在している • この機能として必要な情報は、あくまで「無効な顧客であるかどうか」である。 • この機能は有効/無効な顧客に対して何を行うかに関心を持つべきであり、ビジネス上の無効な顧客の条件に関しては関心を持つべきでは ない。 • ルールの変更の都度、このモジュールに対しても変更が加えられ、テストも必要になる。 • ルール変更時に検証すべきは新たなルールに従い有効/無効な顧客を判定できているかであり、判定によって提供する機能ではない。 ・顧客のデータを取得する ・顧客の状態を確認する。※以下の場合は無効な顧客とする。 ・アカウントロック ・一時ロック ・反社 ・有効な顧客だった場合、口座情報を取得する。 ・口座の状態を確認する。 ・条件1 ・条件2 …etc ・口座の利用可能残高に応じてxxxxをする。 設計書 機能を満たすモジュールは様々なビジネスルールに依存している。 設計の段階で、機能として提供すべき処理とビジネスルールとして定義すべきものを識別する必要がある
  7. • ルールと機能を意識した実装に変更した場合。 14 Customer customer = customerRepository.getCustomer(id); if(customerRule.isInvalidCustomer(customer)) { {無効な顧客用の処理}

    return; } Account account = accountRepository.getAccount(customer.getAccountId()); if(accountRule.isInvalidAccount(account)) { {無効な口座用の処理} return; } If … 実装 ・顧客のデータを取得する ・顧客の状態を確認する。※以下の場合は無効な顧客とする。 ・アカウントロック ・一時ロック ・反社 ・有効な顧客だった場合、口座情報を取得する。 ・口座の状態を確認する。 ・条件1 ・条件2 …etc ・口座の利用可能残高に応じてxxxxをする。 設計書 • 機能を提供するか否かを判断するのにルールの状態を意識する必要がなくなった。 • 他の機能でもルールを使用することができる。 • モジュールに対してテストすべきことが明確になる。 • ルールに変更があっても、機能を再検証する必要がなくなる。 • ルールに則った実装か否かはルールを満たすモジュールのテストコードが担保する。 依存性を意識したモジュール設計の例 機能とビジネスルールの責務を明確にしモジュールを分割することで、 ルールの変更や、機能として参照するルールの差し替えの際に影響を最小限にできる。
  8. 変更を受け入れるシステムアーキテクチャ • システムはスケールアウトまたはスケールアップすることで対応できるユーザ数を増やすことができる。 • モノリス → マイクロサービスも期待される価値は前述したコードの話と変わらない。 • MSAの本質は「ビジネスとして必要な単位で構成を変えられるアーキテクチャ」 •

    リアルタイムにビジネスを支えているモジュールをオートスケールさせる仕組み(K8Sやコンテナ)を活用できる。 • 意味のない塊になっていれば、不要なスケーリングやコストがかかる。 • エコシステムの中で最も価値を生み出しているサービスそのものを利用できる。 • 責務過多による単一障害点を作りこまないためにも、システム間でも依存関係は大事な要素となる。 21 アーキテクチャも単純かつ振る舞いが明確なモジュールの集まりにすることが、近年のトレンドと なっている やるべきことや考えるべきことの本質はクリーンなコードと変わらないが、それを果たすことによ るビジネスの価値はクリーンなコードに比べると計り知れない単位になる。
  9. 誰でもできるチーム → 誰かに任せられるチーム • チームにも変更はつきもの。 • フェーズによって必要となる人数も人材も変わる。 • 他にもライフプランやキャリアプランに応じてメンバーがチームを離れる等、人の出入りがあるのは当たり前。 •

    個々のスペシャリティが明確なら、出入りによる変更に対して耐性のあるチームが生まれる。 • 全員が何でもやるチームは出入りの都度、教育コストがかかってしまう。 • メンバーのやることが明確ならば、 チームとしてやるべきことや、やるべきでないこと(他のチームに任せるべきこと)も明確になる。 • 人ではなく、ツールに一任することもできる。 22 皆が浅く広く知を共有しているチームは個々人の価値が最大化しにくい。 なぜならば、知を共有している状態なので、課題解決に際に合意を得るなどのプロセスが生じ、同じ知識 を共有していても知識量や過去の経験などへの暗黙的な依存関係が生まれる。 個々が責任を果たすものを明確にすることで、知識やスキルの依存性をスペシャリスト1人に閉じること ができる。
  10. まとめ 23 ソフトウェア以外にも責務と依存関係を明確にする考え方の応用は可能。 また役割が明確なものは俯瞰してみたときに、問題に気づきやすい。 問題に気づきやすいと、それを解決する判断がしやすい。 • 何を責務として全うするのか。 • 何と協働すべきか(しないべきか) この2つを考えるだけでソフトウェア開発がクリエイティブになり、楽しくなる。

    変更に強いコード、テスト、アーキテクチャ、チームは見栄えがいいだけではなく、ビジネスに価値を生み出す。 こんな簡単なことを放棄する人にビジネスもエンジニアリングも語る資格はない。 本当に必要なのは、変更に強い人間なのかもしれない。
  11. 25 • 参考文献 – David Scott Bernstein. レガシーコードからの脱却 ―ソフトウェアの寿命を延ばし価値を高める9つのプラクティス –

    Eric Evans. ドメイン駆動設計 – Fowler, Martin. リファクタリング(第2版): 既存のコードを安全に改善する – Robert C.Marti. Clean Architecture 達人に学ぶソフトウェアの構造と設計 – 質とスピード(2020秋100分拡大版) / Quality and Speed 2020 Autumn Edition https://speakerdeck.com/twada/quality-and-speed-2020-autumn-edition