1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-05-08

前回までの記事

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

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

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

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

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

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

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

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

トップ階層のコードのみ。階層のメソッドは省略

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 にベタ書きしていたら、
こんなふうに冷静に追加なんてできなかった。

修正前コード

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 に集約されていて、
“どこに手を入れるか”がひと目でわかる。
その安心感が、作業スピードもテストのしやすさも、全部変えていた。

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

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

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

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

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

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

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

次回の記事予告

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

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

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


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

Udemyコース(8,800円 → クーポンで割引中)

▶️ AIとC#で極める!クリーンコードの技法(限定クーポン付き)

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

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

▶️ Amazonで見る

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?