1. はじめに
現代のソフトウェア開発では、初期開発よりも保守・運用フェーズのコストが全体の80%を占める1と言われています。この現実を踏まえると、ソフトウェアの「質」を考える上で保守性は避けて通れない重要な要素です。
t_wadaさんの有名な講演「質とスピード」2では、「質」が「スピード」を生み出すという本質的な関係性が語られていますが、その「質」の具体的な内容、特に保守性については詳しく触れられていません。
本記事は、中級エンジニアを対象に、「質とスピード」では詳しく説明されていない「保守性」について、「質とスピード」の中でも取り上げられていた品質モデルであるISO/IEC 25010 - SQuaREの品質特性を基に実践的な視点で解説します。
2. SQuaRE及び品質モデルの概要
2.1 SQuaREとは?
SQuaRE(Systems and software Quality Requirements and Evaluation)3は、システム及びソフトウェアの品質要求を定義し評価する際の共通言語として機能する国際規格シリーズです。ISO/IEC 25000シリーズとして体系化されており、多様なステークホルダ間での品質に関する共通認識を形成するための枠組みを提供しています。
2.2 品質の分類
SQuaREでは、品質を以下の3つの観点から分類しています:
- 利用時品質:実際の利用環境でユーザが体験する品質
- 製品品質:ソフトウェアやシステム自体が持つ品質
- データ品質:システムで扱うデータの品質
本記事では、「質とスピード」でも特筆されていた「製品品質」の一部である「保守性」に焦点を当てます。
2.3 品質特性と品質副特性の考え方
SQuaREでは、品質を階層的に整理しています。最上位の「品質特性」(保守性など)は、より具体的な「品質副特性」に分解されます。この階層構造により、抽象的な「品質」という概念を、測定可能で改善可能な具体的な要素に落とし込んでいます。
品質特性(例:保守性)
└─ 品質副特性(例:モジュール性、修正性など)
└─ 品質測定量(例:結合度、凝集度など)
3. 保守性について
3.1 保守性の定義
ISO/IEC 25010では、保守性を以下のように定義しています:
「意図した保守者によって、製品又はシステムが修正することができる有効性及び効率性の度合い」
また、保守性はコードだけでなく、設計書、仕様書、運用手順書などのドキュメントも対象としていることも重要なポイントです。美しいコードでも、ドキュメントが整備されていなければ保守性は低下します。
3.2 保守性の目的
保守性を高める目的は以下の通りです:
- 変更コストの削減:修正や機能追加にかかる時間と労力を最小化
- 品質の維持:変更時のデグレードリスクを低減
- 持続可能な開発:長期的な製品ライフサイクルに対応
- チーム生産性の向上:新規メンバーの参画や引き継ぎを容易にするため
3.3 保守性が低い場合のリスク
保守性が低いシステムは以下のようなリスクを抱えています:
- 小さな変更にも多大な時間がかかる(技術的負債の蓄積)
- 変更時にバグが混入しやすい(デグレードの頻発)
- 特定の開発者に依存する(属人化)
- 最終的にシステムの作り直しが必要になる
3.4 保守性を構成する5つの品質副特性
ISO/IEC 25010では、保守性は以下の5つの品質副特性で構成されています。
次の節では、各品質副特性の特徴について説明します。

出典:P115.SQuaRE 品質モデル(製品品質)3より抜粋
4. 各品質副特性の特徴について
4.1 モジュール性(Modularity)
目的
特定の部品(モジュール)への変更が、他の部品に予期せぬ悪影響(副作用)を及ぼすのを最小限に抑えることで、システムの変更コストを下げ、安定性を維持するため。
手段
システムを、それぞれが明確な「責任」を持つ小さな部品に分割し、お互いの依存度を低くする「高凝集・疎結合」な状態を目指す。
具体例
// 悪い例:密結合
public class OrderService {
public void createOrder(Order order) {
// 直接メール送信処理を記述
EmailSender.send("order@example.com", "新規注文: " + order.getId());
// 直接データベースアクセス
Connection conn = DriverManager.getConnection("jdbc:...");
// ...
}
}
// 良い例:疎結合
public class OrderService {
private final NotificationService notificationService;
private final OrderRepository orderRepository;
public OrderService(NotificationService notificationService,
OrderRepository orderRepository) {
this.notificationService = notificationService;
this.orderRepository = orderRepository;
}
public void createOrder(Order order) {
// 永続化の詳細は Repository に委譲
orderRepository.save(order);
// 通知の詳細は別モジュールに委譲
notificationService.notifyNewOrder(order);
}
}
4.2 再利用性(Reusability)
目的
開発効率と品質を同時に高めるためです。既に開発・検証済みの資産を使い回すことで、ゼロから作るコストを省き、新たな欠陥が混入するリスクを最小限に抑えます。
手段
特定のシステム環境に依存しすぎず、多様な状況でも利用できるように汎用性を持たせて設計します。具体的な実装だけでなく、抽象的な設計モデルのレベルで再利用できるように構成することもあります。
4.3 解析性(Analysability)
目的
意図した変更がシステムに与える影響の評価(影響調査)や、欠陥・故障の原因診断、および修正箇所の特定を「有効かつ効率的」に行うためです。これにより、修正箇所の特定を正しく・網羅的に行うことが可能になります。
手段
システムの内部構造を分かりやすく保つことや、実行時の挙動を観察可能(可観測性)にすることが挙げられます。具体的には、早期リターンによるネスト解消でコードの構造を単純化したり、適切なログ出力や自己診断機能を実装したりして、中身を「見える化」します。
具体例
悪い例:複雑なネストと条件分岐
- if文が多重にネストされており、わかりづらい
- コメント文がなく、コードを読み理解が必要
public double calculatePrice(User user, Product product) {
double price = product.getBasePrice();
if (user != null) {
if (user.isActive()) {
if (user.hasPermission("discount")) {
if (product.getCategory().equals("ELECTRONICS")) {
price = price * 0.9;
} else if (product.getCategory().equals("BOOKS")) {
price = price * 0.95;
}
}
}
}
return price;
}
良い例:早期リターンと責務の分離
- 早期リターンにより、if文が多重にネストが改善され、わかりやすくなった
- javadocコメント文があることで、コードを読まなくても概観・ビジネスルールが理解可能
- 各メソッドが単一の責務を持つ構成となり、理解しやすくなった
/**
* 商品の販売価格を計算します。
*
* ユーザーの購入資格(アクティブ状態、割引権限)を確認し、
* 該当する場合はカテゴリ別の会員割引を適用した価格を返します。
*
* <p>価格決定のビジネスルール:
* <ul>
* <li>非会員(user=null)または非アクティブ会員:定価</li>
* <li>割引権限のない会員:定価</li>
* <li>割引権限のある会員:カテゴリ別割引率を適用</li>
* </ul>
*
* @param user 購入者情報(null可、nullの場合は非会員として扱う)
* @param product 購入対象商品(必須)
* @return 割引適用後の販売価格
*/
public double calculatePrice(User user, Product product) {
if (user == null || !user.isActive()) {
return product.getBasePrice();
}
if (!user.hasPermission("discount")) {
return product.getBasePrice();
}
return applyDiscount(product);
}
/**
* 商品に対してカテゴリ別の割引を適用します。
*
* @param product 割引対象商品
* @return 割引適用後の価格
*/
private double applyDiscount(Product product) {
double basePrice = product.getBasePrice();
double discountRate = getDiscountRate(product.getCategory());
return basePrice * discountRate;
}
/**
* 商品カテゴリに対応する割引率を取得します。
*
* <p>現在の割引率設定:
* <ul>
* <li>電化製品(ELECTRONICS):10%割引(0.9)</li>
* <li>書籍(BOOKS):5%割引(0.95)</li>
* <li>その他:割引なし(1.0)</li>
* </ul>
*
* @param category 商品カテゴリ
* @return 割引率(0.0〜1.0、1.0が割引なし)
*/
private double getDiscountRate(String category) {
Map<String, Double> discountRates = Map.of(
"ELECTRONICS", 0.9,
"BOOKS", 0.95
);
return discountRates.getOrDefault(category, 1.0);
}
補足:可観測性(Observability)
可観測性とは、システムの外部出力(ログ、メトリクス、トレース)から内部状態を推測できる能力を指します。最近のマイクロサービスアーキテクチャでは特に重要視されている概念です。
4.4 修正性(Modifiability)
目的
新たな欠陥の混入(デグレード)や既存の品質低下を招くことなく、修正作業を正確かつ効率的に完了させるためです。これにより、バグを入れずに正確かつ効率的にコードを変更できるようになります。
修正性が高い=サクサク変更できる状態こそが、t_wadaさん「質とスピード」で言われている「スピード」の源泉になります。
手段
修正の影響が最小限になるよう責務を分離し、高凝集・疎結合なモジュールを作成したり、解析性の高いモデル・設計書ドキュメントやコードを作成することで理解しやすくなり、素早く・正しく修正できるようにする。
4.5 試験性(Testability)
目的
修正後のシステムが要求を満たしているかを、効率的かつ確実に検証するためです。これにより、変更による意図せぬ不具合(デグレード)を早期に発見し、システムの信頼性を維持します。
手段
テスト基準を確立しやすい構造を選び、ユニットテストが書きやすいよう依存関係を整理します。
5. 個人的見解
5.1 品質副特性の相互関係
実務経験から、保守性の5つの副特性は独立ではなく、相互に影響し合う関係であると考えています。特に、モジュール性・解析性を高めることが重要であると考えていて、その理由はモジュール性・解析性を高めることで、修正性・試験性に対して良い影響を与えることができるからです。具体的にモジュール性・解析性がどのように修正性・試験性に影響を与えるのか説明します。
モジュール性向上による影響
修正性への影響:
モジュール性向上により、一つの変更が他の部分に与える影響を最小限に抑えられます。これにより、意図せぬ副作用(デグレード)を防ぎながら、安全に変更作業を行うことができ、修正性を高められる。
試験性への影響:
システムが独立した小さな部品に分割されているほど、テストの予測や実行が容易になります。特に疎結合であれば、他の部品に依存せず独立してテストできるため、試験性を高められる。
解析性向上による影響
修正性への影響:
解析性が高い状態とは、修正すべき箇所や影響範囲を素早く正確に特定できる状態です。どこを直すべきかが明確になれば、修正作業そのものの効率と正確性が直接的に高められる。
試験性への影響:
内部構造が分かりやすく、理解が容易であれば、テストケースが立てやすい。何をテストすべきか、どのような入力でどのような出力を期待すべきかが明確になるため、試験性も高められる。
5.2 再利用性は優先度を下げても良い
実践的な観点から、再利用性については以下の理由で優先度を下げても良いと考えます:
- 不確実性が高い:別箇所や別のプロジェクトで使い回すことを確約された状態で開発することは少ない
- 設計コストが高い:様々な状況で使うことを想定して設計することはハードルが高い(=手間・時間が必要)
- YAGNI原則:「You Aren't Gonna Need It」の原則に従い、明確に再利用することが決まっていない限りは再利用性を考えなくても良い
むしろ、現在のプロジェクトにおける保守性(特にモジュール性と解析性)に注力することで、将来的に再利用が必要になった際、リファクタリングしやすい状態を維持することが重要です。
またモジュール性を上げることで高凝集・疎結合なモジュールとなり、他の機能を引きずらず、使い方もシンプルになるため、再利用が容易になります。再利用性の観点からもモジュール性が重要と言えます。
6. まとめ
t_wadaさんの「質とスピード」における「質」の本質は、この保守性にあると言っても過言ではありません。特に修正性が高い状態、つまり「サクサク変更できる状態」こそが、持続的な開発スピードを生み出す源泉となります。
保守性を向上させるためには:
- まずモジュール性と解析性に注力する:これらは他の副特性の基盤となる
- コードだけでなくドキュメントも重視する:保守性はシステム全体の特性
- YAGNI原則を意識する:再利用性は必要性が明確になってから考える
品質特性を意識的に設計・実装に組み込むことで、「質がスピードを生む」好循環を作り出すことができるのです。
参考文献
-
IT READERS 「IT予算の8割がシステム維持管理」が依然続き、沈みゆく日本のITユーザー/IT業界 ↩
-
IPA つながる世界のソフトウェア品質ガイド」 ↩ ↩2