第1話 AIでReactアプリを一気に作った。でも、設計不在で機能を追加していく未来にゾッとした

>師匠：「動くコードは誰でも作れるが、“役割の形”がどこにもないからだよ」

# 前任不在、急な無茶ぶり。AIで爆速開発したけれど──

そのプロジェクトは、すでに継続が危ぶまれる状態だった。

要件は曖昧、スケジュールは逼迫。
仕様が決まらないまま、実装だけが進められる。

上司も余裕がなく、会話はいつも指示だけ。
そして追い詰められたように、前任のフロントエンド担当が、ある日突然いなくなった。

それでもプロジェクトは止まらない。

>「君、React触れるよね？」

上司にそう言われて、若手の自分にすべてが降ってきた。

設計も方針も分からない。
ただ“早く作れ”という空気だけが漂っていた。

>若手（心の声）：「……無理だろ、これ……」

唯一の救いは、「AI使っていい」という社内ルールだった。
自分に残された時間も、気力も、決して多くはなかった。

試しにChatGPTに打ち込んだ。

「ReactでTodoアプリ作って」

ほんの数秒でコードが返ってきた。
コピペして実行すると、ちゃんと動く。
フォームもボタンも、リストも整っている。

>若手：「……これ、本当に使えるんじゃないか……？」

興奮気味に追加機能も投げ込んだ。
完了チェック、削除、残数カウント……
ChatGPTは言えば言うだけ応えてくれる。

誰も教えてくれない現場で、唯一“助けてくれる存在”のように思えた。

>若手：「……もう、開発ってこういう時代なんじゃないか？」

けれど、ふとした瞬間に違和感が刺す。

似たような処理があちこちに散らばっていく。
コードを読むたび、「これ、どこで何してるんだっけ？」と手が止まる。

>若手：「このまま機能や処理を積み重ねたら……」
「いつか“触るのが怖いコード”になってしまう……」

目の前にあるのは、“今は動くアプリ”。
でもその裏側に広がるコードは、
かつて自分がプロジェクトで経験した“破綻の兆し”と、どこか似ていた。

:::note info
作成した画面
:::

![Todoアプリ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4079641/05009694-da6c-4754-8fd0-6f93d6e8a0ec.png)

:::note info
作成したコード
:::

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

function App() {
  const [text, setText] = useState('');
  const [todos, setTodos] = useState<string[]>([]);

  const handleAdd = () => {
    if (text.trim() === '') return;
    setTodos([...todos, text]);
    setText('');
  };

  return (
    <div style={{ padding: '2rem' }}>
      <h1>Todoアプリ</h1>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="やることを入力"
      />
      <button onClick={handleAdd}>追加</button>
      <ul>
        {todos.map((todo, idx) => (
          <li key={idx}>{todo}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;
```

# 機能が増えると、コードが長く複雑化して不安が募る

締切・優先度・並び替えなど、よくある仕様を追加し始めると、
useState や useEffect が画面中にどんどん増えていった。

 
最初はコピペで済んでいた処理も、だんだん違いが出てきて、
似たようなロジックがあちこちに散らばるようになった。

 変更するたびに、「あれ、ここも直すんだっけ？」と不安になる。
気づけば、「どこで何をしてるのか」が自分でも把握しきれなくなっていた。


>「今は動いてる。でも……もう全体が読めない」
>「手を入れるたびに、自分の書いたコードが怖くなる」

:::note info
機能追加した画面
:::

![Todoアプリ機能追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4079641/df307a25-1cbe-4e92-9f67-904d3a35e86d.png)


:::note info
作成したコード
:::

```
import React, { useState, useEffect } from 'react';

type Todo = {
  text: string;
  done: boolean;
};

const TodoApp: React.FC = () => {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [newTodo, setNewTodo] = useState('');
  const [filter, setFilter] = useState<'all' | 'active' | 'completed'>('all');
  const [sorted, setSorted] = useState(false);
  const [filteredTodos, setFilteredTodos] = useState<Todo[]>([]);

  useEffect(() => {
    let result = [...todos];

    if (filter === 'active') {
      result = result.filter(todo => !todo.done);
    } else if (filter === 'completed') {
      result = result.filter(todo => todo.done);
    }

    if (sorted) {
      result.sort((a, b) => a.text.localeCompare(b.text));
    }

    setFilteredTodos(result);
  }, [todos, filter, sorted]);

  const addTodo = () => {
    if (newTodo.trim() === '') return;
    setTodos([...todos, { text: newTodo, done: false }]);
    setNewTodo('');
  };

  const toggleTodo = (index: number) => {
    const updated = [...todos];
    updated[index].done = !updated[index].done;
    setTodos(updated);
  };

  const deleteTodo = (index: number) => {
    const updated = [...todos];
    updated.splice(index, 1);
    setTodos(updated);
  };

  return (
    <div>
      <h1>Todo List</h1>
      <input
        value={newTodo}
        onChange={e => setNewTodo(e.target.value)}
        placeholder="Add new todo"
      />
      <button onClick={addTodo}>Add</button>
      <div>
        <button onClick={() => setFilter('all')}>All</button>
        <button onClick={() => setFilter('active')}>Active</button>
        <button onClick={() => setFilter('completed')}>Completed</button>
        <button onClick={() => setSorted(!sorted)}>
          {sorted ? 'Unsort' : 'Sort'}
        </button>
      </div>
      <ul>
        {filteredTodos.map((todo, index) => (
          <li key={index}>
            <input
              type="checkbox"
              checked={todo.done}
              onChange={() => toggleTodo(index)}
            />
            {todo.text}
            <button onClick={() => deleteTodo(index)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoApp;

```

# “どこから手をつけていいか分からないコード”にぶつかったとき、頼る人は一人だった

過去、別のプロジェクトで、
不具合だらけのコードに誰もが匙を投げかけていたとき――


一人の技術者が、黙々とコードを読み解き、整理し、
機能を壊さずにリファクタリングを進めていった。


60代、酒好きでちょっと豪快。
でも書くコードは静かで、整っていて、まるで無駄がなかった。


その姿に、技術だけじゃない「人としての芯」を感じた。
そして何より、崩れていたコードが美しく整っていくあの体験が忘れられなかった。


>若手：「この人に、もっと近づきたい」

そう思って、弟子入りを願い出たのが、あのときだった。

# 再び師匠から教わる。キーワードは“WHATで説明しろ”

若手は迷いながらも、師匠に連絡を入れた。
すぐに「近くにいる」と返事があり、その日のうちに会うことになった。

カウンターの奥、酒を片手に師匠は待っていた。
ノートPCを開いて座ると、師匠が静かに言った。

>師匠：「……そのコード、見せてごらん」

機能を追加したコードではなく、シンプルな機能に戻したコードを師匠に見せた。

画面を見つめながら、師匠はスクロールを止めずに言う。

>「ひどいコードだなぁ。こりゃ」
「一見動いてるけど、誰も中身が追えなくなるやつだよ、これ」
「これじゃ、どこを直しても他に影響出そうで怖いだろ？」

若手は焦りながら、コードの内容を説明しようとするが──

>師匠：「ソースコードで説明するな。“WHATで説明しろ”って言っただろう？」

一瞬、言葉に詰まった。

>若手：「……WHATって、なんでしたっけ……？」

>師匠：「“何をしたいのか”だよ。それが設計の出発点なんだよ。コードはその手段にすぎない」

言われてみれば、自分は機能の処理やUIの操作の話ばかりしていた。

画面に書いたコードが、何のためにあるのか。
どこで意味を持ち、どう伝わるべきか。
それを考えていなかった。

若手はコードを見つめながら、ぽつりとつぶやく。

>若手：「……全部“処理の話”ばっかりしてた気がする……」

師匠はグラスを置き、ふと笑ってこう言った。

>師匠：「ちなみに俺、Reactのことは全く知らないんだけど」
>若手：「……えっ！？」

いや、使う言語知らなくて、設計ってできるの？

次回へ続く

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

---

# 次回の記事予告

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

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