📈

開発の進み具合を計測し、モチベーションを維持するために rails stats を保存しよう

に公開

こんにちは。Freespk、Copipedia というウェブサービスを個人で開発しています、Beyond です。

https://freespk.com/
https://copipedia.org/

さて仕事で開発を行うときは Issue などでバーンダウンを作成し、順調に開発が進んでいるかを「見える化」していますが、個人で気ままに作る際にはチケットとか Issue とか面倒で作ってられません。

とはいえ何も指標が無いと、ペースが落ちているのかどうか判断することすら感覚的なものとなってしまいます。幸い、Rails には、rails stats というコマンドがあり、コードやテストの有効行数(コメントを除いた行数)を簡単に計算してくれます。下の図の、Code LOC とか Test LOC がそれになります。

rails statsの出力例
$ rails stats
+----------------------+--------+--------+---------+---------+-----+-------+
| Name                 |  Lines |    LOC | Classes | Methods | M/C | LOC/M |
+----------------------+--------+--------+---------+---------+-----+-------+
| Controllers          |      4 |      3 |       1 |       0 |   0 |     0 |
| Helpers              |      2 |      2 |       0 |       0 |   0 |     0 |
| Jobs                 |      7 |      2 |       1 |       0 |   0 |     0 |
| Models               |      3 |      3 |       1 |       0 |   0 |     0 |
| Mailers              |      4 |      4 |       1 |       0 |   0 |     0 |
| Views                |     90 |     55 |       0 |       1 |   0 |    53 |
| Stylesheets          |     10 |     10 |       0 |       0 |   0 |     0 |
| JavaScript           |     23 |     16 |       0 |       0 |   0 |     0 |
| Libraries            |      0 |      0 |       0 |       0 |   0 |     0 |
| Controller tests     |      0 |      0 |       0 |       0 |   0 |     0 |
| Helper tests         |      0 |      0 |       0 |       0 |   0 |     0 |
| Model tests          |      0 |      0 |       0 |       0 |   0 |     0 |
| Mailer tests         |      0 |      0 |       0 |       0 |   0 |     0 |
| Integration tests    |      0 |      0 |       0 |       0 |   0 |     0 |
| System tests         |      0 |      0 |       0 |       0 |   0 |     0 |
+----------------------+--------+--------+---------+---------+-----+-------+
| Total                |    143 |     95 |       4 |       1 |   0 |    93 |
+----------------------+--------+--------+---------+---------+-----+-------+
  Code LOC: 95     Test LOC: 0     Code to Test Ratio: 1:0.0

コード行数の推移を記録してみた

そこで、CI でテストが成功するたびに、Mackerel に rails stats の値を自動で記録するようにしてみました。1月から始めたので、約半年間(~6月13日まで)の推移が以下の図となります。グラフを見れたから何と言うことも無いのですが「基本、線形にコード量が増えているな」とか「テストの方が伸びが速いな」ということがわかります。

Claude Code で特異点発生!

なお、6月13日までのグラフを見ていただきましたが、開発は 100% 人力ということはなく、Github Copilot(途中から、エージェント mode も活用), ChatGPT を使いつつ開発しています。人+AIのベロシティーがこのグラフです。それで、最近は Claude Code が良いらしいので、1週間前に Claude Code MAX を使って開発を始めました。

そして、Claude Code を使い始めてからの期間を足したものが以下のグラフになります。コードの伸び率はあまり変わっていませんが、テストの伸び率がとんでもないことになっています。まさに微分不可能、特異点が発生してるようです。

この伸び率が本物か、単にものめずらしさで色々タスクを頼んだので伸びが良かっただけか、はたまた無駄なテストが多すぎるのか、これからの推移を見ていく必要があります。「見える化」しておくと、こんなことも分かるので、ぜひ皆さんも記録してみてください。

補足:coverage も記録していますが、Claude Code の前後で大きな変化はありません。

参考:Mackerel に stats を送信するコード

私が、Mackerel に rails stats を記録するのに使っているコードが以下のものになります。

要点としては、rails stats は標準出力に結果を出すため、perform_stats の中で標準出力を hook して取り込んでいます。外部コマンド実行はしたくないので、 Rails::Command を直接実行しています。Rails の仕様が変更した場合、もしかしたら変える必要があるかも知れません。

class MackerelStats
  attr_accessor :api_key, :service_name

  def initialize api_key: nil, service_name: nil
    @api_key = api_key || ENV["MACKEREL_API_KEY"]
    @service_name = service_name || ENV["MACKEREL_SERVICE"]
  end

  def run
    stats = perform_stats

    stats =~ %r"Code LOC: (\d+)"
    code_loc = $1.to_i

    stats =~ %r"Test LOC: (\d+)"
    test_loc = $1.to_i

    now = Time.zone.now.to_i
    values = [
      { name: "loc.code", value: code_loc, time: now },
      { name: "loc.test", value: test_loc, time: now },
    ]

    mackerel = Mackerel::Client.new mackerel_api_key: api_key
    mackerel.post_service_metrics service_name, values
  rescue Mackerel::Error => e
    raise unless e.message.match? %r"failed: 50[34]"
  end

  def perform_stats
    original_stdout = $stdout
    $stdout = StringIO.new

    stats_command.perform
    $stdout.string
  ensure
    $stdout = original_stdout
  end

  def stats_command
    Rails::Command.find_by_namespace("stats", "stats").new
  end
end

MackerelStats.new.run if $PROGRAM_NAME == __FILE__

Discussion