アノテーションを活用した影響調査にトライしてみた

最近アノテーションを何かに使えないかなーと思い、影響調査で活用してみました。
やってみて色々と思う所があったので、今後のためにまとめておきます。

きっかけ

最近のJavaの開発ではアノテーションを使うのが一般的です。
Springも元々はXMLベースでしたが、最近はほとんどアノテーションベースで実現できるようになっています。
LombokやMyBatisでメソッドを自動生成する際もアノテーションです。

そんな中、「ライブラリやフレームワークによって用意されたアノテーションを使って開発しているが、アノテーションを自分たちで定義したり、実装以外にも活用することができないか」と考えるようになりました。
実際、ソースコードの実動作には影響を及ぼさないアノテーションとして@Deprecatedがありますし、もっと実装以外への使い方もあるはずです1
そこで思いついたのがアノテーションベースで影響調査の成果物を管理・共有するという方法です。

仕様変更による影響を調査する際、日本語でまとめるとソースコードとの対応が取りづらくなります。
具体的には、変更箇所が何箇所あるのか、具体的にどこを直すのか、どのように直すのか、などなど。
「ソースコードさえきっちりメンテできていれば、ドキュメントは自動生成できる仕組みにしておくこと」が理想形だと考えているので、その一環として影響調査の成果物をソースコードにしてみようと思ったのです。

やってみたこと

こういうのは小さくトライするのがよいので、最小限のアノテーションでやってみました。
具体的にはプロジェクトAのために以下のようなアノテーションを定義しました。

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Target({ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface ProjectA {
    String value() default "";
}

アノテーションだけだと情報として少なすぎるので、引数に文字列で具体的に必要な対応などをメモできるようにしました。
今回はフィールド、コンストラクタ、メソッド、クラスのいずれにも付与できるようにしました。

このアノテーションを実際には、以下のように使用します。

@ProjectA("要修正。hogeをfugaに変更すればよい?")
public String method() {
    return "hoge";
}

内心「これだと以下のような書き方をするのと大差ないかも…」と思いました。

// TODO ProjectA:要修正。hogeをfugaに変更すればよい?

が、やってみないと分からないことも多いので、とりあえずやってみることにしました。

やってみてよかったこと

まず、やってみてよかったことを挙げていきます。

アノテーションにすることでコンパイルチェックが可能

コンパイルチェックがあるため、フォーマットをチェックできることです。
例えばTODOコメントのフォーマットを指定していたとしても、以下のようにtypoがある場合には検索でヒットしなくなります。

// TODO ProjctA:要修正。hogeをfugaに変更すればよい?

@ProjectAというアノテーションを作ればtypoはコンパイル時点で検知できるので、そのアノテーションの使用箇所を全て抽出可能であることが担保できます。

容易に過不足なく取り除くことができる

アノテーションはコンパイルチェックのおかげで、後から取り除くことも容易でした。
アノテーションを取り除く際は、アノテーションの定義を削除するだけで使用箇所が全てエラーになります。
なので、後はそれらを一つ一つ消していけばよいことになります。

アノテーションは一行だけで完結して記載することが多いので、grep -v "@ProjectA"を実行するだけで適切に取り除ける箇所も多かったです。
また、JavaParserを使えばアノテーションだけを適切に削除することも容易にできると思います。

影響調査の成果物をコミットやプルリクで管理・レビューできる

アノテーションを付与したソースコードが影響調査の成果物の一つになるので、プルリクでレビューや管理できます。
コードレビューと同様に「ここは修正不要なのでは」などの議論をすることもできますし、どのように修正すべきかが見えていなくてもプルリクを出せます。
一番便利だったのは、途中からプロジェクトへ入った人に「ここのプルリク箇所を見ておいてほしい」「ここをアノテーションに従って修正してほしい」の一言で伝えられることでした。

影響調査の結果にまとめる際もプルリクやコミットへのリンクを貼っておくだけで参照できるので、非常に楽でした。

今後の課題

次に、やってみて今後の課題だと感じた点についてです。

チーム内での周知が不十分

具体的にどのような使い方をするか、チーム内で事前に周知したり、認識を合わせられなかったことが一番の反省点です。
その結果、アノテーションにつけるコメントはメンバーによって大きく差が出てしまったように感じます。
そもそも複数チームが関わる規模の案件だったので、小さくトライするには一チームで完結できるプロジェクトで試行したほうがよかったのかもしれません。

次はより具体的な使い方を決めてからスタートするか、もっと小規模な案件でトライするか、どちらかにしたいと思います。

既存の開発フロー(プルリクやマージなど)との統合

今回はプルリクやマージとの開発フローがうまく統合できていなかったと感じました。
具体的には以下のような点です。

  • アノテーションを付けたプルリクを出すまではよいが、本流ブランチにマージすべきなのか?
  • 本流ブランチにマージした場合、いつ誰が取り除くのか?改修した人なのか?それともプロジェクト終了時なのか?
  • プルリクを本流ブランチにマージしようとしたら、ソースコードのコンフリクトが多発した
  • プルリクをマージせずに置いておいたら、本流ブランチとの乖離が大きくなってしまった

その結果「影響調査はブランチを切ってプルリクを立てたものの、実装時にはまた別途ブランチを切って別プルリクを立てることに…」というような事態も発生しました。

いざとなったら容易に取り除けることは分かっていたので、取り返しのつかないことにはなりませんでしたが、チーム内では多少混乱もあったのかなと思います。
次回は極力開発ブランチにマージすることを前提で、既存の開発フローとうまく統合できるように考えたいところです。

進捗や影響を可視化する

今回は実現できませんでしたが、これを進捗や影響を可視化することに活用していけないかと考えています。
CIの中でアノテーションに関する集計を行うことで、以下のようなことが実現できそうです。

  • 影響箇所をパッケージ、クラス、メソッド毎に集計し、見積もりの補正に活用する
  • 既に改修が終わった所はどこか、まだ終わっていないところはどれだけあるか
  • 影響調査の結果を一覧表示できるようにすることで、資料作成の手間を省く

ざっくり集計するだけならgrepコマンドでもできそうですし、精密に集計するのであればJavaParserを使えばよさそうです。
そのためにはアノテーションのパラメータにどのような項目を追加したほうがよいか、アノテーションはどのクラス/メソッド/フィールドに付与するかなどもきちんと設計していく必要があると思いました。

アノテーションのパラメータを増やし、具体化する

今回はアノテーションをStringのパラメータ一つだけにしました。
しかし複数のパラメータを指定できるようにすると、いろいろなことができそうです。

例えば、アノテーションに進捗を表すクラスをEnumで定義しておきます。

public enum Progress {
    NOT_INVESTIGATED,   // 未調査
    NO_NEED_TO_FIX,     // 対応不要
    NEED_TO_FIX,        // 要修正だが未修正
    FIXED;              // 修正済み
}

そして、アノテーションの定義には進捗を指定できるようにprogressパラメータを追加します。

@Target({ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
@interface ProjectB {
    String value() default "";
    Progress progress();
}

すると、アノテーションを指定する際、以下のように進捗をパラメータで明示できるようになります。

    @ProjectB(value = "要修正。hogeをfugaに変更すればよい?", progress = Progress.NEED_TO_FIX)
    public String method2() {
        return "hoge";
    }

もちろん、これだとパラメータ名も記載することが必要になるため、手軽にアノテーションを付けられなくなります。
しかし規模の大きいプロジェクトや納期がずらせないプロジェクトであるほど、状況は常に把握する必要がありますし、進捗を自動集計したくなります。
その中で、このような方法は管理コストを抑えつつ進捗の可視化ができそうだと感じました。

サンプルコード

全然大したものではありませんが、一応こちらに置いておきます。

まとめ

というわけで、アノテーションを活用して影響調査にトライしてみたお話でした。
アノテーションを付ける方法が絶対的に正しいと主張するつもりはありませんし、そもそも「影響調査のアノテーションを付ける時間があるなら、修正までやってしまえばよいのでは?」という意見もあると思います。それはそれで正しいと思いますし、否定はしません。
しかし影響調査においては広い業務知識や技術的観点が求められることも多いのが現実ですし、影響調査を見落とせば不具合に直結します。 そのような環境では影響調査と実装を分担せざるを得ないのですが、アノテーションベースで影響調査をするというのはうまく機能しそうだという実感を得られました。

影響範囲の全体が見えづらいプロジェクトでは活用の場がありそうなので、今回課題だと感じた点は改善して、次の機会でもトライしてみようと思います。
また、今後はそもそも影響調査そのものをJavaParserを使って実施する方法についても考えていきたい所です。


  1. というか、アノテーション(注釈)なので、実動作に影響しないような使い方が本来の用途なのだとは思いますが… ↩︎

関連記事


  1. Groovyの == 演算子と equals() は厳密に同じではない
  2. ModelMapperで1対1に対応しないフィールドのマッピング
  3. Javaでオブジェクト配列からフィールド配列を生成
  4. Javaサーブレットにおける部分URLやファイルパスの取得
  5. DDLを自動生成してJavaと各DBのデータ型を比較してみた
  6. 既存コードへのCheckstyle導入におけるルールの選定
  7. Checkstyleで汎用的に使えそうなルールをピックアップしてみた

comments powered by Disqus