PLAY DEVELOPERS BLOG

HuluやTVerなどの日本最大級の動画配信を支える株式会社PLAYが運営するテックブログです。

HuluやTVerなどの日本最大級の動画配信を支える株式会社PLAYが運営するテックブログです。

GitHub Copilot を活用して社内 UI コンポーネントの Storybook カタログを作った話

こんにちは。テックリードの丸山 @maruyamaworks です。最近は Claude Code に全部賭けています。

弊社も生成 AI を活用することで開発効率を圧倒的に高めるべく、さまざまなツールの検証や導入、社内ルールの整備などを急ピッチで進めています。また、実際に開発支援系 AI を活用してうまくいった事例もいくつか出てきています。今回はその中から、GitHub Copilot を活用して社内 UI コンポーネントのカタログを作った話をご紹介したいと思います。

社内 UI コンポーネントについて

弊社の提供するプラットフォーム「PLAY CLOUD」は、さまざまな機能を持った複数のサービスから構成されていますが、それらのサービスはすべて統一されたデザイントークンに基づき実装された管理画面を持っています。

PLAY CLOUD 管理画面 UI

具体的には、以下のような要素が共通のモジュールとして用意されており、各サービスがこれらを import して利用する形となっています。

  • Button や InputField など 120 種類以上の基本的な UI 部品を提供する React コンポーネント群
  • テーマカラーや余白の幅などを CSS variable として定義したスタイルシート
  • ルーティングや認証の状態を管理する React コンテキスト
  • およそ 200 種類の SVG アイコン群

ところで、先述した React コンポーネント群には、これまでドキュメントがありませんでした。そのため、どういった props が用意されているか、複数のコンポーネントを組み合わせるときにどういう風に組み合わせればよいか、といった使い方が明確化されておらず、ソースコードを読んだり、既存のコードを探して真似したり、ということを行う必要がありました。その結果、コンポーネントが(設計者の意図しない)色々な使われ方をしたり、同じような機能を持つ props が乱立したり、という状況になっていました。

この状況を改善するため、コンポーネントカタログを整備することにしました。

Storybook を利用した UI カタログの整備

Storybook は、UI コンポーネントの開発やテストを実行したり、コンポーネントのドキュメントを出力したりすることができるツールです。Storybook の使い方についてはここでは説明しませんが、下図のようなドキュメントサイトを作ることができます。

Storybook カタログ画面

ひとつの UI コンポーネントには通常、さまざまな状態や使い方のバリエーションがありますが、Storybook ではそのひとつひとつを "Story" と呼びます。カタログ上で色々な Story を眺めながら、開発者は使いたいものを選んで、コードをアプリケーションに組み込むことができます。

バリエーションのひとつひとつが "Story"

この Story を作る作業は、ある程度クリエイティビティが求められる作業なので、生成 AI の得意分野だろうということで、AI に任せてみることにしました。結論としては、Story ファイル全体の 95% 程度は AI に作ってもらうことができ、結構うまく行きました*1。ここからは、その実際の手順を紹介します。

Story ファイルを作成してもらう

今回は例として、下図のような Panel というコンポーネントの Story ファイルを作成します。

Panel コンポーネント

まずは VSCode で React コンポーネントのファイル(Panel.tsx)を開きます。

次に、Copilot Chat のウィンドウを開き、「Agent」モードを選択します。VSCode では、現在表示しているファイルが自動的に LLM に対するコンテキスト情報として渡されます。使用する LLM は任意ですが、Claude の場合は 3.7 Sonnet 以上でないと失敗率が高いです(Claude 3.5 Sonnet の場合、TypeScript の型エラーを自力で解決できずに無限ループに陥ってしまうことがしばしばありました)。

モードを選択

この状態で以下のプロンプトを投げてみます。

Add storybook for this component.
(このコンポーネントの storybook を作って)

これだけの指示で、Panel.tsx の並びに Panel.stories.tsx ファイルが自動的に作成されました。

Panel.stories.tsx

import type { Meta, StoryObj } from '@storybook/react-vite';
import Panel from './Panel';
import Button from './Button';

const meta: Meta<typeof Panel> = {
  title: 'Components/Panel',
  component: Panel,
  parameters: {
    layout: 'padded',
  },
};

export default meta;
type Story = StoryObj<typeof Panel>;

export const Default: Story = {
  args: {
    title: 'Default Panel',
    children: (
      <div>
        <p>This is the default panel content. It automatically wraps content in a PanelBody when needed.</p>
        <p>Panels are versatile containers that can hold various types of content.</p>
      </div>
    ),
  },
};

// 以下省略(実際にはさらに 16 個の Story が続きます)

Chat ウィンドウに生成の過程が表示されていますが、よく見てみると、最初に既存の Story ファイルがあるかどうかを確認し、もしあればそのファイルと同じような書き方で Story ファイルを作成してくれるようです。とても賢いですね!

Story ファイルの生成過程

ちなみに、今回 LLM に対する指示やアウトプットはすべて英語で行いました。日本語とどちらが良いかは議論の余地があると思いますが、ソースコードやドキュメントを AI が読むことを考慮すると英語のほうがトークン効率が良いという点や、開発体制のグローバル化も見据えて英語としています。

実際にできあがった Story ファイルは、そのコンポーネントに用意されている props を網羅するようにさまざまなパターン(全部で 17 パターン)が作られていました。「このコンポーネントにはこんな使い方があったのか!」と驚かされることもしばしばありました。

すべての props を網羅するように Story が作られている

props に説明コメントを付けてもらう

Storybook は React コンポーネントのソースコードから props の型や説明文を自動的に読み取ってドキュメントに反映させるという機能を持っています(内部的には react-docgen を使用してソースコードを解析しているようです)。説明文は JSDoc コメントの形式でソースコードに書いておけば認識されるので、このコメントも AI を活用して自動生成してもらいましょう。

コメントを入れたいファイル(Panel.tsx)を開き、props の型を宣言している箇所を選択します。

JSDoc コメントを追加したい部分を選択

この状態で、以下のプロンプトを投げてみます。

Add missing JSDoc comment for each props in current selection. Each comment should be one-line.
(選択範囲中の各 props について、JSDoc コメントが抜けてたら追加して。各コメントは 1 行を目安に。)

これを実行すると、JSDoc コメントを追加してくれます。なお、生成されるコメントは、ちゃんとソースコードを読まないと分からない内容まで書かれていたので、選択範囲内にある情報(props の名前と型)だけを見て書いているわけではないようでした。とても賢いですね!

JSDoc コメントが追加された

よりよいアウトプットを得るために

当然ですが、AI にコードを書かせることの課題感も感じました。

AI はその確率論的な性質のため、アウトプットに再現性がありません。今回のように、複数の React コンポーネントに対し、1 ファイルごとに指示を出していると、どうしてもファイルごとに書き方が揺れてしまいます。なるべく書き方は統一してもらいたいです。

// こう書かれることもあれば
import React from 'react';
const [count, setCount] = React.useState(0);

// こう書かれることもある
import { useState } from 'react';
const [count, setCount] = useState(0);

また、色をカラーコードで指定されたり、余白をピクセル値で指定されたりするのも困ります。デザインの一貫性がなくなってしまうので、専用のユーティリティクラスを使うか、CSS variable を使ってもらいたいです。

// NG - デザインの一貫性がなくなってしまう
return (
  <div style={{ backgroundColor: '#f6f8fa', padding: '8px' }}>
    Hello, World!
  </div>
);

// Better - 事前定義済みのクラス名や CSS variable を使用
return (
  <div className="util-padding-medium" style={{ backgroundColor: 'var(--color-bg-default)' }}>
    Hello, World!
  </div>
);

こういった問題に対しては、基本的にはプロンプトなどで事前に指示をしておくことが大事ですが、ESLint などの静的解析ツールの活用も有効です。ESLint のカスタムルールを作成することで、AI のアウトプットに制限を設け、出力の一貫性を高めることができます。

ESLint のカスタムルールの作成方法については、当ブログでも過去に紹介しておりますので、こちらもあわせてご覧ください。

developers.play.jp

これからの開発について思うこと

実は先日 TSKaigi 2025 に参加しまして、色々な方(型)のお話を拝聴したのですが、開発支援系 AI に関するセッションもやはり多かったです。特に Yuku Kotani 氏のセッション で紹介されていた、AI を縛る ESLint カスタムルールを AI 自身に書かせるというアイデアは、極めて実用的な AI の活用方法だなと感じました。

AI 全盛期を迎えた私たち人間が、これからの開発(特に直近 1〜2 年くらいの開発)をどう進めるべきかを考えると、私は次の 3 つのポイントに集約されるのではないかと考えています。

  1. コードベースを小さな単位に分割する(AI のコンテキスト上限を超えないように)
  2. ドキュメントを書く(ドメイン知識やコーディング規約を AI にインプットさせる)
  3. テストや ESLint ルールを書く(AI のアウトプットを縛るガードレールとして)

これらはどれも、生成 AI に特有のテクニックではなく、人間が開発する場合においても重要なポイントです。こうした基本的なことが、AI の台頭によってますます重要性を増してきているのです。

今回作成した Storybook は、コンポーネントのドキュメントとしての役割だけでなく、ユニットテストやビジュアルテストを実行する役割も兼ねています。つまり、先に挙げた 3 つのポイントのうち 2 つに関連しています。ただし、現状ではテストコードが十分に書けているとは言い難いので、今後は LLM にコンポーネントのテストコードを書かせるというところにも挑戦したいと思います。

*1:残りの 5% については、TypeScript の型エラーを手動で直す必要があったり、Storybook 全体の設定を変更する必要があったりしました。