AI SE

変わる技術トレンド、変わらない「設計思考」〜 ChatGPTも使う"設計の原理"は、あなたが考えるもの 〜

はじめに:崩れた家の物語

5年前、あるスタートアップ企業のシステムリニューアル案件を担当しました。

前任者が構築したPHP製のECサイトは、見た目は立派でした。管理画面も充実している。決済機能も実装されている。しかし、そのシステムには致命的な問題がありました。

少しの修正が、システム全体を壊す。

「商品画像のサイズを変更したい」という単純な要望が、なぜかカート機能に影響を与える。「会員登録フォームに項目を追加したい」という変更が、注文履歴の表示を崩す。

コードを読み解いていくと、原因が見えてきました。

  • すべての機能が1つの巨大なファイルに詰め込まれている
  • データベース設計に一貫性がない
  • 処理の依存関係が複雑に絡み合っている

つまり、「設計」という土台が存在しなかったのです。

優秀なエンジニアが書いたコードでした。動作も問題なかった。でも、設計がなかったために、メンテナンスも拡張も困難な「レガシーシステム」になっていました。

この経験から、私は学びました。

どんなに完璧なコードも、設計なしでは、システム開発段階において大きな見直しが発生するリスクがある。


AI補助で開発効率は上がっても、設計の責任は人にある

ChatGPTは「大工」であって「建築家」ではない

GitHub CopilotやChatGPTは、確かに驚くべき速さでコードを生成します。

「ログイン機能を作って」と頼めば、数秒で実装コードが完成する。「このAPIを叩いてデータを取得して」と言えば、エラーハンドリングまで含めて書いてくれる。

でも、AIには答えられない問いがあります。

  • このシステム全体を、どんなアーキテクチャで構成すべきか?
  • 将来の拡張性を考えると、どんな設計パターンを採用すべきか?
  • パフォーマンスとメンテナンス性のバランスをどう取るか?
  • このプロジェクトの制約条件下で、最適な技術スタックは何か?

これらは、建築家(アーキテクト)の仕事です。

AIは優秀な大工です。でも、家全体の設計図を描くのは、人間の役割なのです。

実例:Flutter製の社内ツールで学んだこと

先日、Flutter製の社内タスク管理ツールを開発する機会がありました。

最初、若手メンバーがGitHub Copilotを使って、驚くべき速さで機能を実装していきました。タスク一覧、タスク作成、タスク編集、タスク削除——すべてが1週間で完成。

しかし、品質管理の観点からコードレビューをすると、大きな問題が見えてきました。

// 各画面で直接APIを呼び出している
class TaskListScreen extends StatefulWidget {
  @override
  _TaskListScreenState createState() => _TaskListScreenState();
}

class _TaskListScreenState extends State<TaskListScreen> {
  Future<void> loadTasks() async {
    final response = await http.get('https://api.example.com/tasks');
    // レスポンスをパース...
  }
}

一見問題なさそうですが、このままでは:

  • API仕様が変わったら、すべての画面を修正する必要がある
  • オフライン対応を追加したくなったら、大規模なリファクタが必要
  • テストが書きにくい
  • 状態管理が各画面にバラバラに存在する

つまり、「設計」が欠けていたのです。

私はチームと議論し、以下のような設計方針を立てました。

// Repository層でAPI通信を抽象化
abstract class TaskRepository {
  Future<List<Task>> getTasks();
  Future<Task> createTask(Task task);
  // ...
}

// Provider/Riverpodで状態管理を集中化
final taskProvider = StateNotifierProvider<TaskNotifier, AsyncValue<List<Task>>>((ref) {
  return TaskNotifier(ref.read(taskRepositoryProvider));
});

// UIはビジネスロジックから分離
class TaskListScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final tasks = ref.watch(taskProvider);
    // UIの構築...
  }
}

この設計により:

✅ API仕様変更は1箇所の修正で対応可能
✅ オフライン対応も、Repository層を差し替えるだけ
✅ 単体テストが書きやすい
✅ 状態管理が一元化され、バグが減る

AIは個々のコードは書けますが、このアーキテクチャ全体の設計は、人間が考える必要があります。


「設計」と「コーディング」の違い

家づくりに例えると

システム開発を家づくりに例えてみましょう。

画像

家を建てる前に、必ず設計図を描きますよね。

  • どこに柱を立てるか
  • 水回りをどこに配置するか
  • 将来、部屋を増築できるような構造にするか
  • 地震に耐えられる構造にするか

これらを考えずに、いきなり釘を打ち始める大工はいません。

でも、システム開発では、設計なしにコードを書き始めるエンジニアは珍しくありません。

特にAIツールの登場で、この傾向は強まっています。「すぐコードが書ける」からこそ、設計をスキップしてしまうのです。

設計の有無で変わる、システムの未来

設計なしで作った場合

初期: 速い!すぐ動く!
3ヶ月後: 新機能を追加しづらい...
6ヶ月後: バグが多発。修正が怖い。
1年後: 作り直した方が早い状態に。

設計してから作った場合

初期: 時間がかかる...
3ヶ月後: 新機能がスムーズに追加できる
6ヶ月後: バグが少ない。影響範囲が予測しやすい。
1年後: 継続的に成長し続けるシステムに。

時間をかけて設計する = 未来への投資なのです。


設計力を鍛えるための3つの習慣

習慣1:「コードを書く前に図を描く」

私が新しいシステムを作る前に、必ずやることがあります。

ホワイトボードに図を描くこと。

紙でも、iPadでも、なんでも構いません。とにかく、視覚化するのです。

最低限描くべき図

1. システム構成図

[ ユーザー ] → [ Webアプリ(Flutter Web) ]
                      ↓
              [ API Server(PHP) ]
                      ↓
           [ データベース(MySQL) ]
                      ↓
           [ 外部API(決済サービス等) ]

2. データモデル図(ER図)

[ User ] 1 ─── ∞ [ Task ]
   |                |
   | 1              | ∞
   |                |
   ∞                1
   |                |
[ Team ] ─────── [ Project ]

3. 画面遷移図

ログイン画面
    ↓
ホーム画面 → タスク一覧 → タスク詳細
    ↓           ↓           ↓
設定画面    新規作成     編集画面

これらの図を描くことで、全体像が見えるようになります。

そして重要なのは、コードを書く前にチームでこの図を共有し、議論することです。

「この処理、こっちの方が効率的じゃない?」
「将来的にこの機能を追加する可能性があるから、ここは拡張しやすく設計しよう」

こうした議論を、コードを書く前にすることで、後戻りを防げます。

習慣2:「設計パターンを学び、使いこなす」

設計パターンは、先人たちの知恵の結晶です。

よくある問題に対する、実証済みの解決策がパターン化されています。

私がよく使う設計パターン(Flutter/PHP共通)

1. Repository パターン

  • データの取得元(API、ローカルDB、キャッシュ)を抽象化
  • データソースが変わっても、ビジネスロジックに影響しない

2. Factory パターン

  • オブジェクトの生成ロジックを分離
  • 環境(開発/本番)に応じて、適切なインスタンスを生成

3. Observer パターン(Pub/Sub)

  • イベント駆動の仕組みを実現
  • モジュール間の疎結合を保つ

4. Strategy パターン

  • アルゴリズムを切り替え可能にする
  • 例:決済方法(クレカ/PayPal/Apple Pay)の切り替え

実例:Swift製iOSアプリでのRepository パターン

// プロトコルで抽象化
protocol UserRepository {
    func getUser(id: String) async throws -> User
    func saveUser(_ user: User) async throws
}

// 本番環境用の実装
class RemoteUserRepository: UserRepository {
    func getUser(id: String) async throws -> User {
        // API通信でユーザー情報を取得
    }
}

// テスト用の実装(モック)
class MockUserRepository: UserRepository {
    func getUser(id: String) async throws -> User {
        // 固定のテストデータを返す
    }
}

// 使う側は、どちらの実装かを意識しない
class UserService {
    let repository: UserRepository
    
    init(repository: UserRepository) {
        self.repository = repository
    }
    
    func loadUser(id: String) async throws -> User {
        return try await repository.getUser(id: id)
    }
}

この設計により、テストが書きやすく、変更に強いコードが実現できます。

習慣3:「リファクタリングを習慣化する」

良い設計は、最初から完璧には作れません。

作りながら、改善し続けることが重要です。

私のリファクタリングルール

1. 3回同じコードを書いたら、共通化する

DRY原則(Don't Repeat Yourself)ですね。

悪い例:

// 各画面で同じバリデーションを書いている
if (email.isEmpty || !email.contains('@')) {
  return 'メールアドレスが不正です';
}

良い例:

// バリデーションを共通化
class Validators {
  static String? validateEmail(String? value) {
    if (value == null || value.isEmpty) {
      return 'メールアドレスを入力してください';
    }
    if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
      return 'メールアドレスの形式が正しくありません';
    }
    return null;
  }
}

2. 1つの関数/メソッドは20行以内を目指す

長い関数は、責任が多すぎるサイン。小さく分割しましょう。

3. 週に1回、「技術的負債の返済日」を設ける

新機能開発ばかりではなく、既存コードの改善に時間を割く日を作ります。

品質管理の観点からも、この習慣は非常に重要です。


システム構築を「家づくり」で理解する

設計の重要性を、もう少し深く「家づくり」の比喩で考えてみましょう。

ケース1:拡張性を考えた設計

悪い設計:
全ての部屋の壁が構造壁(取り外せない)

→ 将来、間取り変更ができない

良い設計:
必要最小限の構造壁で、あとは間仕切り壁

→ ライフスタイルの変化に対応できる

システムで言うと:

  • すべての機能が密結合している vs モジュール分割されている
  • PHPの巨大なモノリシックアプリ vs マイクロサービス的な設計

ケース2:メンテナンス性を考えた設計

悪い設計:
配管や配線がコンクリートに埋め込まれている

→ 修理するとき、壁を壊す必要がある

良い設計:
配管・配線を点検口からアクセスできるように設計

→ メンテナンスが容易

システムで言うと:

  • コードが複雑に絡み合っている vs 責任が明確に分離されている
  • グローバル変数だらけ vs 適切な依存性注入(DI)

ケース3:パフォーマンスを考えた設計

悪い設計:
水回りが家の四隅にバラバラに配置

→ 配管が長くなり、お湯が出るまで時間がかかる

良い設計:
水回りを近くに集約

→ 配管が短く、効率的

システムで言うと:

  • 毎回データベースに問い合わせる vs 適切なキャッシング戦略
  • N+1問題が発生 vs JOINやEager Loadingで最適化

実践:設計レビューの重要性

設計力を鍛える最も効果的な方法は、他者の設計をレビューすること、自分の設計をレビューしてもらうことです。

私がチームで実践している設計レビュー

設計レビューの流れ

1. 設計書の作成(実装前)

  • システム構成図
  • データベース設計(ER図)
  • API設計(エンドポイント一覧)
  • 画面遷移図

2. レビュー会の開催

  • 設計者がプレゼンテーション(15分)
  • 質疑応答(30分)
  • 改善案の議論(15分)

3. 設計書の修正

  • レビューで出た指摘を反映
  • 再度簡易レビュー

4. 実装開始

この流れで、実装後の大規模な手戻りを防ぐことができます。

設計レビューで聞くべき質問

拡張性:

  • この設計で、将来的に〇〇機能を追加できますか?
  • ユーザー数が10倍になっても対応できますか?

保守性:

  • 新しいメンバーが参加したとき、理解しやすい構造ですか?
  • バグが発生したとき、原因箇所を特定しやすいですか?

パフォーマンス:

  • ボトルネックになりそうな箇所はありますか?
  • データ量が増えても、レスポンス速度を維持できますか?

セキュリティ:

  • 個人情報の扱いは適切ですか?
  • 不正なアクセスを防げていますか?

こうした質問を通じて、設計の穴を事前に見つけることができます。


AIと共存する設計者としての心構え

AIは「提案者」、判断は人間が行う

ChatGPTに「このシステムの設計案を考えて」と聞くことは可能です。

実際、私も設計の壁打ち相手としてAIを活用しています。

私:「PHPで在庫管理システムを作ります。複数の倉庫、複数の商品、
    入出庫の履歴管理が必要です。データベース設計を提案してください」

ChatGPT: 「以下のようなテーブル構成を提案します...」

AIは、素晴らしい提案をしてくれます。

しかし、最終的な判断は人間が行う必要があります。

  • このプロジェクトの制約条件(予算、期間、チームのスキル)を考慮して実現可能か?
  • 将来の拡張計画を踏まえて、最適な設計か?
  • パフォーマンス要件を満たせるか?

これらの判断には、プロジェクト全体を俯瞰する視点が必要です。

「なぜその設計にしたのか」を説明できるか

良い設計者は、設計の意図を明確に説明できます

「なぜこのアーキテクチャを選んだのですか?」

この質問に、きちんと答えられるかどうか。

  • 「ChatGPTがそう言ったから」は、答えになりません
  • 「このプロジェクトの制約条件を考慮し、拡張性と保守性のバランスを取った結果、この設計が最適だと判断しました」というように、論理的に説明できることが重要です

まとめ:AI時代だからこそ、設計思考が差別化要因になる

技術トレンドは、猛スピードで変化します。

  • 5年前、Flutterはまだ登場していませんでした
  • 10年前、Swiftはまだありませんでした
  • 15年前、iPhoneすら存在していませんでした

しかし、「設計の原則」は変わりません。

  • 責任の分離(Single Responsibility Principle)
  • 開放/閉鎖原則(Open/Closed Principle)
  • 依存性の逆転(Dependency Inversion Principle)
  • 疎結合・高凝集

これらの原則は、何十年も前から存在し、今も有効です。

AIがコードを書く時代だからこそ、「なぜその設計なのか」を考え、説明できるエンジニアの価値が高まっています。

あなたがこれからできること

✅ 次の開発から、コードを書く前に図を描く習慣をつける
✅ 設計パターンの本を1冊読む(例:『増補改訂版 Java言語で学ぶデザインパターン入門』は言語を超えて役立ちます)
✅ チームで設計レビューの文化を作る
✅ AIを「壁打ち相手」として活用し、設計案を議論する

設計は、一朝一夕では身につきません。

でも、意識して取り組めば、確実に成長します。

そして、その積み重ねが、AI時代に生き残る、いや、活躍するエンジニアへの道なのです。


次回予告

第4回では、「AIと共存する時代の"チーム開発力"」をテーマにお届けします。

生成AIが進化しても、システム開発はチームで行うもの。
むしろ、AIツールが普及したからこそ、人間同士のコミュニケーションが重要になってきました。

  • GitHub Copilotを使ったチーム開発の変化
  • AIがいても必要な「伝わるドキュメント」の書き方
  • ベテランと若手、AIが交わる現場のリアル

実際のプロジェクト事例を交えて、お伝えします。


【AI時代を生き抜くシステムエンジニア論 - 連載目次】

  1. AI時代の到来と、SEの役割の変化
  2. AIに代替されないSEの基本「課題定義力」と「論理的思考」
  3. 変わる技術トレンド、変わらない「設計思考」(今回)
  4. AIと共存する時代の"チーム開発力"
  5. AIを味方につけるためのスキルマップ
  6. これからも変わらない"システムエンジニアの本質"

おすすめ記事

1

Skill50(スキルゴー)― 50代からの“見えるスキル”アプリ ー50代からのキャリアを強くする、あなた専用のスキル可視化ツール ー Skill50(スキルゴー)は、経験・実績・強みを“選ぶだけ” ...

2

はじめに:50代からのキャリアは、ここから加速する。 「この先、どう働いていけばいいんだろう?」 そんな不安を抱えながら、毎日の仕事をこなしていませんか? 50代は、キャリアの分岐点です。会社員として ...

3

2025年、AI技術はスマホアプリ開発において「なくてはならない存在」になりつつあります。特に**生成AI(Generative AI)**は、アプリの機能だけでなく、開発プロセスそのものを革新し始め ...

4

AIアプリ開発は「作って終わり」ではありません。特に ChatGPT API や 画像生成AI を利用する場合、APIコストがかかるため、収益化の仕組みを最初から考えておくことが重要です。 この記事で ...

5

ブログは、自分の考えや経験を発信できるだけでなく、正しく運営すれば収益を生み出すことができる魅力的なメディアです。特にWordPressは、世界で最も利用されているブログ作成ツールで、自由度が高く、将 ...

6

はじめに フリーランスや副業エンジニアとして活動する皆さん、限られた時間でより多くの高品質なコードを書く必要に迫られていませんか?2025年現在、AI技術の飛躍的な進歩により、個人開発者でも企業の開発 ...

-AI, SE