本文に登場するコードや仕様は実際のプロダクトとは関係ありません。
長年プロダクトを支えてくれたコードは「秘伝のタレ」として存在することがあります。この記事ではAIを用い、その秘伝のタレに安全に追加・改修を加える際にアプローチを紹介します。
私が直面した秘伝のタレには以下の特徴があります。
- 長年の改修が積み重なっている
- 仕様書がない
- テストコードがない
この状態にいきなり改修を継ぎ足しするとどこか壊してしまう恐れがあります。
そこでAIを用い以下の段階的アプローチで継ぎ足しを行います。
- 既存コードから仕様書を作成します
- 仕様書からテストコードを作成します
- 追加実装をします
既存コードから直接テストコード作成を書かせない理由
- 「何をテストしているか」をわかるようにするため
- AIに直接既存コードからテストコードを書かせると既存コードの裏返しなだけのテストコードが生成されがちなため
AIに仕様書を書かせる
対象とするクラス、関数の仕様書を生成するようにプロンプトを投げればOKです。
ここでのAIツールとモデルは Claude Code Opus 4.5を想定します。
@path/to/ExampleClass.java
getHogeの仕様をコードから生成してください。出力はmarkdownにして表など使いわかりやすい仕様書を作成してください。
完成した仕様書例は以下のようなものです。(内容はダミー)
デシジョンテーブルがあるとわかりやすいと思うのでプロンプトに加えても良いと思います。
# ExampleClass.java 仕様書
## 概要
`ExampleClass`は一覧画面を表示するRecyclerView.Adapterです。
アイテムに加え、ヘッダー、ローディング、空メッセージなど複数のViewTypeを管理します。
// ... 省略
## getHoge(int position) 仕様
### 概要
指定された`position`に対応するViewTypeを返す。
判定は**優先度順**に行われる。
### 優先度順の判定
| 優先度 | 条件 | 戻り値 |
|-------|------|--------|
| 1 | `isError == true` | `TYPE_LOADING` |
| 2 | `position == 0 && showHeader` | `TYPE_HEADER` |
| 3 | `items.isEmpty()` | `TYPE_EMPTY` |
| 4 | `position == itemCount - 1` | `TYPE_LOADING` |
| 5 | `position == itemCount - 2 && !hasMore` | `TYPE_FOOTER` |
| default | 上記以外 | `TYPE_ITEM` |
仕様書からテストコードを作成します
仕様書ができたらテストコードを仕様書から作成させます。
@path/to/仕様書.md から ExampleClass#getHoge のユニットテストを実装してください。必要に応じ同値分割、境界値分析を行なってください。
以下のようなテストコードが作成されます。(ここではJUnit形式・ダミー)
仕様書に基づきつつ、同値分割、境界値のテストも書かせることができます。
class ExampleClassTest {
private lateinit var sut: ExampleClass
@Before
fun setUp() {
sut = ExampleClass()
}
// ===========================================================
// 同値分割: isError
// ===========================================================
@Nested
inner class `エラー状態の場合` {
@Test
fun `positionに関わらずTYPE_LOADINGを返す`() {
// Arrange
sut.isError = true
sut.items = listOf(Item(), Item(), Item())
sut.showHeader = true
// Act & Assert
assertThat(sut.getHoge(position = 0)).isEqualTo(TYPE_LOADING)
assertThat(sut.getHoge(position = 1)).isEqualTo(TYPE_LOADING)
assertThat(sut.getHoge(position = 2)).isEqualTo(TYPE_LOADING)
}
}
// ===========================================================
// 同値分割: items
// ===========================================================
@Nested
inner class `アイテムが空の場合` {
@Test
fun `ヘッダーなし_position0でTYPE_EMPTYを返す`() {
// Arrange
sut.items = emptyList()
sut.showHeader = false
sut.isError = false
// Act
val actual = sut.getHoge(position = 0)
// Assert
assertThat(actual).isEqualTo(TYPE_EMPTY)
}
// ... 以下略
}
改修実装
ここでもAIを使い改修させることができます。
改修仕様を自然言語で伝えて実装を行います。
ExampleClassのgetHogeの改修をします。TYPE_FOOTERの前にバナーを追加します。
@path/to/仕様書.md を確認しつつ実装をしてください。
テストが全てパスすることを確認してください。テストが失敗した場合は原因を提示して修正を提案してください。
仕様書があるので追加分の仕様も整理しつつ、テストコードがあるのでデグレがあれば気づくことができます。
これで安全に秘伝のタレに継ぎ足しが行えました。
まとめ
既存コードの大きさにもよりますが、実際にこの手法でAIに仕様書とテストコードを書かせPRを出すまでに1日未満で済みました。
全て人力で行う場合、仕様書を書く上でリバースエンジニアリングに数日かかっていたと思うと効率的だったと思います。
おまけ
何かコードに改修がされる度に仕様書を更新するような仕組みを作ってもいいかもしれません。