設計を信じすぎた話 〜完璧なアーキテクチャなど存在しない〜

※ChatGPTを使用して記事を作成しています。

「完璧なアーキテクチャを作ろう」
──そう言い出したのは、チームの誰でもない。私自身だった。

クリーンアーキテクチャ、MVVM、UseCase分離、Repository層、DI…。
教科書通りの“理想の設計”を目指したはずが、いつしか誰も手を動かせない構造になっていた。
今回は、設計を信じすぎて開発が止まった話をしたい。

理想への憧れが始まりだった

Android開発歴10年以上。数多くの失敗を経て、私は「設計さえ正しければ、開発はスムーズに進む」と信じていた。
そこで、ある新規プロジェクトでこう提案した。

「今回はクリーンアーキテクチャでいこう。ViewModelはUIだけ、UseCaseでロジックを切り出して、Repositoryでデータを抽象化する。」

チームメンバーも賛同してくれた。
見た目は整然とした構造図。依存関係の流れも美しい。
その時点では、完璧な設計に見えた

しかし──理想を追いすぎた設計は、やがて自分たちを縛る檻になっていった。

クリーンすぎて動かない

初期の実装はこうだった。

// UseCase層
class GetUserProfileUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(userId: String): Result<User> {
        return repository.getUserProfile(userId)
    }
}

// Repository層
interface UserRepository {
    suspend fun getUserProfile(userId: String): Result<User>
}

// 実装
class UserRepositoryImpl(
    private val remoteDataSource: UserRemoteDataSource,
    private val localDataSource: UserLocalDataSource,
) : UserRepository {
    override suspend fun getUserProfile(userId: String): Result<User> {
        return remoteDataSource.getUserProfile(userId)
    }
}

完璧に見える。
だが、これを何十個も実装し始めると話が変わってくる。

  • UseCaseが大量発生してフォルダが肥大化
  • Repositoryも抽象・実装のペアで倍増
  • メソッドを1つ追加するたびに4ファイルを修正

リファクタリングが怖くなり、「小さな変更をするだけでもPRがでかくなる」状態に。

まるで設計そのものが作業効率を奪っていた

チームが設計に縛られた瞬間

ある日、若手メンバーが「新しいAPI対応を追加したい」と言った。
だが、その修正には UseCase、Repository、DataSource の3層に手を入れねばならなかった。

その結果、レビューコメントが飛び交った。

「UseCase名が不適切」
「Repositoryは抽象化されすぎ」
「この責務はDomainではなくDataでは?」

気づけば議論の大半が“設計論”になっていた。
本来の目的である「機能を早く届ける」が、どこかに消えていた。

現場で起きた副作用

開発が進むにつれて、別の問題も出てきた。

  • 画面ごとに似たようなUseCaseが乱立
  • 同じ処理でも層をまたぐためにデバッグが困難
  • 新メンバーが構造を理解するまで数日かかる
  • 単体テストは書けるが、結合テストが破綻

とくに痛感したのは、「現場では完璧より、理解しやすい構造のほうが価値がある」ということだった。

“目的”を見失った設計の危険性

あるとき、ふと我に返った。
「そもそも、なぜクリーンアーキテクチャにしたんだっけ?」

答えは、「保守しやすくするため」だった。
でも現実はその逆。
変更コストは倍増し、スピードは半減

設計が「目的」ではなく「宗教」になっていた。
いつの間にか、設計のために設計していたのだ。

現実的な落とし所:80点の設計でいい

その後、私は思い切って設計を見直した。
全層構造を保ちながらも、ルールを柔軟にした

  • Repositoryは必要なときだけ抽象化
  • 小規模画面はViewModel内で完結してOK
  • UseCaseは共通ロジックのみ定義
  • テスト容易性よりも「理解しやすさ」を優先

結果、コード量が約20%削減。
チームのレビュー時間も大幅に短縮した。

重要なのは、「100点を目指さず、80点で走り続ける設計」に切り替えたことだ。
動いて改善できるコードこそ、本当に“クリーン”な設計だと気づいた。

教訓:設計は地図であって目的地ではない

  • クリーンアーキテクチャは手段であって正解ではない
  • 理想の構造を守るより、「誰でも理解できるコード」が強い
  • 現場の状況に合わせて“崩す勇気”を持つ
  • 設計は「守るため」ではなく「進めるため」にある

アーキテクチャとは、チームの成長に合わせて変わる生き物だ。
最初に決めた形を守ることが目的になった瞬間、開発は止まる。
柔軟でいることこそ、エンジニアとしての成熟だと思う。

エピローグ

振り返れば、この「Android開発失敗談」シリーズは、自分の10数年の試行錯誤をそのまま記録したようなものだった。

Jetpack Composeの無限リコンポーズも、WorkManagerの闇も、Fragmentのライフサイクルの罠も──
結局は「理解したつもり」でいた自分が招いた結果だった。

でも、失敗したからこそ学べた。
そして、同じ失敗を繰り返さないように書き残すことが、次の世代のエンジニアへの“恩返し”になると思っている。

タイトルとURLをコピーしました