前回までの記事
とりあえずコードを整理してみたつもりだった
AIからもらったコードをそのまま使っていた若手は、
前回の反省を踏まえて、自力でリファクタリングに取り組んだ。
変数名を見直し、関数を分け、処理の順番も少し変えた。
とくにUIが煩雑だったので、「画面の中で状態も処理も完結させた」つもりだった。
師匠に画面を見せながら、少し誇らしげに言う。
若手:「とりあえず、整理してみました。UIの中でひと通り完結するようにしてあります」
修正前コード
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;
修正後コード
import { useState } from 'react';
function App() {
// 状態(state)がUIコンポーネント内部に集中している
const [text, setText] = useState('');
const [todos, setTodos] = useState<string[]>([]);
// 入力チェックもこの中に定義
const isInputValid = () => text.trim() !== '';
// Todo追加処理も直接ここで完結している
const handleAdd = () => {
if (!isInputValid()) return;
setTodos([...todos, text]);
setText('');
};
return (
<div style={{ padding: '2rem' }}>
<h1>Todoアプリ</h1>
<div style={{ display: 'flex', gap: '0.5rem' }}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="やることを入力"
/>
{/* 入力チェックの判定もここで直接実行 */}
<button onClick={handleAdd} disabled={!isInputValid()}>
追加
</button>
</div>
<ul>
{/* todos の表示も UI 内に直接展開 */}
{todos.map((todo, idx) => (
<li key={idx}>{todo}</li>
))}
</ul>
</div>
);
}
export default App;
師匠はしばらくコードを眺めていたが、手を組んだまま、静かにこう言った。
師匠:「……まぁ、見た目は多少は整ったけど」
若手は「よかった」と思いかけたが、その言い回しに、どこかモヤっとしたものが残った。
“整ったけど”──そのあとに続く言葉が、少し怖かった。
師匠の静かなひと言が刺さった
師匠は黙ったままコードを見ていた。
若手が自信をもって見せた、整理されたUIコンポーネントの中身。
それでも師匠は、顔色ひとつ変えずにぽつりとつぶやいた。
師匠:「……表示の場所で“判断”までしてるのは、まずいんだよ」
若手:「え?判断ですか?」
師匠:「……表示する責任の場所で、“これを追加していいか”みたいな判断の責任までやるのは、まずいんだよ」
若手は一瞬止まり、少し自信ありげに答える。
若手:「でも、UIの中でまとめた方が分かりやすくないですか?」
師匠は視線をコードから外さず、静かに言葉を続けた。
師匠:「それ、“処理のフロー”なんだよ。動かす順番を並べただけ。
フローチャートと同じで、それは“動的な設計”。悪くはないけど、それだけじゃ足りないんだよ。
本来の設計ってのは、“意味のある構造”をつくること。つまり、静的な設計なんだよ。
まず構造を決める。それから動きを組み合わせていく──順番が逆なんだよ」
若手は何も言い返せなかった。
「動かせるように並べただけ」。
言われてみれば、自分の設計は、まさにそれだった。
構造がない。意味も責任も、バラバラのままだった──
設計の“順番”と“責任”がバラバラだったと気づく
師匠の言葉が頭から離れなかった。
「処理のフロー」と「意味のある構造」──それは、自分がこれまで考えたことのない視点だった。
UIの中で状態を管理し、入力チェックをして、処理を分岐し、さらにそのまま画面に反映している。
それは、「何を表示するか」だけではなく、
「どれが正しい入力か」や「追加すべきかどうか」まで判断していた。
若手:「……僕、画面の中で全部やろうとしてました」
師匠はうなずきながら答える。
師匠:「それじゃ“構造”が見えないんだよ。UIに責任を背負わせすぎなんだ」
画面に責任を詰め込んだ結果、「何をどこで決めているのか」が読みにくくなり、
設計としても、他の人には伝わらないものになっていた。
若手はようやく、「処理の順番」ではなく、「責任の並び」として設計を見ることの大切さに気づき始めた。
責任を分けることで、設計が“読める”ようになる
若手は、コードの中に詰め込んでいた処理を一つひとつ外に出し始めた。
入力の妥当性チェック、状態の更新、そして表示――
これまでUIコンポーネントの中でまとめていたものを、責任ごとに分けていく。
UIは「言われたとおりに見せるだけ」にし、
「何を見せるべきか」「どんなときに追加するか」は、UIの外で決めるように変えた。
若手:「……これ、どこで何やってるか、ひと目で分かるようになりました」
再修正後のコード
import { useState } from 'react';
// UIの外にロジックを分離(責任を分ける)
function isInputValid(text: string): boolean {
return text.trim() !== '';
}
function addTodo(todos: string[], text: string): string[] {
return [...todos, text];
}
function App() {
const [text, setText] = useState('');
const [todos, setTodos] = useState<string[]>([]);
const handleAdd = () => {
if (!isInputValid(text)) return;
const updated = addTodo(todos, text);
setTodos(updated);
setText('');
};
return (
<div style={{ padding: '2rem' }}>
<h1>Todoアプリ</h1>
<div style={{ display: 'flex', gap: '0.5rem' }}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="やることを入力"
/>
<button onClick={handleAdd} disabled={!isInputValid(text)}>
追加
</button>
</div>
<ul>
{todos.map((todo, idx) => (
<li key={idx}>{todo}</li>
))}
</ul>
</div>
);
}
export default App;
そこで、ふと過去を思い出す。
以前のプロジェクトで、仕様の認識違いや不具合が繰り返されたこと。
レビューでも「この処理、なんでここにあるの?」と何度も聞かれたこと。
どれも「UIの中に全部詰めていたから」だったのかもしれない。
師匠:「出力は、“言われたとおりに見せるだけ”でいいんだよ。
「何をどう判断するかは、ちゃんと外に出しておかないと──伝わらないんだよ。
“責任を分ける”という設計の原則が、少しずつ自分の中に染み込んでいく。
コードの意味が見えるようになる。
誰かに説明できるようになる。
それが「読める設計」ということなんだ──
次回、「構造を並べ直す」という新たなステップへ。
“変化に強い構造”を目指して、設計の再構築が始まる──
次回の記事予告
ここまでお読みいただき、ありがとうございました。
本記事では、AIを使って開発を進める中で直面した「設計不在」の怖さと、それを乗り越えるために必要な“伝わる設計”の視点について紹介しました。
次回は、師匠から教わった設計の基本――「STS設計(入力・変換・出力)」というシンプルで強力な考え方を軸に、実際にReactアプリの構造を改善していくプロセスを紹介します。
もし、AIの出力をそのまま使ってコードが混乱してきた方、自分の設計に不安を感じている方には、きっと役立つ内容になるはずです。ぜひご期待ください!
また、今回紹介した内容をより実践的に学びたい方には、以下のUdemy講座もおすすめです。
Udemyコース(8,800円 → クーポンで割引中)
▶️ AIとC#で極める!クリーンコードの技法(限定クーポン付き)
- C#でクリーンコードと設計力を身につける実践講座
- ChatGPTの活用方法や、伝わるコードの考え方を解説
出版書籍『あきらめない者たち』
▶️ Amazonで見る
- 技術の基礎からやり直すために、なぜ一歩勇気を振り絞れたのかのノンフィクション作品です。