こんにちは。
ファインディ株式会社 で Tech Lead をやらせてもらってる戸田です。
現在のソフトウェア開発の世界は、生成AIの登場により大きな転換点を迎えています。
GitHub CopilotやCursorなど、生成AIを活用した開発支援ツールが次々と登場し、開発者の日常的なワークフローに組み込まれつつあります。
そこで今回は、弊社のGitHub Copilotの活用方法について紹介します。
それでは見ていきましょう!
カスタムインストラクション
GitHub Copilotを始めとする生成AIツールを効果的に活用するためには、まず最初にカスタムインストラクションの設定が必要不可欠です。
カスタムインストラクションに関しては以前の記事で紹介しておりますので、併せてご覧ください。
tech.findy.co.jp tech.findy.co.jp
カスタムインストラクションを設定することで、生成AIがプロジェクトのコンテキストを理解し、より適切な提案を行うことが可能になります。
GitHub Copilotの場合は .github/copilot-instructions.md
というファイルをリポジトリに配置するだけで利用することが出来ます。
他にも、実装コード、テストコード、コミットメッセージなど、目的別にカスタムインストラクションのファイルを分けることができるようになっています。
弊社の場合、ファイルや変数の命名規約に留まらず、コミットメッセージやテストコードについてもカスタムインストラクションに記載しています。
コミットメッセージにSemanticVersionを採用しているケースがあり、リリース時にコミットメッセージを元にリリースノートを自動生成しています。
そのため、コミットメッセージが持つ意味合いが重要となっており、誤ったSemanticVersionになっていた場合はレビュー時に修正依頼を出すこともあります。
しかし、Copilotが作業を行いコミットまで自動で行うケースの場合、AIが生成したコミットメッセージが誤っているケースが出てきました。
そこでカスタムインストラクションに次のようなコミットメッセージのルールを記載することで、Copilotが作成するコミットメッセージに適切なSemanticVersionが設定されるようにしています。
## コミットメッセージ規約 ### 1. 基本構造 <semantic version>-<type>: <message> ### 2. 例 - major-feat: remove user field from api params - minor-feat: add api for get user list - patch-docs: fix typo in README ### 3. 各要素の説明 #### semantic version https://semver.org/ - major - 互換性のないAPIの変更 - 既存のフィールドの削除 - 既存のフィールドの型の変更 - 必須パラメータの追加 - エンドポイントの削除や名前変更 - minor - 互換性のあるAPIの変更 - 新しいフィールドの追加 - 新しいエンドポイントの追加 - 任意パラメータの追加 - 既存の機能を拡張するが後方互換性を維持する変更 - patch - リファクタリング - ドキュメントの追加、変更 - CI/CDの追加、変更 - コードの意味に影響を与えない変更 #### type - test - テストの追加、修正 - feat - 新機能の追加 - fix - バグ修正 - chore - ビルドプロセスや補助ツールの変更 - docs - ドキュメントの変更 - refactor - リファクタリング - style - コードの意味に影響を与えない変更 - ci - CIに関連する変更 - perf - パフォーマンスを向上させるコードの変更 #### message - 変更内容を要約した72文字以内の英語のメッセージ - 技術用語は英語のままにする
カスタムインストラクションには事実のみを簡潔に書くのがポイントです。
複雑で長いルールを適用させたい場合は、具体例を示すと良いでしょう。今回紹介したコミットメッセージのカスタムインストラクションにも具体例を記載しています。
また、カスタムインストラクションは最初に1度作成して完成するようなものではなく、徐々に加筆修正して精度を上げていきましょう。
このように、ドメイン知識やプロジェクトのルールをカスタムインストラクションに記載することで、AIがより適切で自分たちの開発ルールに沿った提案を行ってくれるようになります。
MCP
GitHub Copilotや各種AIツールを効果的に活用するためには、MCPの活用も非常に重要になってきます。
カスタムインストラクションに加え、MCPを活用することで、Copilotが扱う情報の範囲を広げ、より適切な提案やタスクの実行を行うことが可能になります。
例えばGitHubのMCPを利用する場合、次のような記述で設定します。
{ "servers": { "github": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_GITHUB_PERSONAL_ACCESS_TOKEN>>" } } } }
GitHubのMCPの設定を行い、対象のIssueの情報を取得して、その内容をそのままCopilotに渡すことが出来ます。
Issueに要件やタスクの内容を記載しておくことで、Copilotがその内容を理解し、適切なコードの提案やタスクの実行を行うことが可能になります。
SentryのMCPも同様に活用することが出来ます。次のような記述で設定します。
{ "servers": { "sentry": { "command": "uvx", "args": [ "mcp-server-sentry", "--auth-token", "<YOUR_SENTRY_AUTH_TOKEN>" ] } } }
Sentryで障害を検知した際、MCP経由でSentryのIssueの情報を取得して、その内容をそのままCopilotに渡すことが出来ます。その情報を元に、Copilotに原因を特定してもらい、Copilotにコードを修正してもらうことが可能です。
このように、MCPを活用することで、Copilotがプロジェクトやリポジトリ、さらにそれらに関連する情報を取得して、より適切な提案やタスクの実行を行うことが可能になります。
Agent mode
GitHub CopilotのAgent modeを利用することで、Copilotが自律的にタスクを実行し、コードの修正や新機能の追加などを行うことが可能になります。
その際にカスタムインストラクションやMCPの設定が非常に重要な役割を果たします。
また、Agent modeを実行する際に、コンテキストを絞ることも非常に有効です。
特定のファイルや、コンテキストを指定することで、Copilotが参照する範囲を絞り込むことに繋がり、結果的に生成AIの提案の精度を向上させることができます。
例えば次のような実装コードとテストコードがあるとします。単純な描画を行うコンポーネントと、それに対するテストコードです。
export const TestComponent = () => { return ( <> <h1>Test</h1> <div> <div> <span>Test Text</span> </div> </div> </> ); };
import { render } from '@testing-library/react'; import { TestComponent } from './test.component'; describe('TestComponent', () => { const setup = () => { const utils = render(<TestComponent />); return { ...utils, } as const; }; describe('render', () => { it('render snapshot', () => { const { asFragment } = setup(); expect(asFragment()).toMatchSnapshot(); }); }); });
一見問題ないように見えますが、弊社で開発しているFindy Team+は複数言語での表示に対応しています。そのため、表示されるテキストに翻訳処理を追加する必要があります。
翻訳処理を追加して、それに伴ってテストコードも修正するのは手間がかかりますが、ルールさえ覚えれば単純作業になります。こういったケースはAIエージェントの得意分野です。
翻訳処理を一気に追加したい場合、まずカスタムインストラクションに次のような記述を追加します。
# 翻訳処理 - 辞書データを定義する - json 形式のファイルで定義する ```json:libs/feature-hoge/src/lib/locales/ja.json { "component": { "hogeLabel": "ほげ", "hogeButtonText": "ほげボタンテキスト" } } ``` - Component 内 で `useTranslation` を利用する - `keyPrefix` に `component` を指定する ```tsx import { Trans, useTranslation } from 'react-i18next'; export const HogeComponent = () => { const { t } = useTranslation('hoge', { keyPrefix: 'component', }); return ( <> <span>{t('hogeLabel')}</span> <button type="button">{t('hogeButtonText')}</button> </> ); }; ``` - Component のテストコードで `I18nextTestProvider` と `translateResources` を利用する - `translateResources` は `libs/feature-hoge/src/lib/locales` を相対パスで import する ```tsx import { render } from '@testing-library/react'; import { I18nextTestProvider } from '@provider/i18n'; import { translateResources } from '../../locales'; import { HogeComponent } from './hoge.component'; const setup = () => { const utils = render( <I18nextTestProvider resources={{ ja: { hoge: translateResources.ja }, }} > <HogeComponent /> </I18nextTestProvider> ); return { ...utils, }; }; describe('HogeComponent', () => { it('should render', () => { const { asFragment } = setup({}); expect(asFragment()).toMatchSnapshot(); }); }); ```
そして次のようなプロンプトをAgent modeで実行します。
あなたは優秀なフロントエンドエンジニアです。 次の指示に従って作業を行ってください。 ## 対象ディレクトリ - `libs/feature-test` ## 辞書データ - `libs/feature-test/src/lib/locales/ja.json` ## 1. 辞書データのファイルに書き込みを行う ### 作業内容 - 対象ファイルに合致する既存ファイルの実装コードから文字列を抽出する - 抽出した文字列の中で辞書データのファイルに定義されていない文字列のみを、辞書データのファイルに追加する ### 対象ファイル - Component - **/*.component.tsx ## 2. 翻訳処理を追加する ### 作業内容 - 対象ファイルに合致する既存ファイル全てに翻訳処理を追加する - 辞書データに記述されているテキストを翻訳処理に利用する ### 対象ファイル - Component - **/*.component.tsx ## 3. テストコードを修正する ### 作業内容 - 対象ファイルに合致する既存のファイル全てに翻訳処理を追加する ### 対象ファイル - Component - **/*.component.spec.tsx ## 4. snapshotを更新する ### 作業内容 - 対象ファイルに合致する既存のファイル全てのsnapshotを更新する - MCPのWallabyを使用して、変更したテストコードの実行とsnapshotの更新を行う
カスタムインストラクションとMCPをAgent modeで活用することで、次のようなコードへの変更が自動で実行されます。
実装コードに useTranslation
での翻訳処理が追加され、テストコードには I18nextTestProvider
と translateResources
を利用した翻訳処理が追加されていることがわかります。
import { useTranslation } from 'react-i18next'; export const TestComponent = () => { const { t } = useTranslation('test', { keyPrefix: 'component', }); return ( <> <h1>{t('testTitle')}</h1> <div> <div> <span>{t('testText')}</span> </div> </div> </> ); };
import { render } from '@testing-library/react'; import { I18nextTestProvider } from '@provider/i18n'; import { translateResources } from '../../../locales'; import { TestComponent } from './test.component'; describe('TestComponent', () => { const setup = () => { const utils = render( <I18nextTestProvider resources={{ ja: { test: translateResources.ja }, }} > <TestComponent /> </I18nextTestProvider> ); return { ...utils, } as const; }; describe('render', () => { it('render snapshot', () => { const { asFragment } = setup(); expect(asFragment()).toMatchSnapshot(); }); }); });
このように、カスタムインストラクションを使ってリポジトリのルールやドメイン知識をCopilotに提供することで、Agent modeの精度を向上させることができます。
またMCPを使うことで、Copilotが参照するリソースを増やしたり、実行できるタスクの幅を広げることができます。
Coding Agent
Coding Agentを利用することで、Copilotをバックグラウンドで独立して動作させて、人間の開発者と同じようにタスクを実行できます。
使い方はとても簡単です。タスクに対するIssueを作成して、Issueの担当者にCopilotを指定するだけです。その後、Copilotが自動でPull requestを作成して、GitHub Actions上でタスクが実行されます。タスクが完了した段階で、依頼者がレビュワーに設定されます。
また、Copilotが作成した変更に対してレビューコメントを書くことが出来ます。そのレビューコメントに対する修正もCopilotが自動で行ってくれます。
Coding Agentで作成されたPull requestに対してレビューコメントを書く場合、Add single comment
で一個ずつコメントを追加するのではなく、 Start a review
から複数のコメントを追加した後にまとめて送信することをオススメします。全てのコメントを一括で送信すると、Copilotは個々のコメントを個別に処理するのではなく、レビュー全体を対象に処理を開始します。
また、ここでもカスタムインストラクションは効果を発揮します。
Copilotが作成するPull requestの内容やコミットメッセージ、コードのスタイルなどをカスタムインストラクションに記載しておくことで、より自分たちの開発ルールに沿った内容でPull requestを作成してくれます。
Coding AgentはMCPの利用も簡単に設定することが出来ます。リポジトリの設定でMCPの設定ファイルを記述することで簡単に設定可能です。
また、Coding AgentがGitHub Actions上でタスクを実行する前に事前に実行しておきたい処理を設定することも可能です。.github/workflows/copilot-setup-steps.yml
というファイルを作成して、GitHub Actionsのworkflowを定義することで、事前に必要な処理を実行しておくことができます。
次の例は、Pythonのプロジェクトで必要なライブラリのインストールと、テスト用のデータベースを事前に起動しておくための copilot-setup-steps.yml
です。この設定をしておくことで、Coding Agentがファイルを修正してテストを実行する時に、必要なライブラリがインストールされており、テスト用のデータベースも起動している状態でタスクを実行できます。
name: "Copilot Setup Steps" on: workflow_dispatch jobs: copilot-setup-steps: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.13' - name: Start database with Docker Compose run: docker compose up -d db --wait - name: Install dependencies run: | pip install ".[test]" - name: Run migrations run: | python -m alembic upgrade head
Coding Agentが作成するPull requestの内容の精度は、次の要素に大きく左右されます。
- カスタムインストラクションの内容
- Issueに記載されているタスクの内容
- 既存実装の内容
特にIssueに記載されているタスクの内容は非常に重要です。タスクは具体的に且つ簡潔に記載することが重要です。そこで重要になってくるスキルがタスク分解です。
Issueのタスクを細分化して箇条書きにすると良いでしょう。大きなタスクをIssueに起票する際は、まず親Issueとして大きなIssueを作成することをオススメします。この親Issueに対してはCoding Agentを利用しません。
次に、親Issueに対して子Issueとして複数のSub Issueを作成します。このSub Issueに対してCoding Agentを利用します。Sub Issueの内容は具体的に且つ簡潔に箇条書きで記載しましょう。
このように簡潔で単純なタスクの集合体であるSub Issueを幾つも作成し、それに対してCopilotを担当者として指定することで、Copilotが自律的にタスクを実行し、Pull requestを作成してくれます。
Issueを作ってCopilotを担当者に設定するだけでPull requestが作成されるので、並行して複数のPull requestを作成してもらえることも強みの1つです。エンジニアはIssueを作って、担当者にCopilotを指定して、作成されたPull requestをレビューするだけです。
Coding AgentがPull requestを作成している間に、自分はローカル環境で別の作業をやっているのは、今では当たり前の光景になってきています。
まとめ
いかがでしたでしょうか?
今回紹介した機能はGitHub Copilotの機能のほんの一部であり、今回は特に重要な機能に絞って紹介しました。
カスタムインストラクションを整備しつつ、MCPを活用することが、GitHub Copilotを始めとする生成AIを効率的に活用するための最初の一歩です。
その上で、Agent modeやCoding Agentを活用することで、より自律的にタスクを実行してもらい、開発の効率化を図ることが可能になり、エンジニア自身は他の作業に集中することが出来るようになります。
生成AIを活用した開発フローに変化していく上で、生成AIが出力したコードが正しいかどうかを判断するスキルが非常に重要になってきました。
この判断するスキルは基礎と経験によって培われるものであり、今まで以上に基礎力が重要になってきていると感じています。弊社でもエンジニアとしての基礎力を重要視しており、これを強化するための取り組みも幾つか行っていますが、それはまた別の機会で紹介したいと思います。
現在、ファインディでは一緒に働くメンバーを募集中です。
興味がある方はこちらから herp.careers