生成コードを保守しやすくする:退屈なアーキテクチャのルール
退屈なアーキテクチャのルール(明確なフォルダ境界、一貫した命名、シンプルなデフォルト)を使って生成コードを保守しやすくする方法を学びましょう。

生成コードはなぜ保守が難しくなるのか
生成コードは日常の仕事を変えます。機能を作るだけでなく、短時間で大量のファイルを生み出すシステムを導くことになります。速さは本物ですが、小さな不一致が急速に増殖します。
生成された出力は単体では問題なさそうに見えます。コストが現れるのは2回目、3回目の変更時です:そのコードがどこに属するか分からない、同じ振る舞いを二箇所で直す羽目になる、あるいは影響範囲が分からず触れたくないファイルが出てくるなどです。
「賢い」構造は予測が難しく高コストになります。カスタムパターン、隠れたマジック、過度な抽象化は初日には理にかなって見えますが、6週目には次の変更が遅くなります。AI支援の生成では、その賢さが将来の生成を混乱させ、ロジックの重複や上に積み重なる新しい層を生むこともあります。
退屈なアーキテクチャはその逆です:明瞭な境界、平凡な名前、分かりやすいデフォルト。完璧を目指すわけではありません。疲れたチームメイト(または将来の自分)が30秒で理解できるレイアウトを選ぶことです。
シンプルな目標は:次の変更を簡単にすること、かっこよくすることではありません。通常は、UI・API・データ・共有ユーティリティそれぞれに明確な置き場所があり、ファイルの役割に合った予測可能な名前を付け、自動配線や隠れたグローバル、メタプログラミングのような“魔法”を最小化することです。
例:Koder.ai に「チーム招待」を追加するよう頼むとき、UI は UI エリアに、API ルートは API エリアに、招待データはデータ層に格納されるようにしてほしい――その機能だけのために新しいフォルダやパターンを発明してほしくない。そうした退屈な一貫性が将来の編集コストを低く保ちます。
退屈なアーキテクチャのルール
生成コードは同じことをする方法が多いほど高コストになります。退屈なアーキテクチャのルールは単純です:最初の構築が少し地味でも、次の変更が予測可能になること。
次の問いに素早く答えられることが理想です:
- この機能はどこにある?
- 新しいファイルはどこに置く?
- 何と名付ける?
- UI からデータへの最短経路は?
ルール
一つの平凡な構造を選び、どこでもそれに従うこと。ツールやチームメイトが凝ったパターンを提案したら、実際の痛みを取り除かない限りデフォルトで「ノー」と答えます。
長期的に持つ実践的なデフォルト:
- フォルダやファイルは一つの責務だけ。もしファイルに変更理由が二つあるなら分割する。
- 予測可能性は柔軟性に勝る。同じ種類のものは毎回同じ場所に置く。
- スタックの標準的なやり方を、カスタムミニフレームワークより優先する。
- ハッピーパス(基本的な流れ)を明らかにする。新しい参加者が場所を当てられるようにする。
- 「魔法」を拒否する。隠れた挙動やリフレクション重視のトリック、巧妙すぎる抽象化は避ける。
簡単なメンタルテスト
新しい開発者がリポジトリを開いて「サブスクリプションをキャンセルする」ボタンを追加する必要があると想像してください。彼らはカスタムアーキテクチャを学ぶ必要はありません。明確な機能領域、UI コンポーネント、単一の API クライアントの場所、単一のデータアクセス経路が見つかるべきです。
このルールは Koder.ai のようなビブコーディングツールと特に相性がいい:高速に生成できても、毎回同じ退屈な境界に出力を誘導すれば保守が効きます。
スケールするシンプルなフォルダ境界
生成されたコードは急速に増えがちです。保守性を保つ最も安全な方法は、誰でもどこに変更が属するか推測できる退屈なフォルダマップです。
多くの Web アプリに適する小さなトップレベルのレイアウト例:
app/スクリーン、ルーティング、ページレベルの状態components/再利用可能な UI 部品features/機能ごとのフォルダ(billing, projects, settings など)api/API クライアントコードとリクエストヘルパーserver/バックエンドのハンドラ、サービス、ビジネスルール
これで境界は明白になります:UI は app/ と components/、API 呼び出しは api/、バックエンドロジックは server/ に集まります。
データアクセスも退屈に保ちましょう。SQL クエリやリポジトリコードはバックエンドの近くに置き、UI ファイルに散らさないこと。Go + PostgreSQL の場合、シンプルなルールは:HTTP ハンドラがサービスを呼び、サービスがリポジトリを呼び、リポジトリが DB と話す、です。
共有型やユーティリティには明確な場所を与えつつ小さく保つ:共通の型は types/(DTO、列挙、共有インターフェース)、小さなヘルパーは utils/(日付フォーマット、簡単なバリデータ)に入れる。もし utils/ が第二のアプリのようになり始めたら、そのコードはむしろ機能フォルダに置くべきです。
生成コードと手書きコードの違い
生成されたフォルダは置き換え可能と見なすべきです。
- 生成出力は
generated/(またはgen/)に置き、直接編集を避ける。 - カスタムロジックは
features/やserver/に置き、再生成で上書きされないようにする。 - 生成された挙動にパッチを当てる必要がある場合は、源を編集するのではなくアダプタでラップする。
例:Koder.ai が API クライアントを生成するなら generated/api/ に置き、api/ に薄いラッパーを書いて再試行やログ、分かりやすいエラーメッセージを追加し、生成ファイルには触らないようにします。
混乱を防ぐ命名規則
生成コードは簡単に増えます。名前付けが、1か月後に読みやすさを保ちます。
一つの命名スタイルを選び混ぜない:
- フォルダとファイル:
kebab-case(例:user-profile-card.tsx,billing-settings) - React コンポーネント:
PascalCase(例:UserProfileCard) - 関数・変数:
camelCase(例:getUserProfile) - 定数:
SCREAMING_SNAKE_CASE(例:MAX_RETRY_COUNT)
「役割」で名付ける、現在の実装方法で名付けない。user-repository.ts は役割名。postgres-user-repository.ts は実装の詳細で、将来変わり得る。複数の実装が本当にある場合にのみ実装サフィックスを使う。
misc や helpers、巨大な utils のようなジャンクドロワーは避ける。関数が一つの機能でしか使われないならその機能の近くに置く。共有されるなら機能を説明する名前(date-format.ts、money-format.ts、id-generator.ts)を付け、モジュールは小さく保つ。
ナビゲーションを速くする規約
ルート、ハンドラ、コンポーネントがパターンに従うと検索なしで見つけられます:
- ルート:
routes/users.ts、パスは/users/:userIdのように - ハンドラ(HTTP):
handlers/users.get.ts,handlers/users.update.tsのように - サービス(ビジネスルール):
services/user-profile-service.ts - データアクセス:
repositories/user-repository.ts - UI コンポーネント:
components/user/UserProfileCard.tsx
Koder.ai(または任意のジェネレータ)を使う場合、これらのルールをプロンプトに入れて生成と編集の間で一貫させましょう。ファイル名を予測できれば将来の変更コストは安くなります。
工夫を排したデフォルト(経験則)
生成コードは初日は見栄えが良くても30日後に苦労を生むことがあります。コードが明白になるデフォルトを選び、多少冗長でも読みやすさを優先します。
まず魔法を減らす。動的ロード、リフレクション風トリック、自動配線は必須でない限り避ける。これらは要素の発生源を隠し、デバッグやリファクタを遅らせます。
明示的なインポートと明確な依存関係を優先する。ファイルが何かを必要とするなら直接インポートする。モジュールの配線が必要なら、1つの目に見える場所(例えば単一の構成ファイル)で行う。何が先に動くか推測させないこと。
設定は退屈で中央集権的に保つ。環境変数、機能フラグ、アプリ全体の設定は一つのモジュールに同じ命名規約でまとめる。便利さから設定をランダムなファイルに散らすのは避ける。
一貫性を保つ経験則:
- 暗黙より明示を選ぶ(インポート、ルーティング、DI、副作用)。
- 10 行節約して新しい概念が増えるならやめる。
- やり方は一つにする(ログ、設定モジュールは一つ)。
- 隠れたオブザーバやイベントチェーンより単純なデータフローを好む。
- デバッグ時はまず巧妙なコードを削除してみる。
エラーハンドリングは賢さが最も害する領域です。パターンを一つに絞りどこでも同じにする:データ層は構造化されたエラーを返し、それを一箇所で HTTP レスポンスにマッピングし、UI 境界でユーザー向けメッセージに翻訳する。ファイルによって三種類のエラーを投げ分けない。
Koder.ai でアプリを生成する場合、最初にこれらのデフォルトを求めておくと良い:明示的なモジュール配線、集中化された設定、一貫したエラーパターン。
UI、API、データの境界
UI、API、データの明確なラインは変更の影響範囲を限定します。多くの難しいバグは一つの層が別の層の仕事を始めると発生します。
UI:状態を表示し入力を集める
UI(多くは React)は画面を描画し UI 固有の状態を管理する場所として扱いましょう:どのタブが開いているか、フォームエラー、読み込みスピナー、基本的な入力処理など。
サーバ状態は分離する:取得したリスト、キャッシュされたプロファイル、バックエンドと一致すべきもの。UI コンポーネントが合計を計算したり複雑な検証を行ったり権限を判断し始めると、ロジックが画面に散らばり変更が高コストになります。
API:薄く、安定した形
API 層は予測可能に保つべきです。HTTP リクエストをビジネスコード呼び出しに翻訳し、結果を安定したリクエスト/レスポンス形に戻す役目です。データベースモデルをそのままワイヤーに流さないように。
シンプルな経路:
- UI は型付きのリクエスト/レスポンスオブジェクトで API クライアントを呼ぶ。
- API ハンドラは入力を検証しサービスメソッドを呼ぶ。
- サービスはビジネスルールとワークフローを保持する。
- リポジトリはデータベースクエリを小さなメソッドで隠す。
データ:クエリはリポジトリの背後に隠す
SQL(または ORM)のロジックはリポジトリ境界の背後に置き、アプリの他部分が「どう保存されているか」を知らないようにします。Go + PostgreSQL では、通常 UserRepo や InvoiceRepo のようなリポジトリを用意し、GetByID、ListByAccount、Save のような小さなメソッドを持たせます。
具体例:割引コードを追加する場合。UI はフィールドを表示し更新後の価格を出す。API は code を受け {total, discount} を返す。サービスがコードの有効性と割引の重複ルールを決め、リポジトリが必要な行を取得・永続化します。
ステップバイステップ:保守しやすい生成コードを整える
生成されたアプリは短時間で「できた」ように見えますが、構造が後で変更コストを左右します。先に退屈なルールを決め、その後に必要十分なコードを生成して検証します。
実践的なセットアップの流れ
短い計画パスから始めます。Koder.ai を使うなら Planning Mode でフォルダマップといくつかの命名ルールを書いておくと良いです。
次にこの順序で進めます:
- マップとルールを文書化する。 境界(例:
ui/,api/,data/,features/)と数個の命名ルールを選ぶ。 - 薄い垂直スライスを一つ生成する。 UI、API、ストレージを触る小さな機能(例:「コンタクト作成」)を選ぶ。目的はエンドツーエンドの流れを示すことで完全性ではない。
- 即座にリファクタして境界に合わせる。 コードを予定したフォルダへ移し、不明瞭なファイル名を変更し、重複を削除する。何でもやってしまう関数は UI、ハンドラ、データアクセスに分割する。
- 二つ目の機能で形をテストする。 似た機能(例:「コンタクト一覧」)を追加してみる。ルールを破らざるを得ないなら境界が間違っている可能性が高い。
- 早めに規約を固定する。 短い
CONVENTIONS.mdを追加し契約のように扱う。コードベースが大きくなると命名やフォルダパターンの変更は高コストになる。
現実チェック:新しい人が「編集コンタクトをどこに置くか」を尋ねずに推測できないなら、まだ退屈すぎるアーキテクチャではありません。
例:機能を追加しても混乱させない方法
単純な CRM を想像してください:コンタクト一覧ページとコンタクト編集フォームがある。最初のバージョンを素早く作り、一週間後に「タグ」を追加する必要が出たとします。
アプリを UI、API、データの三つの退屈な箱として扱います。各箱に明確で文字通りの名前を付ければ「タグ」の変更は小さく済みます。
きれいなレイアウトの例:
web/src/pages/ContactsPage.tsxとweb/src/components/ContactForm.tsxserver/internal/http/contacts_handlers.goserver/internal/service/contacts_service.goserver/internal/repo/contacts_repo.goserver/migrations/
こうすれば「タグ」は予測可能になります。スキーマを更新(contact_tags テーブルや tags カラム追加)し、各レイヤーを順に触るだけ:リポジトリがタグを読み書きし、サービスが検証し、ハンドラがフィールドを公開し、UI が表示・編集する。SQL をハンドラに忍び込ませたり、React にビジネスルールを入れたりしないこと。
テストとフィクスチャも小さくコード近くに置く:
server/internal/service/contacts_service_test.go(例:「タグ名は同一のコンタクト内で一意である」等のルール)server/internal/repo/testdata/(最小限のフィクスチャ)web/src/components/__tests__/ContactForm.test.tsx(フォーム挙動)
Koder.ai で生成した場合でも、エクスポート後に同じルールが当てはまります:フォルダを退屈に保ち、名前を文字通りにしておけば編集が考古学的作業になりません。
将来の変更を高コストにするよくある失敗
生成コードは初日はきれいに見えても後で高コストになることがあります。主な原因は「一貫性の欠如」です。
高コストな習慣の一つは、毎回ジェネレータに構造を発明させることです。機能ごとに独自のフォルダ、命名スタイル、ヘルパーが入り混じり、同じことをする方法が三通りになることがあります。一つのパターンを選び、それを書き留め、新しいパターンは意図的な変更として扱いましょう。
別の罠は層の混在です。UI コンポーネントが DB に直接話す、あるいは API ハンドラが SQL を組み立てると、変更がアプリ全体に跨る危険な編集になります。境界を保つ:UI は API を呼び、API はサービスを呼び、サービスはデータアクセスを呼ぶ。
早すぎる汎用抽象化もコストを増やします。普遍的な BaseService や Repository フレームワークはかっこよく感じますが、初期の抽象化は推測であり、現実が変わると自分の作ったフレームワークと格闘することになります。
頻繁なリネームや再編成も静かな負債です。ファイルが毎週動くなら人はレイアウトを信頼しなくなり、気軽な修正がランダムな場所に入ります。まずフォルダマップを安定させ、その後計画的にリファクタしてください。
最後に、実際にユーザーがいない「プラットフォームコード」は注意深く。共有ライブラリや自作ツールは繰り返しの需要があるときにだけ価値を発揮します。それまでは直接的なデフォルトを維持しましょう。
出荷前の簡単チェックリスト
新しい人がリポジトリを開いたとき、素早く答えられるべき質問:"これをどこに追加する?"
2分ナビゲーションテスト
プロジェクトを同僚(または将来の自分)に渡し、「サインアップフォームにフィールドを一つ追加して」と頼んでみてください。すぐに適切な場所が見つからなければ構造は機能していません。
三つの明確な置き場所を確認:
- UI 変更は一つの明らかな場所にある。
- UI から辿れる API ルート/ハンドラが見つけやすい。
- データモデルや DB 変更の場所が明確。
レビューで守るべきルール
- UI、API、データにはそれぞれ居場所があり、例外は稀。
- 名前はラベルのように読めて謎めいていない。
- 層をまたぐ行為はフラグ化する(例:UI が DB にアクセスする)。
- 巧妙なショートカットはデフォルトで却下する。
プラットフォームがサポートするならロールバックパスを保持しましょう。構造を試す時にスナップショットとロールバックは特に有用です。
次のステップ:退屈に保ち、コストを下げる
保守性が最も速く改善するのは、スタイルを議論するのをやめ、いくつかの決定をしてそれを守ることです。
どこにファイルを置くか、どう名付けるか、エラーと設定をどう扱うかといった日々の迷いを取り除く小さな規約を書き留めてください。一分で読めるくらい短く保ちます。
それから一回のクリーンアップでルールに合わせ、毎週の再編成をやめること。頻繁な再編成は見た目がよくても次の変更を遅くします。
もし Koder.ai(koder.ai)で構築しているなら、これらの規約を起点プロンプトとして保存しておくと各生成が同じ構造に落ち着きます。ツールは速く動けますが、退屈な境界がコードを変更しやすく保つのです。
よくある質問
生成コードにおける「退屈なアーキテクチャ」とは何ですか?
プレーンなフォルダ構成、文字どおりの名前、UIからAPI、データまでの一本の経路を使うことです。独自パターンを先に学ばなくても、誰でも変更箇所を正しく置ける状態を目指します。
AI生成コードでは、なぜ保守が難しくなるのですか?
生成コードは短時間でファイルを増やせるため、小さな不整合がすぐに広がります。予測しやすい構造なら、2回目、3回目の変更で重複したロジックや見慣れないフォルダを探し回らずに済みます。
生成されたWebアプリでは、どのフォルダを使うべきですか?
実用的な出発点としては、画面とルーティングに app/、再利用可能なUIに components/、機能固有のコードに features/、クライアントリクエストに api/、バックエンドロジックに server/ を使います。すべての機能で同じ境界を保ちます。
生成されたファイルとコードは、どのように命名すべきですか?
一貫して1つのスタイルを使います。ファイルとフォルダはkebab-case、ReactコンポーネントはPascalCase、関数はcamelCase、定数はSCREAMING_SNAKE_CASEです。user-repository.ts や money-format.ts のように、役割でファイル名を付けます。
UI、API、データベースのコードは、どのように分けるべきですか?
UIコードは表示と入力に集中させ、APIハンドラはリクエストを検証して安定したレスポンスを返し、ビジネスルールはサービスに置き、SQLやORMの呼び出しはリポジトリに置きます。各層は自分で次の層の仕事をせず、次の層を呼び出します。
生成されたファイルを直接編集すべきですか?
生成物は専用の generated/ または gen/ フォルダに置き、直接編集しないようにします。カスタムのラッパーやアダプターはそのフォルダの外に書けば、変更を失わずにコードを再生成できます。
避けるべき「巧妙な」パターンは何ですか?
隠れたグローバル、自動配線、動的ロード、数行を減らすためだけに新しい概念を持ち込む抽象化は避けます。直接インポート、集中管理された設定、見える形のモジュール設定を優先します。
保守しやすい生成プロジェクトは、どのように立ち上げればよいですか?
連絡先を作成するような、小さなエンドツーエンド機能から始めます。生成後、分かりにくいものを計画したフォルダへ移し、重複を削除します。その後、似た2つ目の機能を作り、構造がまだ自然に感じられるか確認します。
Koder.aiの出力を一貫させるにはどうすればよいですか?
変更のたびに、フォルダ構成、命名スタイル、層の境界、エラーハンドリングのパターンを明示的なルールとしてジェネレーターに伝えます。Koder.aiでは、これらのルールを計画用プロンプトに保存すれば、新しい作業も同じ構造に従います。
プロジェクトが変更しやすいかどうかは、どのように確認できますか?
案内なしで、サインアップ項目のような小さな変更を追加してもらいます。UI、APIハンドラ、サービス、データの場所をすぐ見つけられるなら、そのプロジェクトは扱いやすい状態です。見つけられないなら、さらに大きくなる前に構成を簡素化します。