Open19

仕様をマスターにし、タスクやテストはそこから自動生成したい

lainNaolainNao

まず思ったことは以下です

  • 仕様書の各項目にIDが振られていれば、それをE2Eテストに使えて網羅率図れるのではないか?
  • そのためには仕様書がいい感じに更新されない問題に対応しないといけなさそうであるが、各種あれこれを仕様書をマスターとしてしまう以下の案はどうか?
    • タスクは仕様書から自動生成(仕様の項目のうち- [ ]とチェックがついていないものをタスク化)
      • 「タスクって仕様では」と思ったので。例えば「◯◯を作る」のようなタスクがあれば、それって仕様書に- [ ] ◯◯と書いても同じではないかと思った。
    • 懸念や提案も仕様書に書いてしまう
      • そもそも提案されて却下された案もある意味ADRのような仕様の一種だと思ったので
      • 懸念やそれに対する解答もプロダクトの仕様としていいのかなと思ったので

ただしこれを守るには結構それに見合った書き方をする必要が出てくるはず。そこはまだ答えが出ていないところ

lainNaolainNao

要するに「仕様管理ツール(開発におけるあらゆるものの自動生成源となる機能を持つ)」がほしい。

これは簡単なものならばもう以下で満たせるかもしれないと考えた。

  • 仕様を全部.md(または.mdx)等にまとめてしまう
    • 1ファイルにする必要はないし、凄い末端ディレクトリに置いてもいいとする(例えば細かいUIコンポーネントの仕様はその末端ディレクトリの中に置いたほうが良いのかもという考えで)
  • 仕様となる各項目を- [ ]で定義する
    • なんらかの形式で最後にIDを書く(SPEC-${日時とかでIDとしたいな}
      • 日時をIDとすることで「この仕様古いな〜〜ずっと更新されてないけど今違うのでは?」がなんとなく見やすいし「この仕様ずっとあるけどすごいE2Eテスト大変。無理がありそう」のようになってコンテンツとして面白い気がする
    • もし仕様ごとになんらかの属性を持たせたいならSPEC-{ID}-${カンマ区切りで属性}」のような形式で書かせる。
      • 例えば仕様にも色々あるので属性として「必須なものはmust」、「いつまでに満たしたいか決まってるものはuntil("期日文字列")」などをつけられるようにするなどしてもよいのかな
        • 期限文字列は例えばプロジェクトごとに「フェーズ1」とか「◯年◯期」とか「初期リリース」とか。これは別ファイルに期日文字列A=yyyy-MM-ddのような形式で書いておき、CIで毎日そのファイルを見て合致してる日付ならば仕様書にチェックがついてるかを確認する処理を動かす等で使えるようになる(動かしたくないなら動かさなくてもいい)

さらにCIで以下のようにする

  • 仕様書の各項目でIDかぶりがあったら落とす
  • 仕様書の- [ ]がチェックついていない項目のカバレッジを図る
  • 仕様書のuntilの期日を過ぎてしまってもチェックがついていない項目が無いか(↑に書いたもの)
  • 自動テストのファイルのtest("ここ"の部分にSPEC-${~}の記述が無いものは落とす。そこには`-${属性}`の部分は不要とする
  • 自動テストのtest("ここ"をパースし、仕様の各項目に対するカバレッジを図る
    • - [ ]等の形式でない文の中で、コード等の記号としてではなく普通にSPEC-〜の形式の文字列を書いたら自動生成時にエラーとする

CI以外では以下もできるようにする

  • 仕様書の- [ ]がチェックついていない項目のうち、自動テスト化されてないものは手動テスト項目一覧として書き出せるコマンドを用意しておき、リリース前にそれだけを手動テストすればよいとしておく(そのレベルの仕様書にしたい)
    • オプションとして何か引数渡せば「とあるコミットIDからとあるコミットID間のリリースでは、ここらへんのファイル群はいじられてないので、ここらへんの仕様はチェック不要かもです」的な情報も書き出して欲しい

自分で考えたものなので面倒には感じないが、他の人は面倒に感じるだろうと思う。でもなんらかのSaaSになっていて↑のあれこれをいい感じのGUIで隠蔽できればまた別の印象になるだろうとも思うので、一旦よいのかなと思う。

lainNaolainNao

これをやるにおいて必要そうなことは

  • そもそも↑で実際に運用してみて以下を探る
    • 破綻しないか
    • 本当によいものとなったか
    • 改善案があればどんどん出していく。
  • 既存の仕様管理ツールを見回ってかぶるもの、または参考になるものを探す
  • 既存のテスト管理ツールを見回ってかぶるもの、または参考になるものを探す
  • 他懸念などあればどんどん(そもそも元々の問題を解決できるのならば何使ってもいいし、別に自作でなくてもいいし、別に仕事でもないし問題も解決しなくてもいいので適当にでいい)
lainNaolainNao

Q:「実装している途中で生まれた細かい仕様っぽいものは仕様書に反映したほうがいいの?」
A:「デグレしてほしくないレベルのものならば仕様に反映、デグレしてもいいやというものならば反映しない、でいい気がする」

捕捉

  • 細かすぎる挙動もいちいち仕様書に反映するのはさすがに無理があるのかなとは思った。例えば「ボタンにホバーしたら微妙に若干ボタンが浮く」のような挙動。UIライブラリならまだわからないけど、アプリケーションならばそんなことをメインの仕様書に書いてられないはず。
    • でも、そういう細かすぎるものはそのファイルの同階層に.mdファイルを置いといてそこにSPEC-~~って書けばまあありだと思う。デグレしてほしくないものはやっぱり仕様として書き出しておきたい気がするので。
    • ただ、そういう細かい「末端ファイルの末端仕様」もリリース前に確認する必要があるかは微妙な気がする。そういうのこそ自動テスト書きやすい(ように普通はしていきたい)はずなので、やっぱりそれでよさそうかも。自動テストできないならやっぱりデグレ確認はしたほうがいいのは基本的には間違いないはず。
      • ただ、リリースされる時に『絶対にこのリリースではいじってないファイル群』もテストすべきかどうかは不要かもしれないので、手動テストを書き出す際に「ここは今回の差分ではいじられてませんよ」という軍団を作って上げるのでいいのかも(上のスクラップに追記)
lainNaolainNao

「既存の仕様管理ツールを見回ってかぶるもの、または参考になるものを探す」「既存のテスト管理ツールを見回ってかぶるもの、または参考になるものを探す」

以下でリストアップしつつ参考になりそうな部分を見てみる

  • ALMツール
    • Visure
      • これでよいのかも
    • Helix ALM
      • これもよいのかも。多機能すぎてすごいけど
    • ALM Works https://almworks.com/
      • Jira関係?
    • Apira Teams
      • よさそう
  • プロジェクト管理ツール
  • 仕様管理ツール
  • 要件管理ツール
  • 要件定義ツール
  • タスク管理ツール
    • Jira
    • Backlog
    • GitHubのProjects
  • QA管理ツール
    • Qase
      • 設定可能項目が多すぎるので、PJ内で不要な項目を絞る機能がほしいな
      • WEB APIがあるのはカスタマイズできるからいい(DBとして使えなくもない)

「他懸念などあればどんどん」

  • この.mdで管理する方法だと、タスクのアサインとかまでは設定できない。逆に「いつまで」の状態はなぜか設定できるという半端な感じになってるかも。うーんでも「誰にアサイン」っていうのは仕様からしたら関係ないから…。
    • ここらへん、タスク管理ツールで設定可能な項目のうち、なんか持ってきたい機能あるか見てみたい
lainNaolainNao

これをやるにはまず独自のmarkdownlintのプラグインを作る必要がありそう。
また、マークダウンから仕様やTODOをジェネレートするツールも必要そう。

lainNaolainNao

markdownlintのプラグインは一旦最低限できた

/**
 * 文字列の末尾にある 'spec-${id}' のパターンを見つけ、idかundefinedを返す
 */
function extractSpecId(str) {
  const regex = /spec-(.*?)\)$/;
  const matched = str.match(regex);

  if (!matched) {
    return undefined;
  }

  return matched[1];
}

/**
 * 文字列がチェックリスト項目かどうかを判定する
 * `- [x]` または `- [ ]` で始まる文字列をチェックリスト項目と判定する
 */
function isCheckListItem(str) {
  // チェックリスト項目を検出する正規表現
  const checklistItemStartRegex = /^(?:\s*)- \[(\s|x)\]/;
  const matched = str.match(checklistItemStartRegex);

  if (!matched) {
    return false;
  }

  return true;
}

module.exports = {
  names: ["spec-checklist"],
  description: "チェックリストの末尾には (spec-${id}) を追加してください",
  tags: ["spec", "todo", "list"],
  function: function rule(params, onError) {
    params.lines.forEach((line, lineIndex) => {
      if (!isCheckListItem(line)) {
        return;
      }

      const specId = extractSpecId(line);
      if (specId) {
        // console.log(333, specId);
      } else {
        onError({
          lineNumber: lineIndex + 1,
          context: line,
        });
      }
    });
  },
};

lainNaolainNao

ここで思った点「仕様はTODOかもしれないが、TODOは仕様でない場合もある」

なので全ての- [ ]などの書き方に仕様IDをつけるのはやめたほうが良いのかも。仕様IDをつけた場合だけ仕様(兼TODO)とし、そうでない場合はTODOとして認識してもよいのかも。うーん

lainNaolainNao

いや、そもそもTODOを仕様と同じドキュメントに書くのがおかしいと考えてもいいのかも
TODOは別の箇所に書いておいて別のルールを適用すればいい

なのでTODOには2種類生まれそう

  • 純粋なTODO
  • 仕様チェックのTODO

うーん面倒なのですべてのタスクに仕様IDをつけるリントはする必要無いかも

うーんでもすべてのTODOを仕様としてしまってもよいのかも。というか「仕様」よりも上の概念である「プロジェクトとして完遂しておくべきもの」的な概念であれば、一応IDつけておくのもいいのかもと思う

となるとそれにどういう名前をつけるかどうか

逆に仕様に2種類あると考えるのもありかもしれない。「アプリケーションの仕様」と「プロジェクト、、、

いや、やっぱり以下のように考えるのがよさそう

  • TODOはTODOとして別途用意してもよい(ただし↑で作ったツールと混ぜない)
  • アプリケーションの仕様は、↑で作ったツールで管理する。TODOとしても使えるようにする

なので2系統のタスクが生まれる。アプリケーションの仕様を実現するためのタスクと、それ以外のメタ的なタスク

lainNaolainNao

[ ] ...という記法は、VSCodeのTodo Treeという拡張機能で勝手に集めてくれるっぽい。偶然だけど使えるかもしれない。パーサーの処理とか参考にしたい

lainNaolainNao

プロジェクト内の仕様やTODOをパースしてタスク管理系ツール用にjson等で出力したらよいのかなと考えていたけど、個人開発の場合はTodo Tree使えばそれをタスク管理ツールとして使っちゃったほうが早そう

lainNaolainNao

思った。これだとその仕様(タスク)が実装対応中なのかどうなのか分からない。

- [ ][ ] aaaaaaa ・・・「考えてもないし、実装もまだ」
- [x][ ] aaaaaaa ・・・「考えたが、実装はまだ」
- [x][x] aaaaaaa ・・・「考えたし、実装も終えた」

↑このようにするのは汚い感じがするが、要するに[ ]が一つだと1つの状態の真偽値しか分からない。これがタスク管理ツールがやっている「未着手」「着手中」「完了」のようなステータスのような機能なのか…。

元々のこのスクラップの一番上に書いた問題を意識しつつもどうにかしたいところ…

lainNaolainNao

いやそもそも自分が最初に考えたのが偶然いい感じにできていたかもしれず、
・1つ目の[ ]  ・・・仕様的に決まったかどうか
・2つ目の[ ] ・・・たぶん無しでいい。なぜならどうせパースされたspecIDが自動テストに含まないとエラー出るようにするから
と考えるとどうだろう、

別にエラー出るようにしなくても警告だけでもいいかもしれないし

lainNaolainNao

そもそもチェックリスト無しで
- 仕様(仕様ID)
だけでいいのかも。仕様IDに対する自動テストが書かれているかどうかで終わったかどうかの判定ができそうなので

と思ったら必ずしも自動テスト書かないんだった。skipで書くようにするとしても、逆に「手動テストすべきかどうか」の情報が漏れるかも

いや、skipされているものは手動テストすればいい

でも自動テストが書かれているかどうかで実装済みかどうかの判定はできなさそう。たまにTDDのようなことすることもあることもあるだろうし。でもそしたらテスト落ちるからリリースできないし、skipしたら手動テスト対象になるから、問題はないのか?(不明)

lainNaolainNao

そもそも- [ ]のチェックは簡単に嘘をつきうるので、無しでいいのかも

自動テストにあるかどうかで判定したほうがむしろおとなしいかも

lainNaolainNao

というわけで変更を加える

  1. 文末に//spec-xxxxxxxxxという記法でspecIDを書いたものを仕様とする
  2. そのspecIDをテストコードのタイトルに書いていかどうかをTODO機能とする
  3. テストコードがskipされているかどうかを自動化しているかどうかの判定とする

後はそれらを成り立たせるパーサー等を作ればいい

lainNaolainNao
  1. このようにjsonで書き出されるようにした。これを2や3で使えばいいかな

ちなみに左のtxtファイルがシンタックスハイライトっぽくされているのは https://github.com/lainNao/nao-syntax-highlight を使っている

左のtxtファイルは、目次と詳細を分けた。基本的に目次の方にだけ仕様IDを付加するようにして試したい。