はじめに
Junie を使っていて、
「たまに良いコードは出るけど、安定しない」と感じていた時期がありました。
今はほぼ安定しています。
理由はシンプルで、入力と判断を分離し、AI に判断させていないからです。
この記事では、表向きには次の3点について書きます。
- Junie に何を渡しているか
- 何を絶対に渡していないか
- それをどう再現可能な形にしているか
ただ実際には、全部が 「どこで人間が判断を終わらせているか」 に収束します。
Junie に実装を任せる前に、人間が必ず終わらせていること
Junie に実装を任せるとき、自分は「何を書いてほしいか」を考える前に、
何を AI に判断させないかを先に固定しています。
以下は、実際に Junie を使う前に人間側で終わらせているチェックリストです。
人間が先に決めていること(設計判断)
- 今回編集してほしいクラス・ファイルを明示する
- それ以外の既存コードは原則として触らない前提で依頼する
- 新しい責務を切るかどうかは人間が先に決める
- 既存 UseCase / Repository を使うか、新しく作るかを決めておく
- テスト方針(data-driven にするか、fixture をどう扱うか)を決めておく
- 一度始めた作業は最後まで完了させる(止めるのは指示が誤っていた場合のみ)
明示的に用意している入力(Junie に渡す情報)
- GraphQL schema / OAS / data class などの I/F 定義
- 参照してほしい既存実装(ファイル・クラス単位)
- 使用する UseCase / Repository の明示
- 自動生成コードの場所と「自分で作らない」ことの指定
- 従ってほしいガイドラインのファイル
- 制約条件(nullable、range、文字種など)
明示的に渡していないもの(AI にやらせない判断)
- 抽象度の高い設計議論
- ドメイン固有の意味解釈
- 「いい感じに考えてほしい」といった曖昧な指示
- 既存コードの探索や推測(必要な参照は人間が渡す)
作業は「一気に」ではなく、レイヤーごとに進める
「1つの指示で全レイヤーを書き切ってほしい」という依頼は基本しません。
一度にまとめて書かせると差分が膨らみ、人間が把握できなくなるからです。
代わりに、だいたい次の順序で進めます。
- Repository(データ取得・永続化)
- Domain Service(必要な場合)
- UseCase(流れの組み立て)
- Resolver / Controller(I/F の接続)
こうすると、変更量が小さくなり、差分レビューもしやすく、責務の置き場所もブレにくくなります。
Junie には常に 「今のステップで必要なレイヤーだけ」 を実装するよう依頼します。
ドメイン制約やチェック条件は AI に判断させない
入力値の制約や業務ルールのような「チェック条件」は、置き場所がどこであれ、
AI に解釈させず 人間が先に決めて渡すようにしています。
Junie には、その決定をコードに落とす作業だけを任せます。レビューでは少なくとも次を見ます。
- 仕様(型・ガイドライン・設計メモ)と一致しているか
- 失敗時の扱い(エラー型・例外・戻り値)が方針と揃っているか
- 同じチェックが別レイヤーに重複していないか
途中で作業を止めない理由
一度始めた作業は、基本的に途中で止めません。
中断すると「どこまでが完了した指示か」が曖昧になり、以降の修正指示が効きにくくなる気がするためです。(自分のただの直感)
最後まで完了させ、結果を見てから次の指示で修正・制約追加をします。
この運用で得られていることと弱点
設計をここまで固定すると、出力が想定通りかどうかはすぐ分かります。
- 期待していた構造になっているか
- 余計な責務を持ち込んでいないか
- ガイドラインから外れていないか
弱点もあります。
関係ない変更を静かに混ぜ込まれた場合、気づきにくいことです。
書かせた本人はコンテキストを持っていて、指示していない変更は視界から漏れます。
だから可能なら、コンテキストを持たない第三者レビューを入れた方が見つけやすいです。
事例1:GraphQL Mutation / Query の実装
GraphQL の Mutation / Query は、Junie に任せやすく結果も安定します。
理由は単純で、設計判断を渡さない状態を作りやすいからです。
人間が先に終わらせていたこと
- GraphQL schema は人間が定義済み
- schema から必要な型は codegen で生成済み
- レイヤー構成(Repository / Domain Service / UseCase / Resolver)は既存実装で固定
- 参照できる既存の Mutation / Query が存在
この時点で I/F は確定しており、Junie に「解釈」の余地がほとんどありません。
Junie に渡した指示
- 対象 schema
- 参照してほしい既存実装
- 使用する UseCase(既存か新規か)
- 生成済み型の存在とパッケージ
- 編集してほしい Resolver クラス
新規の UseCase を作るなら 名前も先に固定します。
名前を固定すると、責務粒度や置き場所を推測させずに済むからです。
実装の進め方
Resolver から書かせません。
まず下位レイヤー(Repository / Domain Service / UseCase)を固めてから Resolver を書かせます。
この順序の方が差分が小さく、迷いも減ります。
テストについて
テストコード自体は生成させますが、テスト構成方針は人間が握ります。
data-driven にするか、fixture をどう共有するか等は自動では崩れやすいので、次の指示で明示します。
事例2:テストデータジェネレータの生成(Kotest + Arb)
このケースは最も安定します。
入力が data class で、出力も Arb ジェネレータで、設計判断がほぼ入らないからです。
人間が先に終わらせていたこと
- 対象
data classが定義済み - nullable / enum / range などが型として表現されている
- 業務フローやアプリ構成に依存しない
レビュー観点
- 型と Arb の対応が正しいか
- nullable が正しく扱われているか
- enum / range が無視されていないか
- 明らかに意味のない値を生成していないか
正直、制約を最初から渡し切れない部分もあり、
「雑に生成 → テストで落ちる → 制約を追加して直す」は発生します。
ただし、ID の関連整合性だけは必ず見ます。ここが崩れるとテスト全体が壊れるためです。
複雑な Fixture の扱い
複雑な構造(グラフ等)の Fixture は、AI に作らせた上で 機械的に検証します。
妥当性は別途用意したチェック関数で確認し、生成パターンの網羅性はカバレッジ等で見ます。
事例3:OAS に沿った REST API の実装
「既存の GraphQL 機能を REST でも提供する」ケースです。
新しい機能設計ではなく、既存機能を別 I/F として公開するだけ、という前提を人間側で固定しています。
OAS は Junie に書かせる
- 対象 GraphQL schema を指定
- REST として提供したい意図を伝える
- OAS ガイドラインを渡す
ここでも「設計させる」ではなく ルールに従って書かせる扱いです。
良かった点は、YAML の表現作業がほぼ不要だったことです。
REST 実装では構造を固定する
Controller や I/F 定義は自動生成等で固定し、Junie には 中身だけを任せます。
これにより、AI が迷う余地をさらに減らせます。
まとめ:Junie を安定させたのは「AIの工夫」ではなく「人間の線引き」
結論はシンプルです。
Junie を安定して使えるようになったのは、AI が賢くなったからではなく、
AI に判断させる範囲を人間が先に潰したからです。
やっていることは2つだけです。
- 設計判断は人間が握り、AI には実装フェーズだけを渡す
- 参照点と入力を明示して、探索や推測をさせない
弱点は、指示していない変更が混ざったときに気づきにくいことです。
だから第三者レビューや「差分を疑って見る」視点が必要になります。
相性が悪いケースと突破方法:分からない領域に入るとき
Junie は、自分がよく分かっていない領域を出力させたとき、品質は基本的に上がりません。
- 使ったことのないライブラリ
- 内部挙動を理解していないフレームワーク
- 名前だけ知っている仕組み
この状態で出てくる「それっぽいコード」は、自分は怖くて使えません。
挙動を説明できず、妥当性を判断できないからです。
ただし「分かっていないなら使うな」で終わりではありません。
突破できる条件があります。
それは、言語・ライブラリ・フレームワークが どんな用語と概念で実装されているかを掴めたときです。
ここまで見えると、Junie を意図した方向に誘導できます。
自分が実際にやっていること
- 対象のライブラリ / フレームワークを
git cloneしてローカルに持ってくる - IDE でコードを開く
- Junie の ASK / AI Assistant で「用語」「責務」「意図」を質問しまくる
- 自分の言葉で「この世界の中核概念」を整理する
この段階での目的は、コード生成ではありません。
思考の言語を揃えることです。
- どの単語が中核か
- どこが拡張ポイントか
- どこを触ると壊れるか
これが見えるようになって初めて、判断できる側に立てます。
その上で、判断が終わった部分だけを Junie に任せます。
補足:SDDとの関係(あくまで観測として)
正直に書くと、自分自身は SDD を実践していません。
そのため、「SDD をこう使えばうまくいく」といった話をするつもりもありません。
ただ、この記事で書いてきたことを振り返ると、
- 前提・用語・構造を言語化する
- 判断を先に終わらせる
- その判断を、他者やツールに渡せる形にする
という流れは、外から見ると
いま言われている SDD にかなり近いことをやっているとも感じています。
SDDをやっていなくても、避けられないもの
自分が強く感じているのは、
SDD をやるかどうかに関わらず、次の点からは逃げられないということです。
- その spec / 指示 / 前提は妥当か?
- 判断は本当に終わっているか?
- 分からない部分を「分かったこと」にしていないか?
これを評価するためには、
結局 個人の判断力と理解力が必要になります。
自分にとっての結論
今の自分の結論はシンプルです。
- SDD は「判断を代わりにやってくれるもの」ではない
- 判断できる人間が、その判断を伝達・再利用するための形式に近い
- だから、個人の能力を上げる問題からは逃げられない
少なくとも自分にとっては、
判断を終わらせる → その結果を Junie に渡す
という順序が先にあり、
SDD を使うかどうかは、その後に検討する話です。
この順序を逆にしない、という点だけは、
今のところ揺らいでいません。
さいごに
当初は Junie の使い方を書こうと思っていたのですが、
深掘りしていくうちに、
「どう使うか」よりも「どこで判断を終わらせているか」の話になり、
結果としてこのような形になりました。