第3話 “変化に強い構造”を土台に改善していこう

# 前回までの記事
https://qiita.com/Siras-inc/items/841077184cfbe0aec291

https://qiita.com/Siras-inc/items/479858d147abcdb2523f

# 見た目は整理できた。でもまた積み上げて壊れる気がする

若手は、コードを眺めながらため息をついた。
前回、UIの中に詰め込んでいた処理を外に出し、見た目はだいぶスッキリした。

変数名も整えた。関数も分けた。
でも──どこか引っかかる。

>若手：「何がどこで起きてるのか、まだちゃんと整理できてないんですよね……。
またこれ、同じように積み上げてったら、どこかで崩れる気がして……」

師匠はゆっくりとうなずきながら言った。

>師匠：「動きじゃなくて、“責任”を分けて考えてみな。
>処理の順番より、“どこが何を担当してるか”の方が大事なんだよ。
>たとえば建設現場でも、配線工が土台つくってたらおかしいだろ？
>見た目が完成してても、責任がバラけてたら、あとから崩れるんだよ」

その瞬間、若手の中で“設計＝順番”という思い込みが崩れ始めた。
意味で分ける。責任で整理する。
それが、構造の設計というものなのかもしれない──

# useReducerで「どう変えるか」ではなく「なぜ変えるか」を整理

責任を分けろ。
順番より、「どこが何を担当しているか」が大事だ。

そう言われたものの、若手はまだモヤモヤしていた。

>若手：「意味のある構造……処理の順番じゃなくて、責任の整理……」

師匠はそんな若手の様子を見て、静かに言葉を添えた。

>師匠：「たとえば、“どう変わるか”のルールを一箇所にまとめると、整理しやすくなるぞ。
>変わる“意味”がそこに集まっていれば、見通しもつく」

若手はハッとしたように顔を上げた。

>若手：「……あ、それって、Reactでいうと useReducer ですね！」
>師匠：「Reactわからんから、コードで説明するな。“WHATで説明しろ”って言っただろ」

>若手：「……ですよね（切り替え）――“何を変えたいのか”を UIの外で決められるってことです」
>師匠：「まあ、それでええやろ」
>若手：「つまり、状態の“意味付け”を、UIから切り離せるんですよ」

さっそく useReducer を導入してみた。

UIは入力を受け取って dispatch を投げるだけ。
どう変えるかの判断や、状態の意味付けは reducer に集中。
UIは「言われた通りに見せるだけ」の役割に落ち着いた。

:::note info
修正したコード
:::

```
import { useReducer, useState } from 'react';

// ✅ 状態の“意味付け”と“更新ルール”をまとめる reducer（Transferの役割）
function todoReducer(todos: string[], action: { type: string; payload?: string }): string[] {
  switch (action.type) {
    case 'ADD_TODO':
      // 空文字は追加しない（意味のチェック）
      if (!action.payload || action.payload.trim() === '') return todos;
      return [...todos, action.payload.trim()];
    default:
      return todos;
  }
}

function App() {
  // ✅ 入力用の一時状態（入力Source）
  const [text, setText] = useState('');

  // ✅ useReducerでTodoリストの状態と更新ロジックを保持（意味のある状態）
  const [todos, dispatch] = useReducer(todoReducer, []);

  // ✅ UIから「追加してほしい」という命令だけを送る（判断は外へ）
  const handleAdd = () => {
    dispatch({ type: 'ADD_TODO', payload: text });
    setText('');
  };

  return (
    <div style={{ padding: '2rem' }}>
      <h1>Todoアプリ</h1>
      <div style={{ display: 'flex', gap: '0.5rem' }}>
        {/* ✅ ユーザー入力（Source） */}
        <input
          type="text"
          value={text}
          onChange={(e) => setText(e.target.value)}
          placeholder="やることを入力"
        />
        {/* ✅ 命令ボタン（出力ではなく、入力の起点） */}
        <button onClick={handleAdd} disabled={!text.trim()}>
          追加
        </button>
      </div>

      {/* ✅ Todo一覧の表示（Sink：出力専用） */}
      <ul>
        {todos.map((todo, idx) => (
          <li key={idx}>
            {/* ✅ “意味の確定”された状態の出力。判断や加工なし */}
            {todo}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
```

# 構造を“言葉で整理”しただけで、AIの出力も変わった

useReducer によって、若手は「状態の変化ルール」をUIの外に出し、
責任の整理という感覚を少しずつ掴み始めていた。

それでも、どこか“ピースが足りない”感覚が残っていた。

あるとき、若手はふと考えはじめた。

>若手：「UIで受け取って、reducerで変化を決めて、UIで表示する……。
>あれ、これって──入力、判断、出力……？」

頭の中にある構造を、紙に書き出してみる。
入力、変換、出力――
気づけば、**処理の流れではなく、“意味の流れ”**が整理されていた。

>若手：「……この構造って、名前つけるとしたら……？」
>師匠：「それが、構造化設計でいう“STS設計”ってやつだよ」

若手は驚き、少し戸惑いながら返す。

>若手：「STS……確かそれって、昔のCOBOLとか、古い基幹系で使われてた設計ですよね？
>今のオブジェクト指向とか、Reactとかのフレームワークには、もう合わないんじゃ……」

師匠はすぐに答えた。静かに、でも力強く。

>師匠：「何も変わらないんだよ。
>組み込みだろうが、WEB・スマホだろうが──。
>設計の本質は、時代がどんなに進んでも、変わらないんだよ」

若手は、思わず言葉を失った。
設計とは“ツールの話”ではなく、“考え方”の話だったのだ。

気を取り直し、若手は AI にこう投げてみた。

>「UIは表示専用。状態は“入力→変換→出力”に責任を分けて」

返ってきたコードは、以前とまるで違っていた。
責任が整理され、構造が見えていた。
なにより、「意味の流れ」がそのままコードに表れていた。

| 層       | 役割               | 具体例                         |
|----------|--------------------|--------------------------------|
| Source   | 入力・指示         | input, button, handleAdd       |
| Transfer | 状態の変化ルールや判断   | todoReducer + dispatch         |
| Sink     | 表示専用           | TodoList（propsで表示するだけ）|


>若手：「……構造を言葉にしただけで、AIのコードって、こんなに変わるんだ……。
>　　　 これが本来の設計なんですね。理解できた気がします」

師匠は一拍置いてから、静かに言った。

>師匠：「何言ってんの。まだ“設計の形”にはなってないよ」
>若手：「えっ……？」
>師匠：「Appメソッドの中、見てごらん。ひと目で“どこが何を担当してるか”見えないだろ。
>このコードだと、画面を上下にスクロールで何度も往復しないと、全体の意味がつかめないじゃないか。

>なんでそうなるかっていうと、責任がごちゃごちゃに混ざってるからなんだよ。
>他の人が見て理解しやすい構造ってのはな、“責任を分けて、ひと目で理解できる形”になって、初めて意味があるんだよ」

若手は、黙ってコードを見つめ直す。

意味は分けた。責任も整理した。でも──それが“見える形”になっていない。

そこにまだ、“読める設計”の壁があった。

# 変化に強い構造とは、“責任を並べて見える形”だった

責任を分けて、意味を整理して、useReducerも導入した。
でも――師匠の言葉で、若手は気づいた。

「構造」は、“分けただけ”では見えない。
“人に見える配置”になって初めて意味を持つ。

 

若手は、コードを整理し直した。
Appの中で「入力」「状態の意味付け」「表示」の責任を横に並ぶ形で再配置した。

・入力（InputForm）
・変換（todoReducer）
・出力（TodoList）

```
import { useReducer, useState } from 'react';
import TodoList from './TodoList'; // Sink（出力）コンポーネント

// Transfer：状態の意味付けと変化ルールを一箇所に集約
function todoReducer(todos: string[], action: { type: string; payload?: string }): string[] {
  switch (action.type) {
    case 'ADD_TODO':
      if (!action.payload || action.payload.trim() === '') return todos;
      return [...todos, action.payload.trim()];
    default:
      return todos;
  }
}

function App() {
  // Source：ユーザーの入力状態
  const [text, setText] = useState('');
  const [todos, dispatch] = useReducer(todoReducer, []);

  const handleAdd = () => {
    dispatch({ type: 'ADD_TODO', payload: text });
    setText('');
  };

  return (
    <div style={{ padding: '2rem' }}>
      <h1>Todoアプリ</h1>
      <div style={{ display: 'flex', gap: '0.5rem' }}>
        {/* Source：入力 */}
        <input
          type="text"
          value={text}
          onChange={(e) => setText(e.target.value)}
          placeholder="やることを入力"
        />
        <button onClick={handleAdd} disabled={!text.trim()}>
          追加
        </button>
      </div>

      {/* Sink：出力専用。propsで受け取り表示するだけ */}
      <TodoList todos={todos} />
    </div>
  );
}

export default App;
```

>若手：「……これなら、コードを読んだだけで“どういう構造か”が見えてきます」
>師匠：「そう。“責任がどこにあるか”がひと目で分かれば、あとから誰が読んでも怖くないんだよ」

実際、若手は自分で追加機能のイメージをしてみた。
どこに何を書けばいいかが、すぐに思い浮かぶ。
修正も追加も“迷いがない”構造になっていた。

>若手：「……やっと、“壊れにくいコード”の正体が分かってきました」

構造は、「あとから読める」「意味が伝わる」ためのもの。
それが、変化に強い＝育てられる設計の土台だった。


 # 次回の記事予告

https://qiita.com/Siras-inc/items/f400c1165fe57a12c4c2

ここまでお読みいただき、ありがとうございました。  
本記事では、AIを使って開発を進める中で直面した「設計不在」の怖さと、それを乗り越えるために必要な“伝わる設計”の視点について紹介しました。

次回は、師匠から教わった設計の基本――「STS設計（入力・変換・出力）」というシンプルで強力な考え方を軸に、実際にReactアプリの構造を改善していくプロセスを紹介します。

もし、AIの出力をそのまま使ってコードが混乱してきた方、自分の設計に不安を感じている方には、きっと役立つ内容になるはずです。ぜひご期待ください！

---

また、今回紹介した内容をより実践的に学びたい方には、以下のUdemy講座もおすすめです。

## Udemyコース（8,800円 → クーポンで割引中）

▶️ [AIとC#で極める！クリーンコードの技法（限定クーポン付き）](https://www.udemy.com/course/aic-hjke/?couponCode=C49C4B16FBE249DDFFDA)

- C#でクリーンコードと設計力を身につける実践講座  
- ChatGPTの活用方法や、伝わるコードの考え方を解説

## 出版書籍『あきらめない者たち』

▶️ [Amazonで見る](https://amzn.asia/d/dK37sIu)

- 技術の基礎からやり直すために、なぜ一歩勇気を振り絞れたのかのノンフィクション作品です。