😊

Claude Skills と Clojure REPL 駆動開発

に公開
2025/12/22

Clojure Slackの #ai-assist-coding チャンネルで、Clojure と LLM の相性についての議論の中で以下のようなコメントを見かけました:

  • 「skill.mdを使うとコード品質が向上する」
  • 「REPLとAIの相性は最高。It's like peanut butter and chocolate!」

本当にそうなのか?実際に試してみました。

この記事では、簡単なClojureプロジェクトを通じて、skill.mdがどのように機能するか、そしてREPL駆動開発がAIとどう相性が良いのかを共有します。

1. skill.mdとは?

skill.md は、Claude Codeに対してプロジェクト固有のルールや原則を教えるための仕組みです。

1.1. 基本構造

---
name: my-skill-name
description: このスキルが何をするか、いつ使うかの明確な説明
---

# スキルの内容をMarkdownで記述

1.2. 重要なコンセプト

  • Progressive Disclosure(段階的開示): 必要な情報を必要なタイミングで開示
  • ファイルシステムベース: .claude/skills/ ディレクトリに配置
  • コンテキスト効率: 簡潔で表現力のあるドキュメントが推奨される

特にClojureは言語自体が簡潔なため、skill.mdもコンパクトに保ちやすく、コンテキストウィンドウを圧迫しないという利点があります。

2. 実践:EDNファイルベースのTODO管理ツール

2.1. プロジェクト概要

シンプルなコマンドラインTODO管理ツールを作成しました。

  • 言語: Clojure
  • データ永続化: EDNファイル
  • 機能: CRUD操作、優先度管理、ソート

2.2. 開発の流れとskill.mdの理解

skill.mdを作るタイミング

skill.mdをいつ作るかについて、2つのアプローチがあります。

アプローチ1: 既存コード → skill.md(今回採用)

既存コード作成 → ルール抽出 → skill.md作成 → 新機能で活用

メリット

  • 実際のコードから具体的なルールが明確になる
  • 良い例・悪い例を実コードから抽出できる
  • プロジェクトの実態に即したルールになる

アプローチ2: skill.md → 新規プロジェクト

理想のルール定義 → skill.md作成 → ゼロから実装

メリット

  • 最初から品質基準が明確
  • ベストプラクティスを最初から適用できる
  • 技術的負債を防げる

今回は、アプローチ1を採用しました。まず基本機能を実装し、そこから守るべきルールを抽出してskill.mdを作成、その後新機能追加でskill.mdを活用するという流れです。

2.3. Skills に関する注意

skill.mdに書くのは「ルール」であって「機能仕様」ではありません。

✅ skill.mdに書くこと(ルール・原則):
- 純粋関数を使う
- pre条件を書く
- テストを先に書く
- docstringの書き方

❌ skill.mdに書かないこと(機能仕様):
- 優先度機能を追加する
- タグ機能を作る
- ソート機能を実装する

別プロジェクトでskill.mdを再利用するときは

同じskill.mdを別ディレクトリにコピーして使うと、同じコーディング規約で全く異なる機能のアプリケーションが作成できます。

# skill.mdを別プロジェクトにコピー
cp -r .claude/skills /path/to/calculator-app/.claude/

# Claudeに指示
"このskill.mdのルールに従って、電卓アプリを作って"

→ TODOアプリと同じ品質基準(純粋関数、pre条件、テストファーストなど)で、全く別の機能(電卓)が作られます。

2.4. 初期実装(skill.mdなし)

まず、skill.mdを使わずに基本機能を実装します。

(defn create-todo [todos title]
  {:pre [(string? title) (not (empty? title))]}
  (let [id (next-id todos)
        new-todo {:id id
                  :title title
                  :completed false
                  :created-at (Date.)}]
    (conj todos new-todo)))

この段階で以下を作成:

  • src/todo_edn/core.clj - コアロジック
  • src/todo_edn/cli.clj - CLIインターフェース
  • test/todo_edn/core_test.clj - テスト

ここで公開しています。

https://github.com/shinseitaro/claude-skills-repl-guide

2.5. skill.mdの作成

プロジェクト固有のルールをまとめたskill.mdを作成します。

今回の主な内容:

  1. 純粋関数と副作用の分離

    • 純粋関数を基本とする
    • 副作用を含む関数には ! を付ける
  2. pre/post条件の活用

    • すべての公開関数に事前条件を設定
  3. 包括的なドキュメント

    • 目的、引数、戻り値、副作用の有無を明記
  4. テストファースト

    • 新機能は必ずテストから書く
  5. 命名規則

    • 動詞-目的語の形式
    • 述語は ?、副作用は !
  6. エラーハンドリング

    • 何が起きたか、なぜか、どうすればよいかの3要素

実際の skill.md はこちらです。

https://github.com/shinseitaro/claude-skills-repl-guide/blob/main/.claude/skills/todo-edn-development/SKILL.md

2.6. skill.mdを使った新機能追加

.claude/skills/todo-edn-development/SKILL.md を配置したあと、Claudeを再起動し、

優先度機能を追加してください

など指示します。「skill.mdを使って」と明示的に指示する必要はありません。
Claude Codeが自動的に

  • skill.mdを読み込む
  • ガイドラインに従ってコード生成
    • テストファースト
    • pre条件追加
    • 詳細なdocstring記述

を行います。

3. skill.mdの効果

3.1. 良かった点

  1. 一貫性の向上

    • コーディング規約が明文化され、迷わずに実装できた
    • docstring、pre条件、命名規則が統一された
  2. 品質の向上

    • テストファーストの習慣が強化された
    • エラーハンドリングが丁寧になった
  3. 自己レビューの質向上

    • チェックリストがあるため、コミット前の確認が簡単
    • 見落としが減った
  4. 学習ツールとして機能

    • Clojureのベストプラクティスをまとめる良い機会になった
  5. コンテキスト効率

    • Clojureの簡潔さにより、skill.md自体もコンパクト
    • AIのコンテキストを圧迫しない

3.2. 課題・改善点

  1. メンテナンスの必要性
    • プロジェクトの成長に合わせて更新が必要
    • 定期的なレビューが重要
  2. 過度な規則は避ける
    • 本当に必要なルールだけを記載
    • 例:
      • すべての関数にパフォーマンステストを書く(← まだパフォーマンス問題は起きていないのに)
      • 3段階のログレベルを使い分ける(← 今は println で十分なのに)
      • データベース抽象化レイヤーを必ず使う(← EDNファイルだけで十分なのに)
  3. 具体例の重要性
    • 良い例・悪い例があると理解しやすい
    • プロジェクト固有の例を追加すると効果的

4. REPL駆動開発の実践:ファイルを編集せずに新機能を試作

skill.mdとは別に、Slackスレッドで熱く語られていた「REPLとAIの相性の良さ」も体験してみました。

4.1. Slackでのコメント

"REPLは、AIがコードベースと解決空間を探索し、必要に応じて原子的かつインタラクティブにアプローチを検証できるという点で、他のすべてを上回ります。コーディングエージェントにとって、まさにピーナッツバターとチョコレートの組み合わせのようなものです。" selahb 2025/12/17 01:52:37

"AIはファイルを一切編集することなくソリューションを開発できます。同じ言語を使う別の環境ではなく、実際のアプリケーションから現実のフィードバックを得られるのです。" pez 2025/12/17 04:26:53

これが本当かどうか、実際に試してみました。

4.2. 実験:タグ機能をREPLだけで試作

目標: ファイルを一切編集せずに、REPLだけで新機能「タグ機能」を完全に試作・検証する

ステップ1: REPLで既存機能を確認

;; REPLを起動
$ clj -M:dev

;; 名前空間をロード
user=> (require '[todo-edn.core :as todo])

;; 既存機能を試す
user=> (def my-todos [])
user=> (def my-todos (todo/create-todo my-todos "REPLで実験"))
user=> (def my-todos (todo/create-todo-with-priority my-todos "重要タスク" :high))

;; 確認
user=> (clojure.pprint/pprint my-todos)
[{:id 1,
  :title "REPLで実験",
  :completed false,
  :priority :medium,
  :created-at #inst "2025-12-20T07:13:39.046-00:00"}
 {:id 2,
  :title "重要タスク",
  :completed false,
  :priority :high,
  :created-at #inst "2025-12-20T07:13:39.046-00:00"}]

ステップ2: 新機能をREPL上で定義(ファイル編集なし!)

;; タグを追加する関数
user=> (defn add-tag [todos id tag]
         (mapv (fn [todo]
                 (if (= (:id todo) id)
                   (update todo :tags (fnil conj #{}) tag)
                   todo))
               todos))

;; タグでフィルタする関数
user=> (defn filter-by-tag [todos tag]
         (filterv #(contains? (:tags % #{}) tag) todos))

ステップ3: 動作確認

;; タグを追加
user=> (def my-todos (add-tag my-todos 1 :work))
user=> (def my-todos (add-tag my-todos 1 :urgent))
user=> (def my-todos (add-tag my-todos 2 :work))

;; 確認
user=> (clojure.pprint/pprint my-todos)
[{:id 1,
  :title "REPLで実験",
  :tags #{:work :urgent},  ;; タグが追加された!
  ...}
 {:id 2,
  :title "重要タスク",
  :tags #{:work},
  ...}]

;; フィルタリング
user=> (filter-by-tag my-todos :work)
;; => 両方のTODOが返ってくる

user=> (filter-by-tag my-todos :urgent)
;; => 最初のTODOだけ

ステップ4: 既存機能との統合を確認

ここで重要なのは、実際のアプリケーションで統合を確認できることです。

;; 優先度ソート(既存機能)とタグフィルタ(REPL試作)を組み合わせ
user=> (->> my-todos
            todo/sort-by-priority
            (#(filter-by-tag % :work))
            clojure.pprint/pprint)

あれ、エラーが出ました!

Execution error (IllegalArgumentException) at user/filter-by-tag (REPL:4).
Don't know how to create ISeq from: clojure.lang.Keyword

ステップ5: バグをREPLで即座に発見・修正

これがREPLの真価です。パイプラインの引数順序が間違っていることに気づきました。

;; ❌ 間違い:filter-by-tagの第一引数がキーワードになっている
(->> my-todos
     todo/sort-by-priority
     (filter-by-tag :work))  ;; :workが第一引数に渡される!

;; ✅ 修正:無名関数でラップして正しい順序に
(->> my-todos
     todo/sort-by-priority
     (#(filter-by-tag % :work)))  ;; %(todos)が第一引数、:workが第二引数

修正後、即座に実行:

user=> (->> my-todos
            todo/sort-by-priority
            (#(filter-by-tag % :work))
            clojure.pprint/pprint)

[{:id 2,
  :title "重要タスク",
  :priority :high,
  :tags #{:work}}
 {:id 1,
  :title "REPLで実験",
  :priority :medium,
  :tags #{:work :urgent}}]

;; 動いた!優先度高い順 + :workタグでフィルタリング成功

REPLの威力を実感

この実験で以下を体験しました。

  1. ファイル編集なしで新機能完成 - add-tag、filter-by-tagを完全に実装・テスト
  2. 即座のフィードバック - バグを秒速で発見・修正
  3. 実アプリで統合確認 - 既存のsort-by-priorityと完璧に統合
  4. 探索的プログラミング - エッジケースも即座に試せる
  5. 失敗のコストゼロ - 何度でもやり直せる

この実験について: 今回は私(人)が行いましたが、AI(Claude Code + clojure-mcp-lightなど)がREPLツールを使って自律的に試作できます。例:

人: "タグ機能をREPLで試作して"
     ↓
AI (Claude Code + clojure-mcp-light):
  - REPLに接続
  - 関数を定義
  - テスト実行
  - 結果を確認
     ↓
AI: "動作確認できました。ファイルに追加しますか?"

4.3. Pythonならどうなる?

Slackで以下のような指摘がありました。

"Pythonでは、コーディングエージェントがCLIで使い捨てのPythonファイルを書いて、実行して、削除するのを頻繁に見てきました。これはセッション永続性やインタラクティビティという利点のない、貧者のREPLです。" selahb 2025/12/17 23:58:33

Pythonで再現すると、こういうことになりそうです。

# 1. 試作用ファイルを作る
$ cat > test_tags.py << EOF
def add_tag(todos, id, tag):
    # ... implementation
EOF

# 2. 実行
$ python test_tags.py

# 3. バグ発見!編集
$ vim test_tags.py

# 4. 再実行
$ python test_tags.py

# 5. 削除
$ rm test_tags.py

Clojure REPL を使った開発であれば、

  • ファイル作成・削除不要
  • セッション永続性あり
  • 即座の再定義と実行
  • 名前空間サポートで整理された探索

ができるので、そういうことを「peanut butter and chocolate(ピーナッツバターとチョコレート)」と表現しているんだと思います。

5. Skills と REPL 駆動開発の おすすめの始め方

skill.md

  1. 小さく始める: 既存プロジェクトの重要なルール2-3個から
  2. 具体例を入れる: 良い例・悪い例で分かりやすく
  3. 定期的に更新: コーディングセッションをレビューして改善
  4. チェックリスト活用: コミット前の確認を習慣化

REPL駆動開発

  1. 新機能はREPLで試作: ファイルを編集する前にREPLで検証
  2. 既存機能との統合確認: 実際のアプリケーションで統合テスト
  3. 自信を持ってファイルに追加: REPLで検証済みなので安心

skill.mdとREPL駆動開発、どちらもAIとのペアプログラミングをより生産的にする強力なツールです。ぜひ試してみてください!

6. 参考リソース

7. プロジェクトコード

主な構成:

Discussion