スパイダープラス Tech Blog

建設SaaS「スパイダープラス」のエンジニアとデザイナーのブログ

Claude Code の Learning Style で学ぶ Rust Clean Architecture

はじめに

皆さん、こんにちは。

技術開発部のWです。 普段はWebエンジニアとしてフロントエンドやサーバーサイドの開発に携わっています。最近 Rust を学び始めました。

Clean Architecture の復習を兼ねて、学習中の Rust で最小の TODO アプリを作ってみることにしました。せっかくなので Claude Code の Learning Style という機能を使い、自分でコードを書きながら進める形式で試してみました。

この記事では、その体験の全記録をスクリーンショット付きで紹介します。

想定読者は以下のような方です。

  • Clean Architecture を知っているが、Rust で実装したことはない
  • Claude Code を開発学習に活用してみたい
  • AI ツールに「答えを出させる」のではなく「学びを支援させる」使い方に興味がある

目次

  1. Claude Code の Learning Style とは
  2. 使ったプロンプト
  3. 実践:5ステップの学習体験
  4. Learning Style の3つの特徴
  5. やってみた感想
  6. 最終的なディレクトリ構成
  7. まとめ

Claude Code の Learning Style とは

Claude Code には output-style という設定があり、応答のスタイルを切り替えられます。

/output-style コマンドで Learning を選択すると、Claude Code が以下のように振る舞います。

  • コードを全部書いてくれるのではなく、学習者にコードを書かせる
  • 設計判断が必要な箇所で「Learn by Doing」として課題を出す
  • 実装後にレビューと Insight(教育的な解説)を提供する

つまり、答えを教えるのではなく、考えさせてからフィードバックをくれるメンターに近い使い方ができます。


使ったプロンプト

Learning Style を有効にした上で、以下のプロンプトを入力しました。

RustでClean Architectureの基本を学ぶためのtodoアプリ用README.mdを作成してください。

- 想定ディレクトリ: clean-architecture-rust/
- 目次を必ず含める
- 全体の想定学習時間は合計15分以内とする
- README内にコード例は一切記載しない
- Claude Codeによるコード生成・ファイル作成も禁止
- 全体100行以内でまとめる
- 各ステップに学習者への問いかけを含め、自分で考えてから手を動かす構成にする
- 学習者自身がコードを書く前提で、各ステップには実装の指針(何を・どこに)とヒントだけ示す

このプロンプトで特に工夫したポイントは3つです。

  • 「コード例は一切記載しない」「学習者自身がコードを書く前提」 ── Claude Code にコードを書かせるのではなく、学習のロードマップだけを作らせて、実装は自分でやるという方針
  • 「全体の想定学習時間は合計15分以内」 ── これは実際の学習時間の見積もりではなく、README の出力ボリュームを抑えるための制約です。「15分で終わる内容量」と指定することで、100行以内に収まる粒度にコントロールしています
  • 「全体100行以内」 ── 上と合わせて、各ステップを「問いかけ + 実装指針 + ヒント」の3要素に絞らせるための制約

プロンプトを入力すると、Claude Code はいきなり作業に入るのではなく、3つの確認質問をしてきました。

  • 学習者のレベル: Rust 初学者も含むか
  • 永続化層: インメモリ(Vec)かファイル保存か
  • 操作範囲: 追加・一覧表示のみか、CRUD 全部か

15分という制約の中で何にフォーカスするか、Claude Codeからスコープの確認を求められています。ここでは「Rust 初学者も含む」「インメモリのみ」「追加・一覧表示のみ」を選択しました。


実践:5ステップの学習体験

Claude Code が作成した README は100行ぴったり。5ステップ × 各3分の構成です。

ここから実際の実装に入ります。

Step 1:ドメイン層 ── Todo struct を定義する

Claude Code はプロジェクトの初期化(cargo init)とディレクトリ構造、mod.rs などの定型コードを準備した上で、src/domain/todo.rs にこんなプレースホルダーを置きました。

// TODO(human): ここに Todo struct を定義してください

そして「Learn by Doing」として課題が提示されます。

Your Task: src/domain/todo.rs の TODO(human) を置き換えて、Todo struct を定義してください。「追加」と「一覧表示」という2つの操作から逆算して、必要最小限のフィールドを考えてみてください。

ヒントとして「識別子が必要」「#[derive(...)] で Debug と Clone を付けると後が楽」という情報だけが与えられます。答えは教えてくれません。

実際にエディタで書いてみました。

書き終えて「確認してください」と伝えると、Claude Code がレビューしてくれます。

最初のレビューでは、まず completed フィールドについてコメントがありました。今回のスコープは「追加・一覧表示」だけなので completed は不要です。ただ、TODO アプリなら完了状態は持っていて当然だろうと思い、あえて入れていました。

Claude Code の反応は「今回のスコープでは直接使わないが、TODO として自然な属性なので問題ない」。スコープ外だから削除しろ、ではなく設計意図を汲んでくれたのは少し意外でした。

その上で2点の修正指摘がありました。

  1. 可視性(pub)が未指定 ── struct 自体とフィールドの両方に pub が必要
  2. #[derive(...)] が未指定 ── Debug があると println! で表示でき、Clone は後のステップで Vec から取り出すときに役立つ

この2点を修正して再度確認を依頼すると、さらにフィールドレベルの pub が足りないという指摘がありました。

pub struct にしてもフィールドはデフォルト非公開で、他の層から todo.title と書いてもコンパイルエラーになるという指摘です。Rust の可視性は struct とフィールドが独立しているという設計思想も教えてくれました。

このように段階的にレビューを受けながら修正し、最終的に以下の形で Step 1 が完了しました。

#[derive(Debug, Clone)]
pub struct Todo {
    pub id: u32,
    pub title: String,
    pub completed: bool,
}

ステップの順序変更 ── コンパイル駆動開発

Step 1 が終わると、Claude Code は README の Step 2(ユースケース層)ではなく、Step 3(リポジトリ境界)を先にやろうと提案してきました。

理由は、ユースケース層がリポジトリの trait に依存するため、trait を先に作れば cargo check が通る状態を保てるから。

Claude Code はこれを「コンパイル駆動開発」と呼んでいました。各ステップでコンパイルを通しながら進めるアプローチで、Rust の型システムと相性が良い進め方です。

Step 3 → Step 2:リポジトリ境界とユースケース層

リポジトリの trait 定義では、ヒントでは Vec<Todo> を直接返す案が示されていましたが、自分の判断で Result<T, String> を戻り値に使いました。Rust では失敗しうる操作に Result を使うのが慣習であり、リポジトリのようなデータ操作は本来失敗する可能性があるものだと考えたからです。

pub trait TodoRepository {
    fn save(&mut self, todo: Todo) -> Result<Todo, String>;
    fn get_all(&self) -> Result<Vec<Todo>, String>;
}

これに対して Claude Code は「実践的な判断」と評価してくれました。

ヒントを超えた判断にもフィードバックが返ります。さらに Insight として「実務では enum TodoError のような独自エラー型を定義するのが一般的」という発展的な情報も教えてくれました。

続いてユースケース層です。ここでは TodoUsecase<R: TodoRepository> という書き方をします。<R: TodoRepository> はジェネリクス(型パラメータ)を使った書き方で、「TodoRepository trait を満たす任意の型 R を受け取れる」という意味です。この仕組みにより、ユースケース層は具体的な保存方法を知らないまま動作できます。

ここで Claude Code が指摘した重要な点があります。

このファイルの use 文を見てください。crate::domain::todocrate::repository::todo_repository だけをインポートしています。crate::infrastructure は一切登場しません。これが Clean Architecture の依存性逆転が正しく機能している証拠です。

use 文から依存関係を読み取るという視点は、自分だけでは気づきにくいポイントでした。

Step 4:所有権エラーとの出会い

インフラ層(InMemoryTodoRepository)の実装で、Rust ならではの学びがありました。

Claude Code からは「Vec<Todo> をフィールドに持たせる」「save では push した後に保存した Todo を返す(Clone が役立つ場面)」というヒントが出されました。

このヒントを頼りにエディタで実装してみました。

最初に書いたコードはこうです。

fn save(&mut self, todo: Todo) -> Result<Todo, String> {
    self.todos.push(todo);   // ← ここで todo の所有権が Vec に移動
    Ok(todo)                 // ← 移動済みの todo を使おうとしてエラー
}

Claude Code はすぐにこの問題を指摘しました。

Rust の所有権(ownership)システムが防いでくれる典型的なバグです。C/C++ では解放済みメモリへのアクセスとして実行時に問題になりますが、Rust ではコンパイル時に検出されます。

指摘されて「あ、これか」と。Step 1 で #[derive(Clone)] を付けたのがここで活きました。clone() で複製を作ってから push する修正で解決です。

fn save(&mut self, todo: Todo) -> Result<Todo, String> {
    let saved = todo.clone();  // 先に複製を確保
    self.todos.push(todo);     // オリジナルを Vec に移動
    Ok(saved)                  // 複製を返却
}

Step 5:main.rs で組み立てる ── Composition Root

最後のステップでは、全層を main.rs で組み立てます。Claude Code はここを「Composition Root(合成ルート)」と呼んでいました。DI(依存性注入)を行う場所、つまり「どの具体型を使うか」を決定する唯一のファイルです。

use crate::infrastructure::in_memory_repository::InMemoryTodoRepository;
use crate::usecase::todo_usecase::TodoUsecase;

fn main() {
    let repository = InMemoryTodoRepository::new();
    let mut usecase = TodoUsecase::new(repository);

    usecase.add_todo("Buy groceries".to_string()).unwrap();
    usecase.add_todo("Walk the dog".to_string()).unwrap();
    usecase.add_todo("Read a book".to_string()).unwrap();

    let todos = usecase.get_all_todos().unwrap();
    println!("TODO List:");
    for todo in todos {
        println!("- {}", todo.title);
    }
}

cargo run を実行すると、無事にビルドが通り、動作しました。

TODO List:
- Buy groceries
- Walk the dog
- Read a book

振り返り:依存の方向を図で理解する

全ステップ完了後、Claude Code から3つの振り返り問題が出されました。

  1. 保存先を Vec からファイルに変える場合、どの層のどのファイルだけを変更すれば済むか?
  2. ユースケース層のコードは保存方法の変更に影響を受けるか?
  3. 自分の実装で「依存の方向は常に外→内」のルールは守れているか?

私の回答は以下の通りです。

  1. infraの実装を主に変更して、mainでインスタンスを生成
  2. 保存方法はユースケースは知らないため影響はない
  3. 常に外から内になっている、内: domain ← repository(domainだけuse::crate) ← usecase(domainとrepositoryをuse::crate) ← infra(domainとrepositoryをuse::crate) ← main :外

これらに答えると、Claude Code が同心円の図を出してくれました。

そして依存性逆転のポイントも図示されました。

この図の核心は、usecaseinfrastructure が直接繋がっておらず、repository trait を介して間接的に繋がっている点です。矢印が内側に向かって合流する構造が、依存性逆転の原則(DIP)そのものです。


Learning Style の3つの特徴

ここからは実践を振り返って、Claude Code Learning Style の特徴を3つにまとめます。

1. TODO(human) パターン

Claude Code は定型コード(mod.rsuse 文)だけを用意し、本質的な設計判断が必要な部分に TODO(human) を置きます。

さらに、各ファイルの冒頭には // ドメイン層: TODO エンティティ // この層は他のどの層にも依存しない のようなコメントが自動で記載されます。これも Claude Code がヒントとして付けてくれたもので、エディタでコードを書いている最中に「今自分がどの層を書いていて、どんな制約があるか」を確認できます。

「何を考えるべきか」が明確になり、自分で書くぶん記憶にも残ります。

2. レビュー → 修正のサイクル

書いたコードを「確認してください」と伝えると、レビューが返ってきます。

今回の体験では、Step 1 だけで2回のレビューサイクルがありました。1回目で pub#[derive] の未指定を指摘され、修正後の2回目でフィールドの pub 漏れを追加指摘される、という流れです。段階的にコードが改善されていく過程は、実際のコードレビューに近い感覚でした。

3. Insight による文脈の補足

各ステップの前後で「Insight」と題した教育的な解説が挿入されます。

特に印象的だったのは以下の3つです。

  • use 文から依存関係を読み取れるという指摘(Step 2)
  • 所有権エラーを #[derive(Clone)] が解決するという Step 1 との伏線回収(Step 4)
  • main.rs = Composition Root という Clean Architecture の用語との対応付け(Step 5)

単にコードの正誤を判定するのではなく、「なぜそうなるのか」「他のどこと繋がっているのか」を補足してくれる点が、ドキュメントを読むだけの学習との大きな違いでした。

やってみた感想

学習時間について

前述の通りプロンプトの15分はボリューム調整用の制約で、実際の所要時間はレビューのやりとりを含めて 1時間弱 でした。写経では得られない理解が伴っているので、学習の密度としては十分です。

Claude Code Learning Style が向いている場面

  • 新しい言語 × 新しい設計パターンの組み合わせ。今回の「Rust × Clean Architecture」のように、両方を同時に学びたいときに特に効果的でした
  • 一人で学習していて、レビューしてくれる人がいないとき。段階的なフィードバックが得られるので、間違った理解のまま進むリスクが減ります

注意点

  • Learning Style は考えさせてくれる反面、効率重視の作業には向きません。既知の技術でサッと実装したい場合は通常モードの方が適切です
  • プロンプト設計が体験の質を左右します。今回のように「コード例は載せない」「学習者自身がコードを書く」という制約を入れることで、学習メンターとしてうまく機能しました
  • 同じプロンプトでも、毎回同じ出力になるとは限りません。ステップの順序変更(今回の「コンパイル駆動開発」による Step 3 先行)や Insight の内容は、実行のたびに変わる可能性があります。この記事はあくまで一回の体験記録です

Clean Architecture × Rust の学び

Rust の型システム(trait、ジェネリクス、所有権)が Clean Architecture の原則を自然に強制してくれる、というのが今回の実感です。

  • trait が依存性逆転の原則(DIP)を実現する
  • ジェネリクスがユースケース層を具体実装から切り離す
  • 所有権システムがデータの流れを明示的にする

他の言語では規約やレビューで守るべきルールを、Rust ではコンパイラが強制してくれます。


最終的なディレクトリ構成

clean-architecture-rust/
└── src/
    ├── main.rs                              # Composition Root
    ├── domain/
    │   ├── mod.rs
    │   └── todo.rs                          # Todo struct(エンティティ)
    ├── repository/
    │   ├── mod.rs
    │   └── todo_repository.rs               # TodoRepository trait(境界)
    ├── usecase/
    │   ├── mod.rs
    │   └── todo_usecase.rs                  # TodoUsecase(ビジネスロジック)
    └── infrastructure/
        ├── mod.rs
        └── in_memory_repository.rs          # InMemoryTodoRepository(具体実装)

まとめ

Claude Code の Learning Style を使うことで、「教わる」のではなく「自分で考えて書く」学習体験ができました。

Clean Architecture の概念は書籍やブログで理解できますが、実際に層を分けてコードを書き、use 文の依存方向を自分で確認し、所有権エラーと格闘する過程で、理論が実感に変わりました。

Clean Architecture を別の言語で実装してみたい方、あるいは AI ツールを学習支援に活用してみたい方は、ぜひ Claude Code の Learning Style で試してみてください。

スパイダープラスでは仲間を募集中です。 スパイダープラスにちょっと興味が出てきたなという方がいらっしゃったらお気軽にご連絡ください。

Claude Code / AgentTeams に批判的エージェントを入れたら、精度が上がった「気がした」話

最近、AI駆動開発(AI-driven development) という言葉をよく見かけるようになりました。 AIにちょっと手伝ってもらう開発から、AIが主体でタスクを回す開発へ。そんな流れが加速しているように感じます。

ClaudeCode の Swarm Mode は、まさにその流れの中で生まれた仕組みで、 複数のエージェントに役割を振って、チームとして動きます。

本記事では、Swarm Mode をベースにプロンプトとオーケストレーションを独自に構成したマルチエージェント体制を 「AgentTeams」 と呼んでいます。Swarm Mode のネイティブな動作(タスク分割 → 子エージェント並列実行 → 親エージェント統合)とは異なり、今回は役割ごとに直列パイプラインを組んで制御しています。

この AgentTeams に 批判的エージェント(Critical Agent) を追加してみたら、アウトプットの質が体感で良くなった、という話をまとめてみました。

  • どんな Agent 構成だったのか
  • なぜ批判的エージェントを入れたのか
  • なぜ効果があったと感じたのか(仮説)

あくまで実践ログベースの話なので、定量評価や厳密な比較実験はしていないです。現場の試行錯誤の記録として読んでもらえると嬉しいです。

続きを読む

AWS Certified Solutions Architect - Associate(SAA-C03)を受験しました

はじめに

プロジェクトマネージャーがSAA-C03を受験した道のりをご紹介します。

受験に至る経緯

某プロジェクトにPMとして参画し、要件を整理するフェーズにて、お客様側はセキュリティ部門やインフラ部門などの関係者が参画されていました。こちらも顧客の期待に応えるため、知識向上を目的としてAWSの学習に取り組みました。 そして、どうせやるなら目標を立てようと思い、学習の結果が一番わかりやすい資格取得にチャレンジしました

学習時間と方法

私の場合、まだ子供が小さく、休日などは出来るだけ子供達に時間を使いたいという思いがありました。

そこで勉強タイミングはほぼ毎日朝の5時30分~7時に実施していました。

基礎の学習

AWSの試験は、AWSのサービスが増えたりアップデートされ続けている事もあり、書籍ではなくUdemyで学習する事にしました。 まずはこちらの動画を視聴・実践しながら、コツコツと学んでいきました。 チャプター毎に、学習した事の振り返りの問題と実践形式で手を動かすので、非常に分かりやすかったです。 www.udemy.com

問題集での学習

ある程度、理解が進んでからは、試験用にWebの問題集などもやっていきました。 通勤時など電車内でも気軽に出来るのが良いですよね。 aws-exam.net

受験の1ヶ月前には、模擬試験としてこちらも実施しました。

www.udemy.com

試験の本番

全65問あり、試験時間は130分初めは順調に進めていましたが、最後はかなり時間ギリギリでの終了となってしまい、これは2回目の受験も必要か・・・と絶望していましたが、なんとか合格出来ました。 (720点が合格ラインで766点 笑)

振り返り

技術資格の受験は約15年ぶりだったので、試験中の緊張感などもあり、もう少し慣れていればスムーズだったかなと思いました。 またAWS認定試験には普段慣れているのとは少し異なる言い回しなどもあり、文章を読解する力も必要だと感じました。

最後に

スパイダープラスでは「デジタルスキル資格取得支援」として、AWS認定資格などの受験料補助の制度があり、非常に助かりました。 今後は学習した知識を活かして業務に貢献出来ればと思います。

スパイダープラスでは仲間を募集中です。 スパイダープラスにちょっと興味が出てきたなという方がいらっしゃったらお気軽にご連絡ください。

spiderplus.co.jp

建設技術者から建設DXの知財推進者への転身。プロダクト開発と並走で新たな価値創出を

【自己紹介】

こんにちは。知財推進部のFukaです。スパイダープラスに入社して約半年が経過しようとしています。
私はこれまで約10年間、土木構造物の設計や施工計画を行う建設コンサルタントに従事してきました。

その後、次なるステップとして選んだのは、建設系メーカーの営業会社でした。
当時は建設業のお客様への営業提案を行いながら、自社プロダクトのみならず、他社サービスとの連携を通じた価値創出を考え、日々建設DXの動向を広範に調査していました。

このように、建設業界を『設計・計画』から支えた経験と、外側から『技術動向』を俯瞰した経験。建設DXという大きな波の中で、これまでの建設技術者としての歩みと、知財という新しい専門性が掛け合わさる可能性に強く惹かれたことが、建設DXの知財推進者というキャリアへ至るきっかけとなりました。

【建設業界について】

建設コンサルタント時代、私は施工現場そのものに立つ機会は多くありませんでしたが、膨大な図面や報告書と格闘する日々を過ごしていました。
業務内容によっては、デスクの左右に分厚いキングファイルの山や紙の図面を置きながら、デスクで細かな作業に追われることも珍しくありませんでした。

続きを読む

ターミナル環境を100倍楽しくする

こんにちは。EMの本田です。
まずはこれを見てください。

Ghosttyでシェーダーを利用できると知り、ゲームエフェクトのような見た目をターミナル環境で実現すべくAIでGLSLを作成した結果、上のようなアニメーションのターミナル環境が爆誕しました⚔️

去年の記事では Alacritty を使っていたのですが、シェーダーを使いたかったので乗り換えました。 また、Neovimの smear-cursor.nvim で似たようなことができたのですが、Ghosttyの場合はターミナル全体で使えるのとアニメーションの自由度が高いのがやはり良いです。 techblog.spiderplus.co.jp

以下、設定例へのリンクです。

カーソルの軌道を表示

これが最も実用性があります。稲妻のような無駄なエフェクトに関しては実用性皆無の趣味です。

タイピング時の爽快感

打鍵時の気持ちよさは快適な体験をもたらします。気持ち以外の実用性は薄いです。

カーソル静止状態

ターミナルに目線を戻したときのためにカーソルを目立たせる事ができます。

ghostty config例

~/.config/ghostty/config では以下のように複数のシェーダーを指定することができます。

custom-shader = ./shaders/cursor_blaze.glsl
custom-shader = ./shaders/cursor_lightning.glsl
custom-shader = ./shaders/sparks.glsl
custom-shader = ./shaders/slash.glsl
custom-shader = ./shaders/gravity.glsl

感想

GLSL作成に関してはド素人だったのでAIの力をかなり借りました。
こういったアニメーション効果のイメージをAIに伝えるのがとても難しく、めちゃくちゃラリーしたのも良い思い出です。
使い始めてそろそろ半年くらいになるのですが、いまだにターミナル操作時の満足度が非常に高いです。

まとめ

ターミナルはロマン。

おまけ

これをチーム内で紹介したらノリの良いメンバーがとてもユニークな設定を作って披露してくれました 🔫

最後に、スパイダープラスでは仲間を募集中です。スパイダープラスにちょっと興味が出てきたなという方がいらっしゃったらお気軽にご連絡ください。ご覧いただき、ありがとうございます。

参照

GLSLを書くに当たり、KroneCorylus/ghostty-shader-playground のコードを参考にしました。作者の方、ありがとうございます🙏

手動リグレッションテストから自動化フィジビリティ調査への挑戦

はじめに

こんにちは。プロダクト品質部のSです。
プロダクトの成長に伴い、私たちは常に「リリースのスピード」と「品質の担保」の両立という課題に直面しています。
今回は、増大し続けるリグレッションテストの負荷を解消すべく取り組んだ、テスト自動化フィジビリティ(実現可能性)調査の道のりをご紹介します。

続きを読む

カスタマーサポートからCREへ。 未経験からエンジニア転職を目指した私のリアル

こんにちは。スパイダープラスCREチームの脇です。

今回の記事では、

エンジニア知識0スタートのカスタマーサポートから社内のCREチームへキャリアチェンジした実体験をもとに、
何を学び、どんな壁にぶつかり、どう乗り越えたのかをお話していきたいと思います。
未経験から技術サイドに踏み込む人の参考になれば幸いです。

続きを読む