65
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

新人AI禁止令と、その結果の答え合わせ

Last updated at Posted at 2025-12-25

はじめに

こんにちは、和田です。いえらぶGROUPで開発部の執行役員を務めています。
弊社も例に漏れず、今年はAI活用に非常に注力してきました。Cursorを全エンジニアに導入し、テックリードにはClaude Codeを配布、業務効率化・実装スピード強化・精度向上を進めてきました。

そんな会社で、私はある新人エンジニアに対して「AIの使用を禁止する」という判断を下しました。
先日ちょっと話題になってましたね、こちらの彼の話です。

社内でも圧倒的なAI推進派の私がなぜそのような判断をしたのか。そして3ヶ月後、その新人はどう変わったのか。この記事では、私の目線からのある種答え合わせ的なつもりで経緯と結果をまとめてみました。

最初は「AIをどんどん使わせていた」

新人が入社した当初、私は彼にもCursorを使わせていました。なんならClaude Codeも使わせていました。理由はシンプルです。

  • 実装スピードが上がる(はず)
  • 最新のベストプラクティスを学べる(はず)
  • コード品質が向上する(はず)

実際、彼は完全未経験でしたがそれなりのスピードで機能を実装していきました。CRUD機能も、データベース操作も、それなりに「動くもの」を作ってきます。

「これはいい流れかもな~」と思っていました。最初は。

レビュー830件の衝撃

最初の大きな機能のコードレビューを行った時のことです。
レビューコメントは830件に達しました(実際は静的解析による自動コメントも含まれてます)。

主な問題点を挙げると:

  • Model層でHTML生成(MVCアーキテクチャを完全に無視)
  • デバッグコードが本番に混入(error_logが大量に残存)
  • 500行を超える大規模インラインJavaScript
  • テストコードが1つもない
  • 変数名や関数名が既存コードのパターンと全く異なる

実際に、Model層で直接HTMLを生成しているコードを見たときは、「あ、こいつAIや」と一目でわかりました。

Before: Model層でHTML生成(MVC違反)

<?php
class Product_Model_Service_AiDescriptionService
{
    public function generateCategorySelectHtml($keyword)
    {
        // ❌ Model層でHTMLを直接生成している
        $htmlOutput = '';
        foreach ($categories as $category => $items) {
            $htmlOutput .= '<div class="category-group">';
            $htmlOutput .= '<h3 class="category-title">カテゴリ: ' .
                           htmlspecialchars($category, ENT_QUOTES, 'UTF-8') . '</h3>';
            $htmlOutput .= '<ul class="product-list">';

            foreach ($items as $item) {
                $htmlOutput .= '<li class="product-item">';
                $htmlOutput .= '<input type="checkbox" name="products[]" value="' .
                               htmlspecialchars($item['sku'], ENT_QUOTES, 'UTF-8') . '">';
                $htmlOutput .= htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8');
                $htmlOutput .= '</li>';
            }
            $htmlOutput .= '</ul></div>';
        }
        return $htmlOutput;
    }
}

いやまあコードは「動く」んですよね。しかし、弊社ルールで推奨している保守性や拡張性、既存システムとの整合性という観点で見ると、「よし、ええから黙ってやり直せ」の状態でした。

「AIを神だと思っていました」

レビューしながら彼に
「なぜModel層でHTML生成をしたの?」
と聞いたんですが、ドストレートで衝撃でしたね。

「AIがそう実装してくれたので、正しいと思っていました」

「(うわぁ、ホントにこんなことあるんだぁ)」と思いました。

それに続けて
「正直、出力されたコードは自分が考えるより正しいと実際思っちゃう節があります。もはや神と変わんないですね。(誇張)」

「なるほどな~」というめちゃくちゃ腑に落ちました。やっぱりAIの出力を判断する「基礎」がないと、結構やばいことになりそうだなと。

AI禁止令

というわけで、「AI禁止!触ったら手が溶けると思え。自分の頭で考える筋トレタイム」 を命じました。

禁止期間中にやってもらったこと

  1. 公式ドキュメントを読む

    • フレームワークの公式ドキュメント
    • PHPUnit/PSRの仕様
  2. 既存コードを読む

    • 似た機能を実装した過去のコードを読み解く
    • コードの「パターン」を発見する
  3. 設計を言語化する

    • 実装前に設計書を書く
    • 責務分離を図で表現する

質問の質があっさり変わった

禁止前:

  • 「このエラーが出ます。どうすればいいですか?」
  • 「動きません」

禁止後:

  • 「この設計だと、Controllerに実装が集中しそうなんですが、Service層に切り出した方がいいですか?」
  • 「既存のProductHelperと今回のProductService、命名規則が違うんですが、どっちがいいですかね?」

禁止期間中の段階的フィードバック

あとは、単にAIを禁止しただけだと管理者として芸がないので(ほんとにそう思った)。週に1回のプロジェクト進捗フィードバックの際、段階的に実装を進めるという工夫をしました。

ステップ1: クラス名とファンクション名だけでレビュー

最初に行ったのは、クラス名とfunction名だけを書かせて、まずそこでレビューすることでした。

コードの中身は一切書かせず、「どんなクラスを作るのか」「どんなfunctionが必要か」だけを提出させる。これにより、実装の全体像だけに対してフィードバックできました。

それでもやはり最初は一発でOKは出ずでした。彼の理解では、全体設計が既存システムと噛み合っていなかったのです。でも、この段階で修正できたことで、後の手戻りを大幅に減らせましたな~と思います。

ステップ2: コメントアウトだけ書いてレビュー

クラス名とファンクション名が私の認識と合った後に行ったのが、function内の実装をコードではなくコメントアウトのみで書かせることです。

<?php
public function registerProducts($csvData)
{
    // CSVデータのバリデーション
    // バリデーションエラーがあれば例外をスロー
    // 各行のデータをループ処理
    //   既存商品をSKUで検索
    //   存在すれば更新、なければ新規作成
    //   AI生成された説明文を適用
    //   保存処理
    // 処理結果をログに記録
    // 成功した件数を返す
}

そのコメントアウトの内容(擬似コード的なもの)に対して、私がレビューを行います。要するに、処理の流れを追いかけるというイメージです。

ここでもやっぱり何度か修正が入って「この順番だと整合性が取れない」「このバリデーションはService層じゃなくてModel層でやるべき」みたいなやり取りが結構ありました。ここの認識が合って、ようやく実装開始です。

ステップ3: CSS・レイアウトを無視した実装をレビュー

しかし実装といっても今度はCSSやレイアウトをいったん全部無視した実装です。

あくまでも業務ロジックやデータの受け渡しに関してのみ注力させる。エンジニアがCSSを考慮し始めると、めちゃくちゃ時間がかかるというのは、私の体感としてありました。

「コチョコチョ見た目いじるよりまずは、まずは正しくデータが流れるか、ビジネスロジックが正しいか、それだけに集中して」

こっからは何となく普通のコードレビューという感じでしたが、やっぱりここまで段階的に話しているので、以前よりかなりスムーズになった手ごたえを覚えています。CSSのことを気にしなくていいので、彼も私も本質的な部分だけに集中できたというのもあります。

ステップ4: 最後にCSS・レイアウト

データの受け渡しや基本的なビジネスロジックの実装ができたタイミングで、ようやくCSSを使って全体レイアウトを整える作業に入ります。

当たり前っちゃ当たり前でしたが、レイアウトに集中すると既存のCSSクラスの命名規則やレイアウトパターンを理解を進みやすかったらしく、比較的スムーズに進みました。

段階的アプローチができたこともよかった

AIを使った実装をさせてしまうと、この一連の流れを途中で分断して対応することがかなり難しいよな という私自身の気づきも得られたと思っています。

AIは一度に全部出力してしまうため、「設計→ロジック→見た目」という段階的なフィードバックができません。クラス名も、function内のロジックも、CSSも、全部一気に出てくる。

それをレビューしてくれっていわれても、まじでどこから手をつけていいかわからないし、新人も「どこが本質的な問題なのか」を理解できません。

一つずつ丁寧にフィードバックできたという意味でも、AIを禁止した効果はあったと確信しています。

3ヶ月後のレビュー結果

そしてAI禁止期間を経て、彼が実装した「クーポン管理機能(仮)」は無事に、合格レベルでした。

改善されてたところ

1. 適切な責務分離(MVC原則の理解)

<?php
// Model層: データのみを返す
class Product_Model_Service_AiDescriptionService
{
    public function getCategoryCandidates($keyword)
    {
        return $categories; // 純粋なデータ配列
    }
}

// ViewHelper: HTMLを生成
class Product_View_Helper_CategoryProductList extends View_Helper_Abstract
{
    public function categoryProductList(array $categories)
    {
        $html = '';
        foreach ($categories as $category => $products) {
            $html .= $this->view->partial(
                'partials/category-products.phtml',
                ['category' => $category, 'products' => $products]
            );
        }
        return $html;
    }
}

2. DataProviderパターンを使用したユニットテスト

<?php
class Coupon_Model_Service_CalculatorTest extends TestCase
{
    public function provideCalculateDiscountCases(): array
    {
        return [
            '固定金額割引:通常ケース' => [
                'subtotal' => 10000,
                'discountType' => 1,
                'discountValue' => 500,
                'expected' => 500,
            ],
            'パーセント割引:端数切り捨て' => [
                'subtotal' => 1999,
                'discountType' => 2,
                'discountValue' => 15,
                'expected' => 299,
            ],
        ];
    }

    /**
     * @dataProvider provideCalculateDiscountCases
     */
    public function test_calculateDiscount($subtotal, $discountType, $discountValue, $expected): void
    {
        $result = $this->calculator->calculateDiscount($subtotal, $coupon);
        $this->assertSame($expected, $result);
    }
}

3. その他の改善点

  • ViewHelperによるビューロジック分離
  • 外部ファイル化されたCSS/JavaScript
  • EC特有のビジネスロジックの適切な実装

一番違ってたなーと思ったことは、実装内容についてのQAができたことですね。
理解が進んでいたことがよくわかる。

AI解禁、しかし使い方が変わった

その後、一部AI利用も解禁したんですが、AIとの付き合い方も以前と違っていたことは嬉しい産物です。

解禁前の使い方

  • AIに「クーポン機能を作って」と丸投げ
  • 出力されたコードをそのままコピペ
  • 動いたらOK

解禁後の使い方

  • フェーズを分けて指示

    • 「まずデータモデルの設計案を出して」
    • 「次にこの設計に対するテストケースを考えて」
    • 「その後で実装コードを出して」
  • AIの提案に自分でツッコミを入れる

    • 「このHTML生成はViewHelperに切り出すべきでは?」
    • 「この処理はService層の責務じゃない?」
  • 最終判断は自分が持つ

    • AIの提案を採用するか、自分で書き直すか、判断できる

手前味噌で言うのも恐縮なんですが、「私が彼とやっていたやり取りを、彼がAIとやっている」みたいに見えたことは嬉しかったです。

でもまた禁止するよ笑

とはいえです、いきなりなんでも理解が進んだかと言えばもちろんそうではなく、まだまだ知っておいてほしいことは山のようにあります。なので、またAI禁止を戦略的に発動すると思います笑。

  1. CRUD基本ができたら一度解禁
  2. 次はSOLID原則を学び、また禁止して実践
  3. デザインパターンを学び、また禁止して実践
  4. パフォーマンス最適化を学び、また禁止して実践

みたいな感じですね。まああまり詳細は詰めてないので、都度「そろそろ必要だな~」と思ったタイミングで部分的に制限をかけて行こうかなと。なんとかうまい区切りを見つけてサイクルを回したいところですね。

まとめ

まとめたらめっちゃ当たり前のことだったんですが、それでもやはりまとめます。

  1. 「AIを渡すだけ」では育成にならない

    • AIは強力な道具だが、使う側に基礎がなければ害になる
  2. 禁止か解禁かの二択ではない

    • 段階的に禁止と解禁を繰り返すことで、正しい使い方を身につける
  3. AI時代だからこそ「考える土台」を持った人を育てる

    • AIの出力を判断できる力が、これからのエンジニアには必須
  4. 質問の質が成長の指標

    • 「どうすればいいですか?」から「この設計で合っていますか?」への変化

「AIに依存せず、AIを活用する」エンジニアを育て、「事業を育てる面白さ」を早く一緒に体験できるメンバーが増えるといいなぁ~と思いながら、今日もレビューします。

おわり。

65
23
3

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
65
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?