見出し画像

口だけで開発したコードの改善余地を口に出してもらってわかる使い方の問題点

前回のコードを使用して改善案の提示をしてもらいます。もう少し口だけで開発できるかと思って進めていたのですが、うまくいかないケースが頻発したので、先にその説明をしてから進めます。

口だけ開発がうまくいかなくなるタイミング

モックアップまではサクサク進むのですが、細かい要望が出てきたときに、まったく対応できなくなりますので気を付けましょう。

前回からちょっと手を加えた画面

何がうまくいかないかというと

  • コードを提示してもらう毎に前回の出力結果を踏襲しない

    • 全コピペ前提で進めていると、どこが問題のあったコードかわからなくなります。

  • 長くなると出力しきれない

    • 上記にも影響を与えますが、「続けて」毎に違うコードや値が提示されます。コピペ作業に大きな影響を与えます。

  • エラー頻発かつトラブルシューティングに大きな影響がある

    • 提示されるコードや値に一貫性がなく、問題が発生します。また、機能が積みあがってくると、トラブルシューティングまではいいものの、解決策が妥当ではないことが増えてきます。細かくプルリクするような使い方からは程遠いです。

情報量が多くなるほど、文脈が重要になるので、意図をくみ取ってもらうために毎回全情報をインプットして、ステップバイステップで確認してもらう必要がありますが、一部を直したいのにすべてを確認しなければならないのは無駄です。その先の深い開発に進みたい場合は、実際にコーディングしたほうが圧倒的に速いです。

本題:コードの改善点をあげてもらおう

コピペ貼り付けだとChatGPTはシステム1の思考がごとく、思い付きで改善案を出してくるので、込み入ったものだと意図した結果が生まれません。そのため、システム2の思考のようにステップバイステップで確認してもらいましょう。精度の高い答えをもらうためには、まず必須といえるものです。

Chain-of-Thought PromptingとかGoalSeek Promptとか、Reverse Engineering Promptとか言われている、目的に対して要素を分解して改善点をあげて、目的に達成するまでループさせていくといったアプローチです。今回はループしません。

コードの場合、インプットに対するアウトプットは明確であり、変数要素はそこまで多くないので、ざっくりとしたものでも機能するはずです。

今回はOutput3まででOKです

インプットするとこんな感じで説明してくれます。

はい、お願いします。

ChatGPTに書いてもらったコードはこんな感じです。途中トラブルが続き、実装の提案が遠回りなような気がして、コードの品質を疑うようになっています。

Vue.config.productionTip = false;

document.addEventListener("DOMContentLoaded", function () {
  new Vue({
    el: "#app",
    data: {
      userMessage: "",
      messages: [
        {
          text: "こんにちは!チャットボットです。",
          time: getTimeString(),
          isUser: false,
          isBouncing: false
        },
      ],
      userInputDisabled: false,
    },
    methods: {
      sendMessage() {
        try {
          if (this.userMessage.trim() !== "") {
            if (this.userMessage.length > 140) {
              this.messages.push({
                text: "メッセージは140文字以下で入力してください",
                time: getTimeString(),
                isUser: false,
                isBouncing: false
              });
            } else {
              this.messages.push({
                text: DOMPurify.sanitize(this.userMessage),
                time: getTimeString(),
                isUser: true,
                isBouncing: false
              });

              this.userMessage = "";

              setTimeout(() => {
                this.messages.push({
                  text: `こんにちは!これはチャットボットの応答です。`,
                  time: getTimeString(),
                  isUser: false,
                  isBouncing: true
                });

                // それ以外の回答のisBouncingをfalseに設定
                this.messages.filter((msg, index) => index !== this.messages.length - 1)
                  .forEach(msg => msg.isBouncing = false);

                this.scrollToLatestMessage();
              }, 1000);
            }
          }
        } catch (err) {
          console.error(err);
        }
      },
      scrollToLatestMessage() {
        try {
          this.$nextTick(() => {
            const messagesWrapper = this.$el.querySelector(".messages-wrapper");
            messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
          });
        } catch (err) {
          console.error(err);
        }
      },
      isLastUserMessage(index) {
        const nextMessage = this.messages[index + 1];
        return !nextMessage || nextMessage.isUser;
      },
      isLastBotMessage(index) {
        if (index === this.messages.length - 1) {
          return true;
        }
        return this.messages[index + 1].isUser;
      },    
      buttonHover(event) {
        const submitButton = event.target;
        submitButton.classList.add('animated', 'heartBeat');
        setTimeout(() => {
          submitButton.classList.remove('animated', 'heartBeat');
        }, 1000);
      }
    },
  });
});

function getTimeString() {
  const now = new Date();
  const hour = now.getHours();
  const minute = now.getMinutes();
  const minuteString = ("0" + minute).slice(-2);
  return `${hour}:${minuteString}`;
}

ちゃんと覚えてくれることを期待してコピペします。

何も考えず、全部貼り付けます。

改善余地を提示してもらう

それぞれの視点で改善余地を挙げてくれます。段階的に考えてもらうのが重要です。

提案してもらう

それではどのように改善するのか教えていただきましょう。

改善箇所1
改善箇所2
Vue.config.productionTip = false;

document.addEventListener("DOMContentLoaded", function () {
  new Vue({
    el: "#app",
    data: {
      userMessage: "",
      messages: [
        {
          text: "こんにちは!チャットボットです。",
          time: getTimeString(),
          isUser: false,
          isBouncing: false
        },
      ],
      userInputDisabled: false,
      messagesWrapper: null
    },
    methods: {
      sendMessage() {
        try {
          if (this.userMessage.trim() !== "") {
            if (this.userMessage.length > 140) {
              this.messages.push({
                text: "メッセージは140文字以下で入力してください",
                time: getTimeString(),
                isUser: false,
                isBouncing: false
              });
            } else {
              this.messages.push({
                text: DOMPurify.sanitize(this.userMessage),
                time: getTimeString(),
                isUser: true,
                isBouncing: false
              });

              this.userMessage = "";

              setTimeout(() => {
                this.messages.push({
                  text: `こんにちは!これはチャットボットの応答です。`,
                  time: getTimeString(),
                  isUser: false,
                  isBouncing: true
                });

                this.messages.filter((msg, index) => index !== this.messages.length - 1)
                  .forEach(msg => msg.isBouncing = false);

                this.scrollToLatestMessage();
              }, 1000);
            }
          }
        } catch (err) {
          console.error(err);
        }
      },
      scrollToLatestMessage() {
        try {
          this.$nextTick(() => {
            if (!this.messagesWrapper) {
              this.messagesWrapper = this.$el.querySelector(".messages-wrapper");
            }
            this.messagesWrapper.scrollTop = this.messagesWrapper.scrollHeight;
          });
        } catch (err) {
          console.error(err);
        }
      },
      isLastUserMessageInGroup(index) {
        const nextMessage = this.messages[index + 1];
        return !nextMessage || nextMessage.isUser;
      },
      isLastBotMessageInGroup(index) {
        if (index === this.messages.length - 1) {
          return true;
        }
        return this.messages[index + 1].isUser;
      },
      buttonHover(event) {
        const submitButton = event.target;
        submitButton.classList.add('animated', 'heartBeat');
        setTimeout(() => {
          submitButton.classList.remove('animated', 'heartBeat');
        }, 500);
      }
    },
    mounted() {
      this.messagesWrapper = this.$el.querySelector(".messages-wrapper");
    }
  });
});

function getTimeString() {
  const now = new Date();
  const hour = now.getHours();
  const minute = now.getMinutes();
  const minuteString = ("0" + minute).slice(-2);
  return `${hour}:${minuteString}`;
}
やってもらったことのまとめ

やってみてわかる問題点

丸ごとコードを渡しているのもよくはないのですが、いくつか問題があります

  • 提案に揺らぎがある
    改善余地の方針は揺らぎませんが、提案内容のコードが揺らぎます。提案が揺らぐことをユーザーは認知しますが、なぜ今回はこの実装による提案をしたのか、という視点がAIがこの時点では持っていません。どの提案を採用すべきかは人が判断する必要があります。

  • メタ視点がないため、部分最適化になる
    今回はあえてjsのみとしていますが、関数名を変更しているので、htmlの修正も必要です。ロジックを作り替えたことによる影響は当然ありますので、全体が動作するように考慮する必要があります。

  • コードが長くなるほど、最適化による影響が大きい
    コピペ開発者の限界がここにあります。改善箇所をそのまま受け入れると、どのような振る舞いをするのか理解せず実装するため、エラーに対応できません。遠回りなやり取りが発生してしまい、品質・生産性ともに悪化します。

当たり前といえば当たり前ですが、ChatGPTという専門家がいたとしても、実際の作業者が専門用語を理解していないと、やり取り自体がオーバーヘッドになります。一方専門家が使うと、使いどころがわかっているので、優秀なCopilotになるでしょう。


この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?