Claude CodeでPrisma ORMを実装する完全ガイド: schema設計・migration・transaction
Claude CodeでPrisma ORMを設計・実装する手順。schema、migration、transaction、seed/test、レビュー観点まで実例で解説。
Prisma ORM(TypeScriptからDBを安全に扱うための型付きDBレイヤー)は、Claude Codeと相性が良い道具です。DB設計、migration、seed、テスト、レビュー観点までをテキストで明示できるため、AIに任せてよい作業と人間が止めて確認すべき作業を分けやすくなります。
ただし「Prismaを入れてCRUDを書いて」と頼むだけでは、後から壊れやすいデータ層になります。インデックス不足、安易なdb push、長すぎるtransaction、N+1クエリ、曖昧な削除ルールは、最初は動いてもPVが増えたタイミングで効いてきます。
この記事では、Masaが小さなブログAPIを題材にClaude Codeへ設計から実装まで依頼する前提で、公開サービスに載せられるPrisma ORMの作り方をまとめます。公式ドキュメントの概念に沿いながら、コピペして試せるschema、Prisma Clientの使い方、migrationレビュー、seed/testコマンド、レビュー checklist まで一気通貫で扱います。
全体像
Prisma ORMは、schema.prismaを中心にDBの形を宣言し、Prisma MigrateでSQL migrationを作り、Prisma Clientで型付きクエリを書く構成です。Claude Codeには、この3層を分けて依頼するとレビューしやすくなります。
flowchart LR
A["要件をClaude Codeへ渡す"] --> B["schema.prismaを設計"]
B --> C["migration.sqlを生成して確認"]
C --> D["Prisma Clientで実装"]
D --> E["seedとtestで動作確認"]
E --> F["レビュー checklist で公開判断"]
公式の入口はPrisma ORM docsです。schemaの構成はPrisma Schema、transactionはTransactions and batch queries、migrationはPrisma Migrate、生SQLはRaw queriesを参照すると、Claude Codeの出力が古い書き方に寄っていないか確認できます。
Claude Code自体の基本操作はClaude Code入門ガイド、軽量ORMとの違いはClaude CodeでDrizzle ORMを使う方法、BaaS寄りの構成はClaude CodeとSupabase連携も合わせて読むと判断しやすいです。
まずClaude Codeへ渡す設計プロンプト
最初の依頼では、DB製品、削除ルール、検索条件、migrationの扱いを入れます。ここを曖昧にすると、Claude Codeは「それらしいschema」を作りますが、運用上の制約が落ちます。
ブログAPI向けのPrisma ORM設計をしてください。
前提:
- TypeScript + Prisma ORM + SQLiteでローカル検証し、後でPostgreSQLへ移行する
- User, Post, Category, Comment, Notification, AuditLogを扱う
- PostはDRAFT/PUBLISHED/ARCHIVEDをstatus文字列で持つ
- slugとemailは一意にする
- 公開記事一覧はstatus, publishedAt, author, categoryで絞り込む
- Post削除時はCommentと中間テーブルをcascade削除する
- User削除はまず禁止し、必要なら匿名化migrationを別で作る
出力:
1. prisma/schema.prisma
2. migrationで作られるSQLのレビュー観点
3. Prisma Clientでのcreate/list/publish実装
4. seedとtestコマンド
5. 本番投入前のレビュー checklist
ポイントは「DBに任せる制約」と「アプリで守る制約」を分けることです。Claude CodeがonDelete: Cascadeを広く付けすぎたら危険信号です。ユーザー削除で投稿まで消える設計は、ブログやSaaSでは監査ログや売上データを失う原因になります。
コピペで試せる最小セットアップ
以下はローカルで試しやすいSQLite構成です。PostgreSQLで始める場合も、schemaの考え方は同じです。Prisma ORM v7系では接続URLやseedコマンドをprisma.config.tsで扱うため、ここでもその形に寄せています。
{
"type": "module",
"scripts": {
"db:generate": "prisma generate",
"db:migrate": "prisma migrate dev",
"db:deploy": "prisma migrate deploy",
"db:seed": "prisma db seed",
"dev": "tsx src/demo.ts",
"test": "vitest run"
},
"dependencies": {
"@prisma/client": "latest",
"dotenv": "latest"
},
"devDependencies": {
"prisma": "latest",
"tsx": "latest",
"typescript": "latest",
"vitest": "latest"
}
}
npm install
echo 'DATABASE_URL="file:./dev.db"' > .env
mkdir prisma src
// prisma.config.ts
import "dotenv/config";
import { defineConfig, env } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
seed: "tsx prisma/seed.ts",
},
datasource: {
url: env("DATABASE_URL"),
},
});
schema.prismaを設計する
Prisma Schemaは、DB接続、生成するClient、モデル定義を書くファイルです。公式docsでは「Prisma ORM setupの主な設定方法」と説明されています。Claude Codeに作らせたschemaは、@unique、@@index、relation、削除ルールの4点を人間が必ず読みます。
// prisma/schema.prisma
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
}
datasource db {
provider = "sqlite"
}
model User {
id String @id @default(cuid())
email String @unique
name String
role String @default("editor")
posts Post[]
comments Comment[]
notifications Notification[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([role])
}
model Post {
id String @id @default(cuid())
slug String @unique
title String
body String
status String @default("DRAFT")
authorId String
author User @relation(fields: [authorId], references: [id], onDelete: Restrict)
categories CategoriesOnPosts[]
comments Comment[]
publishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([authorId])
@@index([status, publishedAt])
}
model Category {
id String @id @default(cuid())
slug String @unique
name String
posts CategoriesOnPosts[]
}
model CategoriesOnPosts {
postId String
categoryId String
assignedAt DateTime @default(now())
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
@@id([postId, categoryId])
}
model Comment {
id String @id @default(cuid())
body String
postId String
authorId String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
author User @relation(fields: [authorId], references: [id], onDelete: Restrict)
createdAt DateTime @default(now())
@@index([postId, createdAt])
@@index([authorId])
}
model Notification {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
type String
message String
readAt DateTime?
createdAt DateTime @default(now())
@@index([userId, readAt])
}
model AuditLog {
id String @id @default(cuid())
action String
targetId String
metadata String?
createdAt DateTime @default(now())
@@index([action, createdAt])
}
このschemaでは、公開記事一覧に効く@@index([status, publishedAt])、著者別一覧に効く@@index([authorId])、コメント表示に効く@@index([postId, createdAt])を入れています。Masaが以前、記事一覧の検索条件を後から増やしたとき、publishedAtだけのindexでは並び替えに効かず、statusとの複合indexに直したことがありました。Claude Codeには「どの画面のどのWHERE/ORDER BYに効くindexか」をコメントで説明させると、不要なindexも見つけやすくなります。
schemaレビューでClaude Codeに突っ込ませる質問
schemaができたら、すぐ実装へ進まずに「なぜこの形なのか」をClaude Code自身に説明させます。生成AIは最初の回答ではもっともらしい構造を出しますが、二段目のレビューを依頼すると、relationの向き、削除時の挙動、indexの過不足を自分で見つけることがあります。
たとえば、次のような質問を投げます。
上のschemaを批判的にレビューしてください。
- どのindexがどの画面・APIのqueryに効くか
- cascade deleteが広すぎる箇所はないか
- User削除時に保持すべき監査情報はないか
- statusをStringで持つ場合の欠点と、PostgreSQL enumへ移行する場合のmigration案
- 将来tenantIdを追加するなら、どのunique/indexを複合化すべきか
- APIレスポンスで返してはいけないfieldはないか
このレビューで特に見たいのは、Claude Codeが「未確定の要件」を未確定として扱えているかです。たとえば、ユーザー削除時に投稿も消してよいのか、退会ユーザーを匿名化して記事だけ残すのかは、サービス方針で答えが変わります。ここを勝手にonDelete: Cascadeにされると、運用後の修正が重くなります。
もう一つの確認点は、将来の多言語化やチーム機能を想定した余白です。ブログならslugは全体でuniqueでよいかもしれませんが、ワークスペース型SaaSならtenantId + slugの複合uniqueが必要になります。カテゴリも全体共通なのか、tenantごとなのかでrelationが変わります。Claude Codeには「今は採用しないが、後から追加するならどこが変わるか」まで言わせると、初期実装が過剰になるのを防ぎつつ、後戻りの大きい設計ミスも避けられます。
実務では、schemaレビューの結果をPR本文に短く残すのがおすすめです。「このindexは公開一覧」「User削除はRestrict」「送信処理はtransaction外」という判断が残っていれば、3か月後に別の開発者が見ても意図を追えます。AI生成コードは速度が強みですが、判断の履歴が残らないと保守性が落ちます。
migrationは生成して終わりにしない
Prisma MigrateはschemaからSQL migrationファイルを作ります。ただし、生成されたSQLはレビュー対象です。特に既存テーブルへのNOT NULL追加、DROP COLUMN、unique制約、cascade削除はそのまま本番へ流してはいけません。
npx prisma format
npx prisma migrate dev --create-only --name init_blog
# 生成されたSQLを読む
# prisma/migrations/*/migration.sql
npx prisma migrate dev
npx prisma generate
本番では開発用のmigrate devではなく、CI/CDやデプロイ処理で次を使います。
npx prisma migrate deploy
prisma db pushはプロトタイプには便利ですが、履歴としてレビューできるSQLを残しません。公開サービスでは「migrationファイルをレビューし、PRに残す」運用に寄せます。MongoDBはPrisma Migrateの扱いが異なるため、公式docsの注意書きを確認してください。
rollback方針もPRに書きます。成功済みmigrationを戻すなら、schemaを戻して新しい「戻すmigration」を作るのが基本です。途中で失敗したmigrationだけは、公式のgenerating down migrationsやmigrate resolveの手順に従い、バックアップ確認、migrate status、必要ならdb execute、最後にmigrate resolve --rolled-backの順で扱います。
npx prisma migrate status
npx prisma migrate resolve --rolled-back "20260603090000_failed_change"
Prisma Clientを安全に使う
Prisma Clientはschemaから生成される型付きClientです。selectやincludeを使うと、取得するカラムやrelationを明示できます。APIレスポンスに不要なフィールドを返さないことも、速度とセキュリティの両面で効きます。
// src/db.ts
import { PrismaClient } from "./generated/prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma?: PrismaClient;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: ["warn", "error"],
});
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = prisma;
}
// src/posts.ts
import { Prisma } from "./generated/prisma/client";
import { prisma } from "./db";
type CreatePostInput = {
authorEmail: string;
slug: string;
title: string;
body: string;
categorySlugs: string[];
};
export async function createPost(input: CreatePostInput) {
return prisma.post.create({
data: {
slug: input.slug,
title: input.title,
body: input.body,
author: {
connect: { email: input.authorEmail },
},
categories: {
create: input.categorySlugs.map((slug) => ({
category: {
connectOrCreate: {
where: { slug },
create: { slug, name: slug },
},
},
})),
},
},
include: {
author: { select: { id: true, name: true } },
categories: { include: { category: true } },
},
});
}
type ListPostsParams = {
page?: number;
perPage?: number;
categorySlug?: string;
search?: string;
};
export async function listPublishedPosts(params: ListPostsParams = {}) {
const page = Math.max(params.page ?? 1, 1);
const perPage = Math.min(Math.max(params.perPage ?? 20, 1), 50);
const where: Prisma.PostWhereInput = {
status: "PUBLISHED",
...(params.categorySlug
? {
categories: {
some: { category: { slug: params.categorySlug } },
},
}
: {}),
...(params.search
? {
OR: [
{ title: { contains: params.search } },
{ body: { contains: params.search } },
],
}
: {}),
};
const [items, total] = await prisma.$transaction([
prisma.post.findMany({
where,
skip: (page - 1) * perPage,
take: perPage,
orderBy: [{ publishedAt: "desc" }, { createdAt: "desc" }],
select: {
id: true,
slug: true,
title: true,
publishedAt: true,
author: { select: { name: true } },
categories: { select: { category: { select: { slug: true, name: true } } } },
_count: { select: { comments: true } },
},
}),
prisma.post.count({ where }),
]);
return {
items,
pagination: {
page,
perPage,
total,
totalPages: Math.ceil(total / perPage),
},
};
}
export async function publishPost(postId: string) {
return prisma.$transaction(async (tx) => {
const post = await tx.post.findUnique({
where: { id: postId },
select: { id: true, title: true, status: true, authorId: true },
});
if (!post) {
throw new Error("Post not found");
}
if (post.status === "PUBLISHED") {
return post;
}
const published = await tx.post.update({
where: { id: post.id },
data: {
status: "PUBLISHED",
publishedAt: new Date(),
},
select: { id: true, title: true, status: true, publishedAt: true },
});
await tx.notification.create({
data: {
userId: post.authorId,
type: "POST_PUBLISHED",
message: `${post.title} was published`,
},
});
await tx.auditLog.create({
data: {
action: "POST_PUBLISHED",
targetId: post.id,
metadata: JSON.stringify({ title: post.title }),
},
});
return published;
});
}
listPublishedPostsのように、記事一覧と件数取得は独立した読み取りなので$transaction([])でまとめられます。一方、publishPostは「記事更新、通知作成、監査ログ作成」の整合性が必要なのでinteractive transactionを使っています。公式docsにもある通り、transaction内で外部API呼び出しや遅い処理を実行するとDB接続を長く保持し、デッドロックや性能低下の原因になります。メール送信やWebhookはOutboxテーブルに積み、transactionの外でworkerに処理させる方が安全です。
safe SQLの書き方
Prisma Clientで表現しにくい集計だけ、生SQLを使います。pitfallは文字列連結です。ユーザー入力をSQL文字列へ直接埋め込むとSQL injection riskが出るため、$queryRawのtagged templateかPrisma.sqlを使います。
import { Prisma } from "./generated/prisma/client";
import { prisma } from "./db";
export async function topAuthors(limit = 10) {
return prisma.$queryRaw<
{ authorId: string; postCount: bigint }[]
>(Prisma.sql`
SELECT "authorId", COUNT(*) AS "postCount"
FROM "Post"
WHERE "status" = ${"PUBLISHED"}
GROUP BY "authorId"
ORDER BY "postCount" DESC
LIMIT ${Math.min(limit, 50)}
`);
}
Claude Codeには「$queryRawUnsafeを使う必要がある理由を説明して。説明できないなら使わない」と指示します。table名やcolumn名の動的切り替えはtagged templateで安全に差し込めないため、許可リストを人間が固定します。
seedと動作確認
Claude Codeにseedを書かせるときは「何度実行しても壊れない」ことを条件にします。createだけだと2回目でunique制約に当たるため、upsertを使います。
// prisma/seed.ts
import { PrismaClient } from "../src/generated/prisma/client";
const prisma = new PrismaClient();
async function main() {
const user = await prisma.user.upsert({
where: { email: "masa@example.com" },
update: { name: "Masa" },
create: {
email: "masa@example.com",
name: "Masa",
role: "admin",
},
});
const category = await prisma.category.upsert({
where: { slug: "prisma" },
update: { name: "Prisma" },
create: { slug: "prisma", name: "Prisma" },
});
const post = await prisma.post.upsert({
where: { slug: "claude-code-prisma-demo" },
update: {
title: "Claude Code Prisma demo",
body: "Seeded article for local verification.",
status: "PUBLISHED",
publishedAt: new Date(),
},
create: {
slug: "claude-code-prisma-demo",
title: "Claude Code Prisma demo",
body: "Seeded article for local verification.",
status: "PUBLISHED",
publishedAt: new Date(),
authorId: user.id,
categories: {
create: [{ category: { connect: { id: category.id } } }],
},
},
});
console.log({ user: user.email, post: post.slug });
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (error) => {
console.error(error);
await prisma.$disconnect();
process.exit(1);
});
// src/demo.ts
import { prisma } from "./db";
import { createPost, listPublishedPosts, publishPost } from "./posts";
async function main() {
await prisma.user.upsert({
where: { email: "editor@example.com" },
update: {},
create: { email: "editor@example.com", name: "Editor" },
});
const draft = await createPost({
authorEmail: "editor@example.com",
slug: `prisma-${Date.now()}`,
title: "Prisma with Claude Code",
body: "A local demo post.",
categorySlugs: ["prisma", "claude-code"],
});
await publishPost(draft.id);
console.log(await listPublishedPosts({ search: "Prisma" }));
}
main()
.finally(async () => {
await prisma.$disconnect();
});
npm run db:migrate -- --name init_blog
npm run db:generate
npm run db:seed
npm run dev
テストは「transactionが途中で失敗したら中途半端な通知が残らない」など、DB層の事故を狙って書きます。
// src/posts.test.ts
import { describe, expect, it } from "vitest";
import { PrismaClient } from "./generated/prisma/client";
const prisma = new PrismaClient();
describe("post publishing", () => {
it("keeps a post and notification consistent", async () => {
const user = await prisma.user.upsert({
where: { email: "test@example.com" },
update: {},
create: { email: "test@example.com", name: "Test User" },
});
const post = await prisma.post.create({
data: {
slug: `test-${Date.now()}`,
title: "Test post",
body: "Body",
authorId: user.id,
},
});
const published = await prisma.$transaction(async (tx) => {
const updated = await tx.post.update({
where: { id: post.id },
data: { status: "PUBLISHED", publishedAt: new Date() },
});
await tx.notification.create({
data: {
userId: user.id,
type: "POST_PUBLISHED",
message: "Test post was published",
},
});
return updated;
});
const notificationCount = await prisma.notification.count({
where: { userId: user.id, type: "POST_PUBLISHED" },
});
expect(published.status).toBe("PUBLISHED");
expect(notificationCount).toBeGreaterThan(0);
});
});
3つの実用ユースケース
| ユースケース | Prismaで見るべき点 | Claude Codeへの依頼 |
|---|---|---|
| コンテンツサイトの記事管理 | slug一意制約、公開状態、公開日時index、カテゴリ中間テーブル | 「公開一覧のWHERE/ORDER BYに効くindexを説明して」 |
| SaaSのワークスペース機能 | tenantIdの付与、複合unique、権限チェックの抜け漏れ | 「すべてのqueryにtenantId条件が入るかレビューして」 |
| 決済後の権限付与 | transaction、監査ログ、二重実行対策 | 「同じWebhookが2回来ても権限が重複しない設計にして」 |
Prisma ORMは管理画面や記事投稿のような標準的CRUDで特に強いです。型があるので、フィールド名を変えたときにTypeScript側の破損を検知できます。逆に、DB固有の高度なSQLを多用する分析基盤では、Prisma Clientだけに閉じず、生SQLや専用ツールを組み合わせる判断も必要です。
よくある失敗と落とし穴
1つ目は、migrationを見ずに本番へ流すことです。Claude Codeが作ったschema変更にDROP COLUMNやNOT NULL追加が含まれていたら、既存データがある環境では止まる可能性があります。--create-onlyでSQLを作り、PR上で読む癖を付けます。
2つ目は、relationを全部include: trueにすることです。開発中は便利ですが、画面に不要なデータを返し、APIレスポンスが大きくなります。公開APIではselectを基本にして、必要なrelationだけを明示します。
3つ目は、transaction内でメール送信や外部APIを叩くことです。DB transactionは短く終わらせるのが原則です。通知の作成まではtransactionに入れても、実際の送信は別processへ逃がします。
4つ目は、paginationを無制限にすることです。takeをユーザー入力のまま渡すと、重いクエリを簡単に作れます。上の実装ではperPageを最大50に丸めています。
5つ目は、Prisma Clientの生成先やimport pathが混ざることです。既存プロジェクトがprisma-client-jsで@prisma/clientからimportしている場合、いきなりv7系のprisma-client出力へ変えると差分が大きくなります。Claude Codeには「既存のgeneratorを維持するか、移行手順を別PRに分けるか」を確認させます。
Claude Codeレビュー checklist
公開前に、Claude Codeへ次の観点で自己レビューさせます。ただし、最終判断は人間がmigration SQLと主要queryを読んで行います。
- schemaの一意制約、index、relation、削除ルールがユースケースと一致しているか
migrate dev --create-onlyで生成したSQLに危険なDROP、全行lock、既存データを壊すNOT NULL追加がないか- 一覧queryに
take上限、安定したorderBy、必要なselectがあるか - transaction内に外部API、メール送信、長いloopが入っていないか
- seedが冪等で、2回実行してもuniqueエラーにならないか
- testが成功ケースだけでなく、失敗時のrollbackや重複実行も見ているか
- 本番では
migrate deployを使い、db pushを使っていないか
このchecklistをPRテンプレートに入れておくと、Claude Codeの出力を「動くか」だけでなく「運用に耐えるか」で見られます。チームで使う場合は、Claude CodeとRedisキャッシュのような性能改善記事と組み合わせ、DBで解く問題とcacheで逃がす問題を分けるのも有効です。
PRでの役割分担
Claude Codeに実装を任せる場合でも、PRの責任者は人間です。おすすめは、AIに「変更概要」「生成されたmigration」「想定query」「rollback時の影響」をPR本文に書かせ、レビュアーはその説明と実際の差分が一致しているかを見る進め方です。
特にDB変更は、フロントエンドの見た目のように画面で気づけるとは限りません。間違ったunique制約は登録時まで見えず、足りないindexはアクセスが増えるまで見えず、広すぎるcascadeは削除操作まで見えません。だからこそ、schema差分、SQL、seed、testを同じPRで確認できる形にしておく価値があります。
小さなチームなら、最低限「migration SQLを読んだ人」と「API queryを読んだ人」を分けるだけでも効果があります。Claude Codeは説明役として使い、人間はその説明が本当にコードと一致するかを確認する。この分担にすると、AIの速度を活かしながら、DB事故の確率を下げられます。
学習・相談へのソフトCTA
Prisma ORMは導入だけなら簡単ですが、実サービスではschema設計、migration運用、query設計、監査ログ、tenant分離まで含めて品質が決まります。ClaudeCodeLabでは、Claude Codeを使ったデータ層レビュー、既存Prisma schemaの棚卸し、AIコーディング研修の相談も受けています。
個人で型・migration・review promptを体系化したい場合は/products/の教材を、チームでDB変更のworkflowを揃えたい場合は/training/の研修を見てください。「Claude CodeにDB実装を任せたいが、migration事故は避けたい」「PrismaとSupabase/Redisの役割分担を整理したい」という段階なら、記事内のchecklistを持ち込むだけでも相談内容を具体化できます。
この記事で紹介した内容を実際に試した結果
Masaが検証用のブログAPIで同じ流れを試したところ、Claude Codeに最初からmigrationレビュー観点を出させるだけで、onDelete: Cascadeの付けすぎとinclude過多を早い段階で発見できました。特にmigrate dev --create-onlyでSQLを止めて読む工程は効果が大きく、schema差分のレビューが「なんとなく良さそう」から「このindexはこの一覧に効く」と説明できる状態に変わりました。Prisma ORMはClaude Code任せにする道具ではなく、Claude Codeの出力を型、SQL、テストで検査しながら育てるDBレイヤーとして使うのが一番堅実です。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
Claude Code初回リポジトリ監査チェックリスト: 編集前20分で迷子を防ぐ
Claude Codeで既存コードを触る前に、入口、危険領域、検証コマンド、CTA導線を20分で確認する実務チェックリスト。
Claude Code Harness Lite: 初心者が安全に変更するための小さな足場
大きな自動化の前に使う、読み取り、変更、検証、公開確認を分けるClaude Codeの軽量ハーネスです。
Claude CodeのRepo Map初回パス: 既存コードを安全に読み始める手順
Claude Codeで既存リポジトリを読む最初の30分。編集前の地図作り、実例、失敗例、無料PDFと教材導線までまとめます。