はじめに
前シリーズ「作って学ぶRails 8入門(環境構築編/実践編)」では、Docker Compose で Rails 8 + MySQL 環境を整え、セルフホスト型URL短縮サービス Shlink のAPIと連携し、Rails標準の Turbo / Stimulus / importmap / Tailwind を活用して最小限のUIを作りました。
これで一応の完成形にはなったのですが、どうしても満足しきれませんでした。
理由は明快です。公式の Shlink UI が、私の希望を満たすものではなかったからです。
Shlinkはバックエンドとしては素晴らしく、REST APIも充実しています。しかし付属の管理UIは「機能はあるけど自分の理想と違う」。
統計は見られるけれどカスタマイズ性が足りず、UIも素っ気なくて使っていてワクワクしない。
「なら、自分で作って育てればいい」──こう考えた私は、Railsで独自UIを作り込み、Shlinkを本当に“自分のもの”にするプロジェクトを始めました。
そしてその相棒として選んだのが Claude Code です。
本記事(後日談 前篇)では、「要件をMarkdownにまとめて渡せば、実装・RSpec・RubocopまでClaudeがやってくれる」開発体制をどのように整えたかを具体的に解説します。
さらに、実際のClaudeとのやりとりログを交えながら、AIとペアプロする臨場感をお伝えします。
要件をObsidianでMarkdown化
AIに「◯◯を作って」と言っても、Railsには複数のやり方があり、曖昧な指示だと意図しないコードが返ってきます。
そこで私はまず Obsidianで要件をMarkdownにまとめることにしました。
Markdown仕様の例
# 機能: Google OAuth での認証追加
## 背景
- 現状: DeviseによるEmail/Password認証を導入予定
- 課題: パスワード管理が面倒なユーザーもいる
- 目的: Googleアカウントでもログイン可能にしたい
## 要件
- OmniAuth (google_oauth2) を利用
- Userモデルに provider, uid を追加。既存メール一致時は同一Userでログイン
- ログイン画面に「Googleでログイン」ボタンを設置
- ENV変数で GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET を管理
- .env.example にダミー値を追記
## 受け入れ基準
- 未ログインで /mypage にアクセス → ログイン画面へ
- Googleログイン成功後は /mypage に遷移
- 既存メールと一致した場合は既存アカウントに紐付け
## 実装ポリシー
- 実装 → RSpec → Rubocop の順に進める
- RSpecは system + request で最低限のカバレッジを担保
- rubocop-rails-omakase を最後に適用
これをClaude Codeに読ませると、驚くほど素直に理解してくれました。
Claude Codeに任せるワークフロー
要件を読ませた後は、基本的に 実装 → RSpec → Rubocop をClaudeに一任しました。
私は結果をレビューして方向性がズレていたら修正依頼を出すだけです。
実際の依頼文
このMarkdown仕様を読み込んでください。
次の順序で作業してください:
1. 実装(要件を満たす差分を生成)
2. RSpec(system/requestテストを追加し、Greenを確認)
3. Rubocop(rubocop-rails-omakaseを適用して整形)
出力には、差分プラン → 実装内容 → RSpec結果 → Rubocop結果を含めてください。
Claudeとのやりとりログ(実例)
1. Devise導入
まずはログイン機能の導入から始めました。
私の依頼
この仕様を元に Devise を導入してください。
手順は 実装 → RSpec → Rubocop の順にお願いします。
Claudeの応答(抜粋)
差分プラン:
- Gemfile に devise を追加
- bundle install
- rails generate devise:install
- User モデルに devise を組み込み
- ビューを scaffold 生成
- RSpec: system テストで sign up / sign in / sign out を確認
実装:
# Gemfile
gem "devise"
# app/models/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
...
ここまで自動で進め、最後にRSpecまで生成してきます。
2. RSpecが落ちたときのやりとり
実装まではよかったのですが、最初のテストは失敗しました。
RSpecログ(Claudeが出力)
Failures:
1) User sign in with valid credentials
Failure/Error: visit new_user_session_path
ActionController::RoutingError:
uninitialized constant Devise::SessionsController
私の返答
Deviseのルーティングが設定されていないようです。
`rails generate devise:install` と `rails generate devise User` を忘れていませんか?
Claudeの修正
ご指摘ありがとうございます。ルーティングとUserモデル生成を追加します。
- rails generate devise:install
- rails generate devise User
- config/routes.rb に devise_for :users を追加
このように、人間がヒントを与えると素直に修正してきます。
3. Rubocop修正の流れ
実装とRSpecがGreenになった後、最後にRubocopを適用します。
Claudeの応答(抜粋)
Rubocop結果:
Offenses:
app/controllers/application_controller.rb:5:1: C: Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
修正しました。再度 Rubocop を実行したところ全てGreenです。
小さなトピック単位で進める
このようにして、Devise → Google OAuth → マイページ → 統計 → QRコード → UI強化、と小さなトピックごとに進めました。
1つずつ確実にGreenにしてコミットする。AI開発でもこれは鉄則でした。
autoload設定のトラブル
途中で遭遇したのが、autoloadの設定問題です。
サービスクラスを app/services/shlink/...
に置いていたのですが、Rails側のautoload設定が効かず定数が解決できませんでした。
Claudeは「ファイルパスを直せばいい」と言いましたが、実際には設定側の問題。
ここは人間が修正をアドバイスし、以後は正しい設定をClaudeも学習してくれました。
この前篇で得られたこと
- 公式UIでは満たせなかった希望を自作UIで補う方向性が定まった
- 要件をMarkdownにまとめてAIに渡すスタイルが安定した
- 実装 → RSpec → Rubocop の流れをClaudeが自動で回せるようになった
- autoloadなどRails固有の問題は人間が補正する必要があると再認識
- 「小粒イシューでGreenを積み上げる」ペースが確立した
まとめ
今回の前篇では、「なぜ自作UIを育てようと思ったのか」から始まり、Claude Codeを相棒に据えた開発スタイルの整備について解説しました。
- 公式UIに満足できず、自分で作る動機が生まれた
- 要件をMarkdownで固定してAIに渡すことで、実装・RSpec・Rubocopを任せられる
- 人間はレビューと判断に専念し、AIは実装と検証を担当
- 小さな単位で確実にGreenを積み重ねることで、短期間でも安定した開発が可能になった
👉 完成版コードはこちら
https://github.com/enjoydarts/shlink-ui-rails
次回の後篇では、実際に追加した機能と「Claudeとバイブコーディングしてみてどうだったか」の所感をまとめます。
コメント