Link and Motivation Developers' Blog

リンクアンドモチベーションの開発者ブログです

私と新人エンジニアとGitHub Copilotと、共に乗り越えた3つの壁

はじめに

こんにちは! リンクアンドモチベーション CREグループでエンジニアをやっている者です。

GitHub Copilotとの出会い、それは3月下旬のことでした。

社内も界隈もAIの話題でもちきりの中、GitHub Copilotの社内導入に向け、私の受け持つプロジェクトで先行導入して感想が欲しいと願ったり叶ったりな依頼をいただきました。
待ってましたお任せあれということでGitHub Copilotとのドキドキ共同開発が幕を開け、今ではすっかり私やチームの心強い相棒となり、LMのエンジニア全員が導入して開発を進めています。

そんなGitHub Copilotとの共同開発、実際にどんなところがよかったのか?

コードを書き始めると実装内容を予測してサジェストしてくれたり、コメントを介してコードリーディングの手助けをしてくれたり、開発スピードUPには間違いなく貢献してくれました。

特に、今回先行導入したプロジェクトには「プロジェクト初参画の新人エンジニアと一緒に、1ヶ月以内に実装を終える」というミッションがありました。

このミッションに対してGitHub Copilotはどう効果を発揮してくれたのか?逆に気をつけなければいけないことはあったか?この機会に振り返ってみたいと思います。

GitHub Copilotの導入を検討している方や活用事例を知りたい方の参考になれば幸いです!

この記事の登場人物

ご参考までに

人物 補足
バックエンドエンジニア歴5年。PHP畑で開発していたが、去年LMに入社し、Ruby on Rails歴は半年くらい
新人エンジニア(以降、新人ちゃん) これまではカスタマーサポートとの連携業務(※)をメインに担当
実装者としてプロジェクト参画するのは今回が初めて
※業務の詳細はCREのおしごと〜エンジニアチーム編〜を参照
エディタ RubyMine

GitHub Copilot と共に乗り越えた3つの壁

プロジェクトが始まる少し前、新人ちゃんは既存機能の拡張を行なっており、一部を私とペアプロしながら進めていたところ、とある3つの壁にぶつかりました。
私も初めて触る機能だったこともあり、あーだこーだと結構な時間をかけて乗り越えた記憶が新しいうちに、今回のプロジェクトがはじまります。

さて、 GitHub Copilotの導入前後で壁の乗り越え方はどう変わったのでしょうか?
Before/Afterで振り返ってみます。

壁1:コードが複雑で仕様が理解できない

Before:一行ずつ頑張ってコードを読み解いたり、ドキュメントを探したり、仕様を知っていそうな人に聞きにいく
After:コメントを介してGitHub Copilotに読み解きを手伝ってもらう

これは新人ちゃんに限った話ではなく、人類が頭を抱える壁ですね。
例えばこんな恐怖の条件分岐があったとします(※ChatGPTに「テックブログのサンプルコードとして紹介するのでRubyで複雑な条件分岐を書いてください」とお願いしたコード)

  def sugoi_nest
    if user.logged_in?
      if user.admin?
        if user.name == "admin" && user.age >= 30
          # Do something
        else
          puts 'hoge'
        end
      else
        if user.name != "guest" || user.age < 18
          # Do something
        else
          # Do something else
        end
      end
    else
      if user.name.empty?
        if user.age == 0 || user.email.empty?
          # Do something
        else
          # Do something else
        end
      else
        # Do something
      end
    end
  end

新人ちゃん「で、入力値がこんな時はどうなるの…?」

そんな疑問にはQ&A機能を使ってGitHubCopilotにサッと答えてもらいましょう!

  def sugoi_nest
    # q: この分岐で、user.logged_in?=true, user.admin?=true, user.name="もなか"、user.age=10の時、アウトプットは何ですか?
    # a: 以下のようになります
    # "hoge"
    if user.logged_in?
      if user.admin?
        if user.name == "admin" && user.age >= 30
          # Do something
        else
          puts 'hoge'
        end
      else

もっと複雑な処理には厳しいですが、メモとしてコメントしながら読み解いていくと補完して助けてくれたりもします。
仕様やバグ調査時のコードリーディングで大いに活躍してくれています。

では、スピーディに仕様を把握し、どこに何の処理を追加すればいいかわかったところで、いざコーディング!

壁2:ユニットテスト実装に時間がかかる

前提、LMではテスト駆動開発を推奨しています。
t-wadaさんによる「レガシーコード改善のワークショップ」レポート
t_wadaさんによるTDD研修をワードクラウド・名言5選で振り返ってみた

今回もまずはテストを実装。

新人ちゃん「この時はどのマッチャを使えばいいんだろ、調べなきゃ。パターンもたくさん書かなきゃで…。ああ、データもケースごとに作らなきゃ。先が長そうだなあ…」

Before: 他のユニットテストから書き方を真似たり、調べたり、せっせと実装
After:コメントで指示して最初のテストケースを作成。〇〇な場合のような単純なパターンはGitHub Copilotの提案をそのまま採用

RSpec.describe Calculator do
  describe '#add' do
    it 'returns the sum of two numbers' do
      calculator = Calculator.new
      result = calculator.add(3, 5)
      expect(result).to eq(8)
    end
  end
  
  # 掛け算のテストを追加してください
  describe '#multiply' do
    it 'returns the product of two numbers' do
      calculator = Calculator.new
      result = calculator.multiply(3, 5)
      expect(result).to eq(15)
    end
  end
end

2つ目のテストケースはコメントで指示してGitHubCopilotの提案をそのまま採用しただけです。
これはサンプルコードですが、私も爆速でユニットテスト実装できてめちゃくちゃ気持ちよかったので、テストを書いている・これから導入しようとしている方への推しポイントです!

書きたいテストケースはわかっているけど、経験がなくて手が遅い、という新人ちゃんの実装スピードをブーストしてくれました。

壁3:リファクタリングがムズカシイ

〜レビュー中〜

私「おやおや、なぜか生SQLでデータ取得してる箇所がある。ついでにリファクタしちゃおうか〜」
新人ちゃん「わかりました!!(どうやってやるんだろう)」

sql = <<~SQL
  SELECT users.name, orders.order_date
  FROM users
  INNER JOIN orders ON users.id = orders.user_id
  WHERE users.age > 25
  ORDER BY orders.order_date DESC
SQL

results = ActiveRecord::Base.connection.execute(sql)

(実際はサブクエリを含むもっと複雑なSQLでした)

BeforeActiveRecordへの変換方法をせっせと調査、改修、動作確認
AfterGitHub Copilotにリファクタリングしてもらって動作確認

  sql = <<~SQL
SELECT users.name, orders.order_date
FROM users
INNER JOIN orders ON users.id = orders.user_id
WHERE users.age > 25
ORDER BY orders.order_date DESC
  SQL
  results = ActiveRecord::Base.connection.execute(sql)
  # 上記のSQLをActiveRecordにリファクタリングしてください
  # a: 以下のようになります
  # User.joins(:orders).where('users.age > 25').order('orders.order_date DESC').select('users.name, orders.order_date')

実装者は意図通りのコードであること、ユニットテストが通ることを確認すればいいだけになりました。

他にも、壁1であげた複雑な分岐もあっという間にリファクタ出来たり、新人ちゃんにとってちょっとハードルが高い壁でもGitHub Copilotと一緒ならチャレンジ出来ます。

気をつけた方がいいこと

「動いたからヨシ!」にしない

GitHub Copilotと共に無事スケジュール通りに実装を終え、コードもスマートに仕上がりました。

しかし、レビューで処理の詳細を聞いた時に「GitHub Copilotが提案してくれたからよくわからない。テストは通っているから大丈夫だと思いますが、調べます」というやりとりも一部ありました。

書き始めるとサッと期待通りっぽいコードを提案して、実際に期待通り動くため、簡単なものであればTabキーを押せばほぼ実装終わってしまうということもあると思います。

ただやはり、それっぽいだけで間違えているといることも往々にしてあるため、特に新人のうちは提案されたコードをきちんと理解して説明できる責任を持つこと、メンターやレビュワーはしっかり問いを投げること、これを開発スピードとトレードオフで行う必要があると感じました。

おわりに

GitHub CopilotやChatGPTなどのAIによって開発環境がガラリと変わっていくのを日々感じます。

ただそれらのAIは「今までの開発を取って代わるスーパーエンジニア」というよりは「やりたいことの実現可能性や出力速度をMAXまでブーストしてくれる相棒」だと感じています。

強い相棒を得て、まだまだLMのプロダクトは進化していきます。
「やりたい」「つくりたい」という強い気持ちにAIが力を貸してくれるようになり、新人や中堅の垣根が取り払われ、もっと素敵な未来が切り拓かれていくかもしれません。

技術革新にドキドキハラハラしつつも、そんな未来に胸躍らせて、今日も明日もGitHub Copilotと新人ちゃんと一緒にコードを書いていきたいと思います。

読んでいただきありがとうございました!