10
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?

同じソースをAIに100回リファクタリングさせるとどうなるのか?

Last updated at Posted at 2025-07-30

背景

昔テレビで「床屋へ一日に何回も行くとどうなるのか」という企画を見たことがあり
それが元ネタ。AIの反復自動改善がどんな帰結を迎えるのか、興味本位で確かめてみた。

実験設計

対象コードはJavaで作ったFizzBuzz。
髪の毛が伸びて清潔感がない状態を表現して、意図的に処理を汚した状態でスタート。

public class FizzBuzz {
    public static void main(String[] args) {
        for (int i=1;i<=100;i++){
            if (i%3==0 && i%5==0){
                System.out.println("FizzBuzz");
            } else {
                if (i%3==0){
                    System.out.println("Fizz");
                } else {
                    if (i%5==0){
                        System.out.println("Buzz");
                    } else {
                        System.out.println(i);
                    }
                }
            }
        }
    }
}

リファクタリング用プロンプト

途中からバリエーションを入れると結果が変わるので、固定文言を投げ続ける
(英語のほうがブレが少ない)

Refactor this Java code to improve readability, maintainability, and testability.
Do not change its external behavior. Return only the code block.

日本語訳

このJavaコードをリファクタリングし、読みやすさ、保守性、テスト可能性を向上させてください。
外部動作を変更しないでください。コードブロックのみを返してください。

検証

Node.js + Gemini API (gemini-2.5-flash-lite) を利用
ループで1回前の出力を次回の入力にそのまま渡して、各実行結果をまとめてファイルに出力。
出力結果はGitへアップしてます。
java-result

結果

なんとなく想像はついてましたが1回目で長髪から短髪になるくらい改変されました。

    // 1回目
public class FizzBuzz {

    private static final String FIZZ = "Fizz";
    private static final String BUZZ = "Buzz";
    private static final String FIZZ_BUZZ = FIZZ + BUZZ;
    private static final int FIZZ_DIVISOR = 3;
    private static final int BUZZ_DIVISOR = 5;
    private static final int MAX_NUMBER = 100;

    public static void main(String[] args) {
        for (int i = 1; i <= MAX_NUMBER; i++) {
            System.out.println(generateFizzBuzz(i));
        }
    }

    /**
     * Generates the FizzBuzz output for a given number.
     *
     * @param number The number to process.
     * @return "FizzBuzz" if the number is divisible by both 3 and 5,
     *         "Fizz" if divisible by 3,
     *         "Buzz" if divisible by 5,
     *         otherwise the number itself as a String.
     */
    public static String generateFizzBuzz(int number) {
        boolean isDivisibleByFizz = isDivisibleBy(number, FIZZ_DIVISOR);
        boolean isDivisibleByBuzz = isDivisibleBy(number, BUZZ_DIVISOR);

        if (isDivisibleByFizz && isDivisibleByBuzz) {
            return FIZZ_BUZZ;
        } else if (isDivisibleByFizz) {
            return FIZZ;
        } else if (isDivisibleByBuzz) {
            return BUZZ;
        } else {
            return String.valueOf(number);
        }
    }

    /**
     * Checks if a number is divisible by a given divisor.
     *
     * @param number  The number to check.
     * @param divisor The divisor.
     * @return true if the number is divisible by the divisor, false otherwise.
     */
    private static boolean isDivisibleBy(int number, int divisor) {
        return number % divisor == 0;
    }
}

マジックナンバーが定数化して、メソッド分割がされてます。
docも追加されて保守性も上がってますね。
この時点で実務コードとしては十分読めるレベルです。

    // 2回目
    public static void main(String[] args) {
        printFizzBuzzUpTo(DEFAULT_MAX_NUMBER);
    }

    /**
     * Prints the FizzBuzz sequence for numbers from 1 up to the specified maximum number.
     *
     * @param maxNumber The upper limit of the sequence (inclusive).
     */
    public static void printFizzBuzzUpTo(int maxNumber) {
        for (int i = 1; i <= maxNumber; i++) {
            System.out.println(generateFizzBuzzOutput(i));
        }
    }

2回目では責務分割としてロジック生成と出力が分離されました。
エントリーポイントがより宣言的になっています。

以降は特に大きな変化なし

基本的には判定ロジックのバリエーションが切り替わるだけでした。

共通関数方式

private static boolean isDivisibleByBoth(int n) {
    return isDivisibleBy(n, FIZZ_DIVISOR) && isDivisibleBy(n, BUZZ_DIVISOR);
}
...
if (isDivisibleByBoth(number)) return FIZZ_BUZZ;

15倍数ショートカット

if (isDivisibleBy(number, FIZZ_DIVISOR * BUZZ_DIVISOR)) {
    return FIZZ_BUZZ;
}

ふつうの&&判定

if (isDivisibleBy(number, FIZZ_DIVISOR) && isDivisibleBy(number, BUZZ_DIVISOR)) {
    return FIZZ_BUZZ;
}

基本この3パターンのシャッフルが延々と続いて機能差はなし、
可読性もほぼ同等に思えます、違いを作ることだけが目的化した状態なのかもしれません。
あとは名前変更程度。

まとめ

概ね、予想はついていましたが
小規模のスクリプトは2〜3回でリファクタリングが打ち止め状態になり
以降は差分を作るための差分になりがちのようです。

大胆な設計変更とかが発生すれば、面白いなと思っていたのですが、
もっと色々な組み方が可能なテストコードにすればよかったかも?

別のソースコードでも試してみたいですね。

10
2
0

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
10
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?