はじめに
仕事の都合で、NVMe 周りをちゃんと理解する必要が出てきました。
であれば、仕様書を読むだけではなく、自分で実際にコマンドを叩いて、設計して、テストしてみようと思いました。
そこで、NVMe デバイスを学ぶための小さな CLI アプリケーションを作り始めました。
今回まとめるのは、その v0 を作りきった現状です。
この v0 では、単に「動くものを作る」だけではなく、次のような目的を持たせています。
- NVMe デバイスになるべく低レイヤーで直接触ってみる
- テストを作り、継続して確認できる形にする
- ドキュメントを残し、仕様・設計・実装・テストを往復する感覚を身につける
今後の展望として、v1以降を作成し、設計完成度・品質向上・機能追加を視野に入れています。
v0で作ったもの
成果物は、NVMe デバイスの情報を確認するための学習用 CLI です。
storage_eval
v0 の段階で、以下の command を実装しました。
- Linux Systemへのアクセスのみのコマンド
listshow
- NVMe デバイスへのアクセスコマンド
identifysmarterrinfo
単に command が増えただけではなく、次の土台も揃えました。
- mock / real の両対応
- Python
unittestによるテスト - command / overview / research のドキュメント
また、今回悩んだ部分や課題に感じた部分は以下に残すようにしてみました。
- 判断理由を残す
DecisionLog.md - 後で検討する課題を残す
Followups.md
自分の中では、v0 は「NVMe を学ぶための最小の実験場を一通り作り切った段階」だと捉えていて、まだまだ真の完成までは遠い道のりだと感じています。
なぜ自作したのか
nvme-cliのような既存のツールを使えば NVMe の情報は見ることができます。
ただ、それだけでは「何がどこから取れているのか」「OS が持っている情報と NVMe command で取りに行く情報は何が違うのか」が自分の中でつながりにくいと感じていました。
今回の CLI では、
list / showのように Linux が既に見えている情報を拾う部分identify / smart / errinfoのように NVMe command を自分で投げる部分
の両方を自分で実装してみました。
これにより、
- Linux の情報取得経路
- NVMe の admin command
- その返り値をどう解釈して表示するか
を、1 本のアプリの中でつなげて理解しやすくなっています。
とはいえ、小規模とはいえ入り組んだ構造になったので、他の人が今回のコードをさっと読んで理解できるかはわからないです。
開発環境と前提
環境は VS Code + SSH で、Linux マシン上で開発しました。
手元に十分な Linux 環境がなく、最終的には Mini PC の boot / system デバイスを相手に確認する形で進めました。
この条件だと write 系はかなり危険なので、v0 では I/O command を入れず、read-only 寄りの機能に絞っています。
この判断はかなり良かったと思っていて、学習目的としても、まずは read-only で安全に観察できる範囲を広げる方が価値が高かったと考えています。
作りながら見えた、今回の重要ポイント
1. 設計は最初からきれいではなかった
最初は、正直かなり手探りでした。
- NVMe の規格を少し知っている程度
- テスト設計の経験が薄い
- 本格的に責務分割を考えた設計経験も多くない
- ドキュメントもどの粒度で残すべきか分かっていない
という状態だったので、初期設計はあまり明確ではなかったです。
ただ、実装を進めるうちに、
- device classの責務
- コマンド実行の責務
Mock/Real Deviceの分け方- 情報構造体の扱い方
- 出力メソッドの置き方
が少しずつ見えてきました。
結果として v0 では、
NvmeDeviceというクラスを device access interfaceとして定義- command 側は orchestration と表示
- concrete class は内部に隠す
- 結果型は
*_info.hに寄せる
という形にかなり整理できています。
決してまだ完全とは言い難いですが、初期のゴチャゴチャした構成よりかは遥かにマシになっています。
「最初からきれいに設計できた」というより、
作りながら責務分割を見つけていった という感覚に近いですね。
2. mock を入れたことで、学習とテストの両方が進んだ
今回かなり良かったのは、早い段階で mock を導入したことでした。
mock を作ると、
- 実デバイスが無くても command の流れを確認できる
- 取得後の表示や分岐を安定してテストできる
- real 依存の揺れを避けながら仕様を固められる
という利点があります。
実際の流れとしては、
- mock で command の形を作る
- real に繋ぐ
- テストを追加する
という順番になることが多かったです。
一方で、mock は「どうあるべきか」が分からないと作れない、という問題もあります。
つまり、mock は単なるダミーではなく、期待動作を言語化する作業でもありました。
今回のような汎用 NVMe の世界では、経験のない自分ではそこを全部自力で決めるのは難しかったので、仕様確認やレビューの補助として AI を使ったのはかなり大きかったです。
3. AI をかなり積極的に使った
今回は、Codex との会話にかなり時間を使っています。
ただし、丸投げというよりは、
- 方針を一緒に整理する
- 実装例や比較案を出してもらう
- 自分はそれを写経しつつ理解する
- レビューもしてもらう
という使い方が主です。
このやり方の良かった点は、
- 細かい実装ミスで詰まりにくい
- 設計の比較案を早く見られる
- 自分が知らない観点を補える
ことです。
一方で、例を出してもらいすぎると考えなくなる危険もあるので、後半はあえてコード例を減らしてもらいました。
学習目的で使うなら、このバランスはかなり大事だと考えています。
一つ勘案するべき要素として、当然開発スピードはすべてCodexでやってもらう場合よりかは遅いです。
それでも、今後のAI駆動開発において重要なのは、「AIで開発してもらう」能力ではなく、「AIが書いたコードをレビューできる」能力だと考えているため、このやり方を採用しています。
4. テストは「書いた」より「考え方が変わった」ことが大きい
テスト自体もかなり学びが大きいものでした。
最初は shell から始めたのですが、identify ができて実機アクセスが増えたあたりで Python の unittest に寄せました。
この判断は正解で、今回くらいの規模でも shell だけでは見通しが悪くなると感じています。Shell自体に慣れていない事もあって、初期から厳しさを感じていました。特にRegexだったり条件分岐をしたりするとかなり見通しにくいです。
さらに大きかったのは、テストの観点を意識するようになったことです。
たとえば CLI では、
missinginvalidunsupportedout-of-rangenot-foundduplicate
のような観点で考える、という知見を得ました。
また、
command × optionoption × error-kind
の matrix を README に残したことで、新しいOptionを追加した時の見落としも可能な限り拾えるようになりました。
これは単にテストケースが増えたというより、
実装をレビューする目線をテストが持ち始めた という変化だったと思います。
反省点
もちろん、反省点もかなりあります。
1. 初期設計が曖昧だった
仕方ない部分はありますが、最初の方針が弱かったため、途中で何度も見直しが入りました。
NvmeDeviceの責務- CLI の引数処理
- 各 class の役割
- test の持ち方
- 全体のドキュメント構成
このあたりは、今ならもう少し良い初期設計ができる気がします。
2. エラー管理はまだ弱い
v0 の時点でもかなり整理できたと思いたいですが、まだ広くまとめすぎている部分があります。
例えば、CLIのERR::ARG_MISMATCHというエラーがあるのですが、 本当は、
- parser error
- validation error
- runtime error
- system error
のように、もう少しきれいに分けられるはずです。
これは v1 で最初から設計しなおしたいポイントのひとつですね。
3. lower layer のテストはまだ不足している
今のテストは CLI や command の流れにかなり寄っています。
一方で、
ioctl失敗- timeout
- lower layer の異常系
のようなところは十分に見られていないです。
real E2E ではやりにくいので、今後は executor 層や device access 層をどうテストするかも考えて行きたいと思っています。
v0を通して得られたもの
今回の v0 で一番良かったのは、単に NVMe の command を少し打てるようになったことではなく、
- 仕様を読みながら実装する
- 実装しながら設計を見直す
- テストで観点を可視化する
- 判断をログとして残す
という、一連の開発サイクルを小さいプロダクトの中で回せたことでした。
特に、
DecisionLog.mdFollowups.mdtest/python/README.md
のように、「コードの外に判断や観点を残す」ことができたのはかなり大きいです。
まだ「習慣化」までは到達はしていないので、しっかりクセ付けていきたいと思います。
まとめ
v0 の時点で、個人的にはかなり満足度の高い成果物になりました。
ただし、まだ業務品質としては粗い部分もあるし、NVMe の機能も全然網羅していないです。
それでも、
- 学習対象として十分に使える
- 設計の試行錯誤が見える
- 次の v1 に進む足場ができている
という意味で、悪くないどころか、かなり価値のある v0 になったと思っています。
次は、この v0 で見えた課題をどう v1 に繋げるかを考えていこうと思います。
今回の記事では全体像を優先したので、細かい話は別記事に分けて記載していきます。