Web制作、Web開発の歩き方

分かりやすいコードの書き方

第8話:変更に強いコードの設計

(最終更新日:2025.4.30)


分かりやすいコード

この記事は5分で読めます!
(絵が小さい場合はスマホを横に)

変更に強いコードへ!

「仕様変更が入った瞬間、あちこちのコードを書き直す羽目になった」 そんな経験はないだろうか。 分かりやすく、保守性が高いコードを目指すなら「変更に強いコード」という視点は欠かせない。 今回は、それを実現するための基本原則と設計アプローチを紹介する。



1.OCP(オープン・クローズドの原則)とは

OCPに関しては、以前もいくつか記事を書いている。 ここにリンクを貼る。 開放/閉鎖の原則(1)システムの拡張性を保つ設計方法開放/閉鎖の原則(2)継承とポリモーフィズムの活用。 この考え方を簡単に記すと「拡張に対しては開いているが、修正に対しては閉じているべき」になる。 つまり、新しい機能を追加するときは、既存のコードを変更せずに対応できるようにする。 そして、条件分岐を増やさずに、処理の追加は差し替えや継承・委譲で対応すると言うことだ。 言葉だけ聞くと分かりにくい。例で示そう。下記は悪い例になる。 この書き方だと、ユーザーの種類が増える度に関数を書き直さなくてはならない。

悪い例

次に良い例を示す。dictionaryを使って、ユーザーの種類と割引率を示している。 この書き方であれば、ユーザーの種類が増えたとしても関数自体を変える必要がない。 変えるのは、dictionaryの部分だけである。しかも、同様に種類と値を追加して書くだけなので、非常に分かりやすい。 これだけでもOCPの優位性は伝わるだろう。

良い例

2.関数型 vs 手続き型:どちらが変更に強いか

関数型と手続き型、どちらが変更に強いか。 この問いに対しては、関数型の方が強いと答える。 ただし、組み込みや状態制御を行う場合は、手続き型でないと実装が難しい場合が多い。 なので、臨機応変にコードを書いていけばよい。手続き型でも書き方次第で変更に強いコードにはなる。

関数型と手続き型の比較

タイプ 関数型 手続き型
状態管理 状態を持たない(不変) グローバル変数や状態に依存しがち
副作用 少ない(純粋関数) 多くなりがち
再利用性 関数合成でしやすい 流れの中で再利用しにくい
初期学習コスト やや高い 低い(慣れやすい)


3.変更の影響範囲を最小限に抑える方法

変更の影響範囲を最小限に抑えるには、次の4点を押さえよう。 1、3、4は今までも良く言ってきたことだ。2はPythonのコードでは特に重要だ。 使い方も難しくないので、本項の1を参考に、ぜひマスターしよう。

  • 関数を小さく・明確に分ける : 1つの関数の責任を小さくすることで、修正時の影響を限定
  • 条件分岐をデータ化・構造化: if文を増やすより、辞書やマッピングで処理を抽象化
  • インターフェースや抽象化に依存: 直接具体クラスを使わない。抽象に依存する
  • 変更に備えたレイヤー構造にする: UI / ロジック / データアクセスを分離、クリーンアーキテクチャを使う
4.データ駆動設計とは

データ駆動設計とは「コードを書かずに振る舞いを変える」設計のことだ。 例えば、設定ファイル(JSON、YAML)でフローや閾値を変更する。 これにより、下記のメリットが得られる。

  • 振る舞いを コード変更なしで切り替えられる
  • 非エンジニアでも設定が可能
  • テストや本番環境での運用フレンドリーな作りになる

実際にJavaScriptでデータ駆動設計すると、以下のようになる。 最初に定義したdiscountRulesの部分がデータ駆動になる。 これを引数にして、関数が機能する。 executiveというユーザータイプ(type)、割引率(rate)0.15という新たなルールを加えることだって容易だ。

データ駆動設計を用いた例

5.まとめ

今回は変更に強いコードについて紹介した。 OCPの法則、関数型の採用、条件分岐を避け、構造化・データかする、データ駆動設計という手段をとることで、 変更に強いコードを実現することができる。 そして、今回はOCPの中でもデータ駆動設計を中心に説明した。 データ駆動設計は、非エンジニアでもメンテナンス、拡張が可能である。ぜひ、これを機に使ってみよう。

▼参考図書、サイト

 「リーダブルコード」 Dustin Boswell、Trevor Foucher 著、角 征典 訳 オライリー

How to Write Readable Code Episode 8: Designing Code That’s Resistant to Change (Last updated: April 30, 2025) Readable Code This article takes 5 minutes to read! (Rotate your phone horizontally if the image is too small) Make your code resistant to change! “The moment a spec changes, I end up rewriting code everywhere.” Have you ever had that experience? If you aim for readable and maintainable code, designing it to be resistant to change is essential. In this article, we’ll cover key principles and design approaches to make that happen. [Table of Contents] What is OCP (Open/Closed Principle)? Functional vs Procedural: Which Handles Change Better? How to Minimize the Impact of Changes What is Data-Driven Design? Summary 1. What is OCP (Open/Closed Principle)? We've discussed OCP in previous articles too. Here are the links: Open/Closed Principle (1): Designing for Extensibility, Open/Closed Principle (2): Using Inheritance and Polymorphism. In short, the principle says: "Open for extension, closed for modification." This means new features should be added without modifying existing code. Instead of increasing conditionals, handle changes via substitution, inheritance, or delegation. Let’s look at an example. Here's a bad one: every time a new user type is added, you have to rewrite the function. Bad Example Now here's a better example using a dictionary to map user types to discounts. With this structure, you don’t need to modify the function itself when adding new user types—just extend the dictionary. This clearly shows the strength of OCP. Good Example 2. Functional vs Procedural: Which Handles Change Better? Which paradigm handles change better: functional or procedural? The answer is: functional programming is generally more robust against changes. However, for embedded systems or scenarios involving state control, procedural code may be more suitable. That said, you can still write robust code using procedural style with the right practices. Comparison of Functional and Procedural Styles Aspect Functional Procedural State Management Stateless (immutable) Often relies on global state Side Effects Minimal (pure functions) More frequent side effects Reusability Easy with function composition Harder within sequential flow Learning Curve Slightly steeper Gentler (easy to get used to) 3. How to Minimize the Impact of Changes To reduce the scope of impact from changes, focus on these four points. Points 1, 3, and 4 are common advice, but point 2 is especially crucial in Python. Break functions into small, clear units: Smaller responsibilities mean less impact when modified Use data structures instead of conditionals: Abstract logic using dictionaries or mappings instead of more if statements Depend on interfaces and abstractions: Avoid relying on concrete classes directly Layer your architecture for change: Separate UI, logic, and data access; consider clean architecture 4. What is Data-Driven Design? Data-driven design means modifying behavior without changing the code. For example, using config files (JSON, YAML) to change workflows or thresholds. Benefits include: Behavior can be changed without touching the code Non-engineers can adjust settings Friendly for test and production environments Here's a JavaScript example of data-driven design. The `discountRules` section is data-driven. You can easily add a new rule like `executive` with a 0.15 discount rate by updating the data only. Example of Data-Driven Design 5. Summary This article introduced how to make code resilient to change. By following principles like OCP, adopting functional style, reducing conditionals, and applying data-driven design, you can achieve more maintainable code. This time, we especially emphasized data-driven design, which enables even non-engineers to maintain and extend behavior. Give it a try! ▼References  "**Readable Code**" by Dustin Boswell and Trevor Foucher, translated by Masanori Kado, O'Reilly Japan