第5話 責務の分離ができてたから、機能追加もテストもサクッといった

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

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

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

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

# 新たな機能追加──今までなら、崩れてたかもしれない

>「次、期日を入れてほしいって」

そう依頼された瞬間、若手は少しだけ手が止まった。
期日──ただの日付の追加に見えるが、「期限切れ表示」まで入れるとなると、一気にややこしくなる。

処理と表示の境界が曖昧になりやすい、地味に崩れやすい仕様だった。

 >若手：「前なら……これ追加しただけでコードぐちゃぐちゃだったかも。
期限判定とか、“いつやるべきか”のロジックが、全部UIの中に入ってたからな……」

 
今回は、変な不安がなかった。
どこで“判断”して、どこで“表示”すればいいか、もう**🗺️頭の中に地図**ができていた。

# 気づいたら、追加もテストもサクッと終わっていた

AIに構造化設計を使う指示して、コードを修正してみた。

:::note
トップ階層のコードのみ。階層のメソッドは省略
:::
```App.tsx
import { useState, useReducer } from 'react';
import OutputView from './OutputView';
import { todoReducer } from './todoReducer';

const App = () => {
  // 🔵 入力：ユーザー入力（テキストと期日）
  const [text, setText] = useState('');
  const [dueDate, setDueDate] = useState(''); // ← 🆕 期日入力を追加

  // 🟢 変換：状態と意味付け（reducer内で処理）
  const [todos, dispatch] = useReducer(todoReducer, []);

  const handleAdd = () => {
    if (text.trim() === '' || dueDate === '') return;
    dispatch({ type: 'add', payload: { text, dueDate } }); // ← 🆕 dueDateを含むpayload
    setText('');
    setDueDate(''); // ← 🆕 入力クリア処理
  };

  // 🟣 出力：UI構造（別コンポーネントに分離）
  return (
    <OutputView
      text={text}                     // 🔵 入力値
      onTextChange={setText}         // 🔵 入力イベント
      dueDate={dueDate}              // 🔵 入力値（期日）← 🆕 追加
      onDueDateChange={setDueDate}   // 🔵 入力イベント（期日）← 🆕 追加
      onAdd={handleAdd}              // 🟢 意味づけと状態更新の指示
      todos={todos}                  // 🟣 出力：変換結果（期限判断済）← 🆕 表示内容が変化
    />
  );
};

export default App;
```

期日入力は「入力」
期限切れかどうかの判定は「変換（判断）」
表示する文言は「出力」

やるべきことが自然とこの3つに分かれていた。
しかも、それぞれの責任が**コード上でも“見える形”**になっていた。

 若手は「入力」にフィールドを1つ追加し、
「変換」＝reducer に“期限切れかどうか”のロジックを追加し、
「出力」は、ただ表示を分けるだけだった。

 >「少なくとも“期日のロジック”は reducer に集まってるから、そこだけテストすればいい。
“期日のロジック”だけみると、UIをいちいち動かさなくても、判断の正しさは確認できるって、めっちゃ楽になった」

 **どこに何を書くか**がはっきりしているだけで、追加もテストもここまでスムーズになるとは思っていなかった。

 # 前の書き方だったら、絶対こうはいかなかった

 若手はふと思い返す。
以前のように、状態とロジックを UI にベタ書きしていたら、
こんなふうに冷静に追加なんてできなかった。

:::note
修正前コード
:::

```tsx:App.tsx（構造化されていない例）
import { useState } from 'react';

function App() {
  const [text, setText] = useState('');
  const [dueDate, setDueDate] = useState('');                  // 🟡 UIに状態が集中
  const [todos, setTodos] = useState([]);

  const handleAdd = () => {
    if (text.trim() === '' || dueDate === '') return;

    const today = new Date().toISOString().split('T')[0];      // 🟡 UIロジック内で期限判定
    const isExpired = dueDate < today;

    const newTodo = { text, dueDate, isExpired };              // 🟡 状態の意味付けもここで
    setTodos([...todos, newTodo]);

    setText('');
    setDueDate('');
  };

  return (
    <div style={{ padding: '2rem' }}>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="やること"
      />
      <input
        type="date"
        value={dueDate}
        onChange={(e) => setDueDate(e.target.value)}
      />
      <button onClick={handleAdd}>追加</button>
      <ul>
        {todos.map((todo, idx) => (
          <li key={idx}>
            {todo.text}（期限: {todo.dueDate}）
            {todo.isExpired && <span style={{ color: 'red' }}>期限切れ</span>}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
```

>「text も dueDate も、useState 増やして if 文増やして……」
「前はロジックの場所がバラバラで、書くのもテストするのも全部めんどくさかった……」
「コードもどんどん長くなって、画面を上下にスクロールしないと全体が追えなくなってきてた」
「これで、さらに画面部品とロジックが増えるってなったら……想像しただけでゾッとする」

今は違う。
>「少なくとも“期日のロジック”は reducer に集まってるから、そこだけテストすればいい」
「“期日のロジック”だけみると、UIをいちいち動かさなくても、判断の正しさは確認できるって、めっちゃラク……」
「コード全体はひと目で理解できるし」

ここまで迷いなく機能追加できたのは、設計が“根っこにあった”からだ。

# 設計が守ってくれる。“変化に振り回されない”構造とは、こういうことだった

若手は画面を閉じたあと、静かに深呼吸をした。
「正直、自分のコードを信じられたのは初めてかもしれない」

 
UIはそのまま。ロジックは reducer に集約されていて、
“どこに手を入れるか”がひと目でわかる。
その安心感が、作業スピードもテストのしやすさも、全部変えていた。
 

ただし、それは今の自分が書いたコードに限った話。

>「でも──これが、他の人にも伝わる設計になってるだろうか？」

師匠のあの言葉が、心に残っていた。

>師匠：「“設計の本番”は、自分以外の人と向き合ってから、なんだよ」

 
若手はそっとディスプレイを閉じた。

“次は、機能追加した設計がちゃんと伝わるか試してみたい”

次の挑戦に、静かに向き合おうとしていた──

 # 次回の記事予告

 https://qiita.com/Siras-inc/items/85826c49e776f0e73fba


ここまでお読みいただき、ありがとうございました。  
本記事では、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)

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