KISS(Keep It Simple, Stupid)は、システムの最大限のシンプルさを求める開発原則です。複雑性は、絶対に必要な場合にのみ追加すべきであり、念のために追加してはいけません。IEEE Transactions on Software Engineering(2020)の研究によると、コードの複雑さは欠陥密度と相関しており、サイクロマティック複雑度が高いモジュールには、1000行あたり3.6倍のバグが含まれます。KISSは原始性ではなく、機能する最もシンプルな解決策を意識的に選択することです。
重要ポイント
KISS(Keep It Simple, Stupid)は、システムの複雑さを最小限に抑えることを要求する設計原則です。1960年代にアメリカ海軍でエンジニアのケリー・ジョンソン(Lockheed SR-71 Blackbird)によって提唱されました。ジョンソンは、航空機が特別な工具なしで現場の整備士によって修理可能であるべきだと主張しました。これがKISSの本質です。
ソフトウェア開発において、KISSは次のことを意味します。解決策は可能な限りシンプルであるべきですが、それ以上シンプルであってはなりません(このフレーズの後半はアルバート・アインシュタインに帰せられます)。シンプルさは原始性の同義語ではありません。シンプルな解決策は、最小限の冗長性でタスクを実行します。
Google Research(2022)の調査によると、KISSを遵守するプロジェクトでは、新しい開発者の平均オンボーディング時間は3週間であるのに対し、過剰なアーキテクチャのプロジェクトでは10週間でした。シンプルなコードは、新しいチームメンバーの適応速度への投資です。
KISSをフィルターとして使用してください。新しい抽象化を追加する前に、自分自身に問いかけてください。「これは今日存在する問題を解決するのか、それとも1年後に発生する可能性のある問題を解決するのか?」後者であれば、実行しないでください。
オッカムの剃刀(14世紀)は哲学の原則です。「必要なくして存在を増やしてはならない」。プログラミングでは、これは次のことを意味します。要件を同等に満たす2つの解決策のうち、存在(クラス、モジュール、依存関係)が少ない方を選択してください。KISSは、コードにおけるオッカムの剃刀の実践的な実装です。
違いは、オッカムの剃刀が認知の一般原則であるのに対し、KISSは測定可能な結果を伴う具体的なエンジニアリングプラクティスであることです。サイクロマティック複雑度の低減、コード行数の削減、コードレビュー時間の短縮などです。メトリクスにより、KISSの遵守状況を客観的に評価できます。
次のメトリクスに従ってください。新しい開発者がコメントなしで1分以内にコード断片を理解できれば、コードは「十分にシンプル」と見なされます。それ以上の時間が必要な場合は、簡素化してください。
モバイル開発には、KISSを特に重要にする3つの特徴があります。デバイスのリソース制限(メモリ、CPU)、頻繁なプラットフォームアップデート(iOSは年次、Androidは四半期ごと)、CI/CDによる迅速な機能提供の必要性です。複雑なコードはこのペースについていけません。
Apple WWDC 2023:「Embrace Swift Generics」の分析によると、平均的なiOSプロジェクトには40〜60%の「デッドコード」、つまり「将来のために」書かれたが決して使用されない抽象化が含まれています。このコードはバイナリサイズを増やすだけでなく、コンパイルを遅くし、ナビゲーションを複雑にします。KISSはこれを防ぎます。今必要なものだけを書きましょう。
Android Developer Relations Report(2024)によると、コードとテストの比率が低い(1:0.8未満)プロジェクトでは、本番バグが67%多くなります。複雑なコードはテストが困難であり、これは品質への直接的な脅威です。シンプルさは、高いテストカバレッジの前提条件です。
メトリクスを通じてコードの複雑さを測定してください。サイクロマティック複雑度は各メソッドを10未満に保ち、理想的には5未満にしてください。自動チェックにはDetekt(Android)またはSwiftLint(iOS)を使用してください。
典型的なオーバーエンジニアリングは、単一のデータソースを持つプロジェクトで抽象リポジトリファクトリを作成することです。シンプルなRepositoryクラスの代わりに、開発者はRepositoryFactory → IRepository → BaseRepository → RepositoryImplというチェーンを構築します。これは、APIをGraphQLに変更するという仮想的な可能性のためです。
JetBrains Developer Survey(2023)によると、43%のAndroid開発者が、リファクタリング中にアーキテクチャレイヤーを破棄したことを認めています。それは決して使用されなかったからです。KISSは言います。抽象化は、実装の第二の選択肢が登場したときに作成すべきであり、先取りして作成すべきではありません。
インターフェースなしの具体的な実装から始めてください。第二のデータソースが登場したら、リファクタリングを通じてインターフェースを抽出してください(IDEが自動的に行います)。これは事前にインターフェースを書くより速いです。
DIフレームワーク(Dagger、Hilt、Swinject)は強力なツールですが、複雑さを誘発することがよくあります。開発者は、1か所でしか使用されない場合でも、エンティティごとに個別のモジュールを作成します。KISSの代替案:単純なケースではコンストラクタによる手動注入です。
// オーバーエンジニアリング:単一リポジトリ用のモジュール
@Module
object UserModule {
@Provides
fun provideUserRepo(): UserRepository = UserRepositoryImpl()
}
// KISS:リポジトリが1つの場合は手動注入
class UserViewModel(
private val repo: UserRepository = UserRepositoryImpl()
) { /* ... */ }
コンストラクタでの手動注入は最もシンプルなDIパターンです。コード生成、アノテーション、モジュールは必要ありません。プロジェクトが5画面以上に達し、手動注入の保守が困難になった場合にのみ、切り替えてください。
AndroidのViewModelは、過剰な複雑さの頻繁な原因です。開発者は、シンプルなMutableLiveDataとpostValueで十分な場所に、StateFlow、combine、flatMapLatest、および変換チェーンを追加します。KISSは推奨します。最もシンプルな解決策(LiveData)から始め、特定のニーズ(状態リセット、デバウンス)のためにのみ複雑にしてください。
// KISS:リアクティブチェーンなしのシンプルなViewModel
class ProfileViewModel : ViewModel() {
private val _name = MutableLiveData<String>()
val name: LiveData<String> = _name
fun loadUser(id: String) {
viewModelScope.launch {
_name.postValue(repo.getUser(id).name)
}
}
}
この例では、ViewModelは非同期リクエストにコルーチンを使用し、結果の公開にLiveDataを使用しています。StateFlowもcombineもなく、実際に必要なものだけがあります。明示的な状態を持つ単方向データフロー(UDF)が必要な場合に追加してください。
iOSでは、KISSの原則はデータモデルにクラスではなく構造体を優先することで現れます。構造体は値型であり、ARCによるメモリ管理を必要とせず、デフォルトで不変です。クラスは、同一性(同じオブジェクトへの2つの参照)や継承が必要な場合にのみ正当化されます。
// KISS:モデルにクラスではなく構造体
struct User: Codable {
let id: Int
let name: String
let email: String
}
// オーバーエンジニアリング:手動のinitとdeinitを持つクラス
class UserClass: NSObject {
let id: Int
init(id: Int) { self.id = id }
}
User構造体は、自動的にメンバーワイズイニシャライザ、EquatableおよびHashable準拠(すべてのフィールド)、不変性、マルチスレッド環境での安全性を取得します。クラスは手動のinit、NSObjectの実装が必要であり、共有状態を介した競合状態の影響を受けやすくなります。
ネットワーク層は、KISSが頻繁に違反されるもう1つの領域です。開発者は、5要素以上のInterceptorチェーン、抽象ファクトリによるシリアル化、エンドポイントごとのマッパーを追加します。KISSの解決策:設定付きの1つのURLSessionと、Codable/JSONによる1つのデコードです。
Apple URLSession Programming Guide(2023)によると、URLSessionとCodableを使用したシンプルなネットワーク層は、モバイルアプリのシナリオの95%をカバーします。複雑なInterceptorチェーンは、トークンリフレッシュ、ロギング、暗号化といった特定のケースにのみ必要です。
URLSession + Codableに基づくシンプルなネットワーク層から始めてください。「念のため」ではなく、実際のニーズが生じたときにInterceptorを追加してください。これにより、ネットワーク層のコードが2〜3分の1に削減されます。
シンプルさは原始性と同じではありません。シンプルな解決策とは、冗長性なくタスクを解決する簡潔で明確な解決策です。原始的な解決策は、ベストプラクティスと健全なアーキテクチャを無視します。違いは、シンプルな解決策は拡張が容易であるのに対し、原始的な解決策はそうではないことです。
例:すべての画面の唯一のエンティティとしてActivityを使用することは、シンプルさではなく原始性です。シンプルさとは、異なる画面に異なるFragmentを使用したNavigation Componentの使用ですが、不必要な抽象化はありません。KISSは貧弱なアーキテクチャを正当化しません。
自分自身を確認してください。新しい機能を追加するときにコードは変更できますか?はいの場合、シンプルさは正しいです。すべての機能にすべてを書き直す必要がある場合、それは原始性です。すぐにリファクタリングしてください。
パターン(MVVM、MVI、Coordinator)は複雑化ではなく、構造化です。KISSは実証済みのアーキテクチャパターンの使用を禁止していません。禁止されているのは過剰な使用です。1つで十分なところに3つのパターンを使用することです。黄金の中庸は、プロジェクトあたり1つのアーキテクチャパターンと、2〜3つ以下の補助パターン(DI、Navigation)です。
State of Mobile Architecture Report(2024)によると、正確に1つのアーキテクチャパターンを使用するプロジェクトは、3つ以上のパターンを組み合わせた「フランケンシュタイン」プロジェクトよりも、開発初年度のバグが34%少なくなっています。選択してくださいモバイルプロジェクトにはMVVMまたはMVIを選択し、すべての画面でそれを一貫して使用してください。
同じプロジェクトでMVVMとMVIを混在させないでください。チームがMVVMを選択した場合、プロジェクト全体がMVVMに従う必要があります。例外は、独自のアーキテクチャ上の決定を持つ個別の機能モジュールですが、これは意識的な選択でなければなりません。
よくある質問
KISS(Keep It Simple, Stupid)は、コードを可能な限りシンプルにすることを要求する原則です。タスクを余分なクラス、パターン、抽象化なしで解決できる場合は、それらなしで解決してください。シンプルな解決策は、理解、テスト、変更が容易です。
DRYはコードの重複を禁止し、KISSは過剰な複雑さを禁止します。時にはそれらは衝突します。重複を排除しようとする(DRY)と、複雑な抽象化(KISS違反)につながる可能性があります。Rule of Threeがバランスを取るのに役立ちます。3回目の繰り返しの後にのみ抽象化してください。
KISSは、将来の要件を確実に知っている場合に破ることができます。例えば、KMMを介したセカンドプラットフォームのサポートや、来四半期の新しいアーキテクチャへの移行などです。条件:将来の要件は文書化されている必要があり、仮説的な推測であってはなりません。
客観的なメトリクスを使用してください。サイクロマティック複雑度(メソッドあたり10まで)、メソッドあたりのコード行数(20まで)、ネストレベル(3まで)。AndroidにはDetektプラグイン、iOSにはSwiftLint。主観的なメトリクス:新しい開発者が1分でコードを理解できるべきです。
はい、KISSとSOLIDは互換性があります。SOLIDは正しいアーキテクチャに関するものであり、KISSは最小限の複雑さに関するものです。KISSの違反は、SOLIDを過剰に適用した場合に発生します。3つで十分なところに12ものクラスを作成することです。黄金律:SOLIDは合理的な範囲で、KISSはすべてのステップでのフィルターとして。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。