pwa-development
Service WorkerやWorkboxを活用したProgressive Web Appの開発を支援するスキルです。キャッシュ戦略やオフライン対応の設計・実装など、PWAに必要な機能を効果的に構築する際にトリガーされます。
description の原文を見る
Progressive Web Apps - service workers, caching strategies, offline, Workbox
SKILL.md 本文
PWA開発スキル
目的: オフラインで動作し、ネイティブアプリのようにインストール可能で、すべてのデバイスで高速かつ信頼性の高い体験を提供するProgressive Web Appsを構築します。
PWAの中核要件
┌─────────────────────────────────────────────────────────────────┐
│ PWAの3つの柱 │
│ ───────────────────────────────────────────────────────────── │
│ │
│ 1. HTTPS │
│ Service Workerとセキュリティに必須。 │
│ 開発ではlocalhostが許可されます。 │
│ │
│ 2. SERVICE WORKER │
│ バックグラウンドで実行されるJavaScript。 │
│ オフライン、キャッシング、プッシュ通知を実現。 │
│ │
│ 3. WEB APP MANIFEST │
│ アプリメタデータを記述するJSONファイル。 │
│ インストールとアプリのような体験を実現。 │
├─────────────────────────────────────────────────────────────────┤
│ インストール可能性の条件 (Chrome) │
│ ───────────────────────────────────────────────────────────── │
│ • HTTPS (またはlocalhost) │
│ • Fetch ハンドラーを持つ Service Worker │
│ • Web app manifest: name、icons (192px + 512px)、 │
│ start_url、display: standalone/fullscreen/minimal-ui を含む │
└─────────────────────────────────────────────────────────────────┘
Web App Manifest
必須フィールド
{
"name": "My Progressive Web App",
"short_name": "MyPWA",
"description": "A description of what the app does",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/icon-512-maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
拡張 Manifest(フル機能)
{
"name": "My Progressive Web App",
"short_name": "MyPWA",
"description": "A full-featured PWA",
"start_url": "/?source=pwa",
"scope": "/",
"display": "standalone",
"orientation": "portrait-primary",
"background_color": "#ffffff",
"theme_color": "#3367D6",
"dir": "ltr",
"lang": "en",
"categories": ["productivity", "utilities"],
"icons": [
{ "src": "/icons/icon-72.png", "sizes": "72x72", "type": "image/png" },
{ "src": "/icons/icon-96.png", "sizes": "96x96", "type": "image/png" },
{ "src": "/icons/icon-128.png", "sizes": "128x128", "type": "image/png" },
{ "src": "/icons/icon-144.png", "sizes": "144x144", "type": "image/png" },
{ "src": "/icons/icon-152.png", "sizes": "152x152", "type": "image/png" },
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-384.png", "sizes": "384x384", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/icon-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
],
"screenshots": [
{
"src": "/screenshots/desktop.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide"
},
{
"src": "/screenshots/mobile.png",
"sizes": "750x1334",
"type": "image/png",
"form_factor": "narrow"
}
],
"shortcuts": [
{
"name": "New Item",
"short_name": "New",
"description": "Create a new item",
"url": "/new?source=shortcut",
"icons": [{ "src": "/icons/shortcut-new.png", "sizes": "192x192" }]
}
],
"share_target": {
"action": "/share",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"title": "title",
"text": "text",
"url": "url",
"files": [{ "name": "files", "accept": ["image/*"] }]
}
},
"protocol_handlers": [
{
"protocol": "web+myapp",
"url": "/handle?url=%s"
}
],
"file_handlers": [
{
"action": "/open-file",
"accept": {
"text/plain": [".txt"]
}
}
]
}
Manifest チェックリスト
-
nameとshort_nameを定義 -
start_urlを設定(分析用にクエリパラメーターを使用) -
displayをstandaloneまたはfullscreenに設定 - アイコン: 最小で 192x192 と 512x512
- Androidアダプティブアイコン用のマスカブルアイコンを含める
-
theme_colorがアプリデザインに一致 -
background_colorをスプラッシュスクリーン用に設定 - スクリーンショットでより豊富なインストール UI(オプション)
- クイックアクション用のショートカット(オプション)
Service Worker パターン
基本的な Service Worker
// sw.js
const CACHE_NAME = 'app-cache-v1';
const STATIC_ASSETS = [
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js',
'/offline.html'
];
// Install: 静的アセットをキャッシュ
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(STATIC_ASSETS))
.then(() => self.skipWaiting())
);
});
// Activate: 古いキャッシュをクリーン
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys()
.then((keys) => Promise.all(
keys
.filter((key) => key !== CACHE_NAME)
.map((key) => caches.delete(key))
))
.then(() => self.clients.claim())
);
});
// Fetch: キャッシュから提供、ネットワークへフォールバック
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((cached) => cached || fetch(event.request))
.catch(() => caches.match('/offline.html'))
);
});
登録
// main.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
console.log('SW registered:', registration.scope);
} catch (error) {
console.error('SW registration failed:', error);
}
});
}
キャッシング戦略
戦略選択ガイド
| 戦略 | ユースケース | 説明 |
|---|---|---|
| Cache First | 静的アセット(CSS、JS、画像) | キャッシュをチェック、ネットワークへフォールバック |
| Network First | API レスポンス、動的コンテンツ | ネットワークを試す、キャッシュへフォールバック |
| Stale While Revalidate | 半静的コンテンツ(アバター、記事) | キャッシュを即座に提供、バックグラウンドで更新 |
| Network Only | キャッシュ不可のリクエスト(分析) | 常にネットワークを使用 |
| Cache Only | オフラインのみのアセット | キャッシュからのみ提供 |
Cache First(オフラインファースト)
// 最適:滅多に変わらない静的アセット
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'image' ||
event.request.destination === 'style' ||
event.request.destination === 'script') {
event.respondWith(
caches.match(event.request)
.then((cached) => {
if (cached) return cached;
return fetch(event.request).then((response) => {
const clone = response.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, clone);
});
return response;
});
})
);
}
});
Network First(フレッシュファースト)
// 最適:APIデータ、頻繁に更新されるコンテンツ
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/')) {
event.respondWith(
fetch(event.request)
.then((response) => {
const clone = response.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, clone);
});
return response;
})
.catch(() => caches.match(event.request))
);
}
});
Stale While Revalidate
// 最適:やや古いコンテンツでも問題ないもの
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/articles/')) {
event.respondWith(
caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((cached) => {
const fetchPromise = fetch(event.request).then((response) => {
cache.put(event.request, response.clone());
return response;
});
return cached || fetchPromise;
});
})
);
}
});
Workbox(推奨)
Workbox を使う理由
- 検証済みのキャッシング戦略
- リビジョン管理による事前キャッシング
- オフラインフォーム送信用の背景同期
- 自動キャッシュクリーンアップ
- TypeScript サポート
インストール
npm install workbox-webpack-plugin # Webpack
npm install @vite-pwa/vite-plugin # Vite
Workbox with Vite
// vite.config.js
import { VitePWA } from 'vite-plugin-pwa';
export default {
plugins: [
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'robots.txt', 'apple-touch-icon.png'],
manifest: {
name: 'My App',
short_name: 'App',
theme_color: '#ffffff',
icons: [
{ src: 'pwa-192x192.png', sizes: '192x192', type: 'image/png' },
{ src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png' }
]
},
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 // 24 hours
}
}
},
{
urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
handler: 'CacheFirst',
options: {
cacheName: 'image-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days
}
}
}
]
}
})
]
};
Workbox 手動 Service Worker
// sw.js
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
// 静的アセットを事前キャッシュ(ビルドツールで生成)
precacheAndRoute(self.__WB_MANIFEST);
// 画像をキャッシュ
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new CacheableResponsePlugin({ statuses: [0, 200] }),
new ExpirationPlugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days
})
]
})
);
// API レスポンスをキャッシュ
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new NetworkFirst({
cacheName: 'api-responses',
plugins: [
new CacheableResponsePlugin({ statuses: [0, 200] }),
new ExpirationPlugin({
maxEntries: 100,
maxAgeSeconds: 24 * 60 * 60 // 24 hours
})
]
})
);
// ページナビゲーションをキャッシュ
registerRoute(
({ request }) => request.mode === 'navigate',
new NetworkFirst({
cacheName: 'pages',
plugins: [
new CacheableResponsePlugin({ statuses: [0, 200] })
]
})
);
オフライン体験
オフラインページ
<!-- offline.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Offline - App Name</title>
<style>
body {
font-family: system-ui, sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
background: #f5f5f5;
}
.offline-content {
text-align: center;
padding: 2rem;
}
.offline-icon { font-size: 4rem; }
h1 { color: #333; }
p { color: #666; }
button {
background: #3367D6;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
</style>
</head>
<body>
<div class="offline-content">
<div class="offline-icon">📡</div>
<h1>You're offline</h1>
<p>Check your connection and try again.</p>
<button onclick="location.reload()">Retry</button>
</div>
</body>
</html>
オフライン検出
// オンライン/オフラインステータスの処理
function updateOnlineStatus() {
const status = navigator.onLine ? 'online' : 'offline';
document.body.dataset.connectionStatus = status;
if (!navigator.onLine) {
showNotification('You are offline. Some features may be unavailable.');
}
}
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
updateOnlineStatus();
背景同期(オフラインアクションをキュー)
// sw.js with Workbox
import { BackgroundSyncPlugin } from 'workbox-background-sync';
import { registerRoute } from 'workbox-routing';
import { NetworkOnly } from 'workbox-strategies';
const bgSyncPlugin = new BackgroundSyncPlugin('formQueue', {
maxRetentionTime: 24 * 60 // 24 時間重試
});
registerRoute(
({ url }) => url.pathname === '/api/submit',
new NetworkOnly({
plugins: [bgSyncPlugin]
}),
'POST'
);
// main.js - フォーム送信をキュー
async function submitForm(data) {
try {
const response = await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
} catch (error) {
// オンラインに戻ったときに背景同期で再試行
showNotification('Saved offline. Will sync when connected.');
}
}
アプリのような機能
インストールプロンプト
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
showInstallButton();
});
async function installApp() {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
console.log(`User ${outcome === 'accepted' ? 'accepted' : 'dismissed'} install`);
deferredPrompt = null;
hideInstallButton();
}
window.addEventListener('appinstalled', () => {
console.log('App installed');
deferredPrompt = null;
});
スタンドアローンモードの検出
// インストール済み PWA として実行中かどうかを確認
function isInstalledPWA() {
return window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true; // iOS
}
// ディスプレイモード変更をリッスン
window.matchMedia('(display-mode: standalone)')
.addEventListener('change', (e) => {
console.log('Display mode:', e.matches ? 'standalone' : 'browser');
});
プッシュ通知
// 許可をリクエスト
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
await subscribeToPush();
}
return permission;
}
// プッシュをサブスクライブ
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
// サブスクリプションをサーバーに送信
await fetch('/api/push/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription)
});
}
// sw.js - プッシュイベントを処理
self.addEventListener('push', (event) => {
const data = event.data.json();
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icons/icon-192.png',
badge: '/icons/badge-72.png',
data: { url: data.url }
})
);
});
// 通知クリックを処理
self.addEventListener('notificationclick', (event) => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
シェアターゲット
// sw.js - シェアターゲットを処理
self.addEventListener('fetch', (event) => {
if (event.request.url.endsWith('/share') &&
event.request.method === 'POST') {
event.respondWith((async () => {
const formData = await event.request.formData();
const title = formData.get('title');
const text = formData.get('text');
const url = formData.get('url');
// 共有コンテンツを保存または処理
// 共有データを含むアプリにリダイレクト
return Response.redirect(`/?shared=true&title=${encodeURIComponent(title)}`);
})());
}
});
パフォーマンス最適化
クリティカルレンダリングパス
<!-- クリティカル CSS をインライン化 -->
<style>
/* クリティカルな above-the-fold スタイル */
</style>
<!-- 重要なリソースを事前ロード -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/scripts/app.js" as="script">
<!-- 非クリティカル CSS を遅延 -->
<link rel="stylesheet" href="/styles/main.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/styles/main.css"></noscript>
画像最適化
<!-- レスポンシブ画像 -->
<img
src="/images/hero-800.webp"
srcset="
/images/hero-400.webp 400w,
/images/hero-800.webp 800w,
/images/hero-1200.webp 1200w
"
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
alt="Hero image"
loading="lazy"
decoding="async"
>
<!-- 最新フォーマットとフォールバック -->
<picture>
<source srcset="/images/hero.avif" type="image/avif">
<source srcset="/images/hero.webp" type="image/webp">
<img src="/images/hero.jpg" alt="Hero image" loading="lazy">
</picture>
コード分割
// ルートベースの分割用動的インポート
const routes = {
'/': () => import('./pages/Home.js'),
'/about': () => import('./pages/About.js'),
'/settings': () => import('./pages/Settings.js')
};
async function loadPage(path) {
const loader = routes[path];
if (loader) {
const module = await loader();
return module.default;
}
}
PWA のテスト
Lighthouse Audit
# CLI から Lighthouse を実行
npx lighthouse https://your-app.com --view
# チェックすべき主要メトリクス:
# - PWA バッジ (インストール可能、オフライン対応)
# - パフォーマンススコア
# - ベストプラクティス
# - アクセシビリティ
手動テストチェックリスト
-
インストール可能性
- デスクトップ Chrome にインストールプロンプトが表示される
- モバイルのホーム画面に追加できる
- インストール後、スタンドアローンモードでアプリが開く
-
オフラインサポート
- オフライン時(飛行機モード)にアプリが読み込まれる
- キャッシュされたページが正しく表示される
- キャッシュされていないルートへのオフラインフォールバックページが表示される
- オンラインに戻ったときに背景同期が機能する
-
パフォーマンス
- First Contentful Paint < 1.8s
- Largest Contentful Paint < 2.5s
- Time to Interactive < 3.8s
- Cumulative Layout Shift < 0.1
-
Service Worker
- SW が正常に登録される
- インストール時に静的アセットがキャッシュされる
- SW が正しく更新される(新しいバージョン)
- 古いキャッシュの問題がない
-
Manifest
- すべての必須フィールドが存在する
- アイコンが正しく表示される
- テーマカラーが適用される
- 起動時にスプラッシュスクリーンが表示される
Service Worker 更新のテスト
// 更新チェックを強制実行
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then((registration) => {
registration.update();
});
}
// 更新をリッスン
navigator.serviceWorker.addEventListener('controllerchange', () => {
// 新しい service worker がアクティベートされた
window.location.reload();
});
プロジェクト構造
project/
├── public/
│ ├── manifest.json # Web app manifest
│ ├── sw.js # Service worker (バンドルされていない場合)
│ ├── offline.html # オフラインフォールバックページ
│ ├── robots.txt
│ └── icons/
│ ├── icon-72.png
│ ├── icon-96.png
│ ├── icon-128.png
│ ├── icon-144.png
│ ├── icon-152.png
│ ├── icon-192.png
│ ├── icon-384.png
│ ├── icon-512.png
│ ├── icon-maskable.png # アダプティブアイコン用
│ ├── apple-touch-icon.png
│ └── favicon.ico
├── src/
│ ├── sw.js # Service worker ソース (バンドルされる場合)
│ ├── pwa/
│ │ ├── install.js # インストールプロンプト処理
│ │ ├── offline.js # オフライン検出
│ │ └── push.js # プッシュ通知処理
│ └── ...
└── tests/
└── pwa/
├── manifest.test.js
├── sw.test.js
└── offline.test.js
よくある間違い
| 間違い | 修正方法 |
|---|---|
| マスカブルアイコンが不足 | "purpose": "maskable" を含むアイコンを追加 |
| オフラインフォールバックがない | offline.html を作成してキャッシュ |
| キャッシュが決して期限切れにならない | Workbox の ExpirationPlugin を使用 |
| SW が過度にキャッシュする | リソースタイプごとに適切な戦略を使用 |
| 更新メカニズムがない | skipWaiting() + リロードプロンプトを実装 |
| インストールプロンプトが壊れている | manifest がすべての条件を満たしていることを確認 |
| 本番環境で HTTPS がない | SSL 証明書を設定 |
| キャッシュサイズが大きすぎる | maxEntries と maxAgeSeconds を設定 |
| 古い API レスポンス | 動的データに NetworkFirst を使用 |
| start_url トラッキングが不足 | クエリパラメーターを追加: /?source=pwa |
PWA開発チェックリスト
ローンチ前
- HTTPS が設定される(本番環境)
- Manifest が完全で必要なフィールドをすべて含む
- すべての必須サイズのアイコンがある(192、512、マスカブル)
- Service Worker が登録され機能している
- オフラインページが作成されてキャッシュされている
- すべてのリソースタイプに対してキャッシング戦略が定義されている
- インストールプロンプト処理が実装されている
- Lighthouse PWA audit がパスする
ローンチ後
- キャッシュサイズを監視
- SW 更新がアプリを壊さないことをテスト
- アナリティクスを通じて PWA インストールを追跡
- 複数のデバイス/ブラウザでテスト
- Core Web Vitals を監視
- プッシュ通知フローを設定(必要に応じて)
フレームワーク固有ガイド
Next.js
npm install next-pwa
// next.config.js
const withPWA = require('next-pwa')({
dest: 'public',
disable: process.env.NODE_ENV === 'development'
});
module.exports = withPWA({
// Your Next.js config
});
Create React App
# CRA 4+ は PWA サポートが組み込まれている
npx create-react-app my-pwa --template cra-template-pwa
Vite(任意のフレームワーク)
npm install vite-plugin-pwa -D
上記の Workbox with Vite セクションで設定を参照してください。
クイックリファレンス
キャッシング戦略チートシート
静的アセット(CSS、JS、画像) → Cache First
API レスポンス → Network First
ユーザー生成コンテンツ → Stale While Revalidate
アナリティクス、キャッシュ不可 → Network Only
オフラインのみのアセット → Cache Only
Manifest 最小要件
{
"name": "App Name",
"short_name": "App",
"start_url": "/",
"display": "standalone",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
Service Worker ライフサイクル
1. Register → 2. Install → 3. Activate → 4. Fetch
↓ ↓ ↓ ↓
アプリロード アセットキャッシュ 古いキャッシュ キャッシュ/ネットワーク
削除 からリクエスト提供
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- alinaqi
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/alinaqi/claude-bootstrap / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。