オープンソースRailsリポジトリの統計比較
Rails開発のあたりをつける
オープンソースRailsリポジトリの統計を比較してみる
RailsはオープンソースのWebサービスの実装が豊富です。以前公開されていない商用Webサービスのrails statsは比較したのですが、今回公開されているものについても調べたので比較してみます。
リポジトリの設定そのままで動かしたので、元がちゃんと設定されていないとパースもれはあるかもしれません。
Redmine
commit 9315481cdfefed08d0e1bfa0701de872ba6d241b
テストがRspecベースでないのは他と違う点ですね。Code to Test Ratioは低く、テーブル数も少ない。
$ bundle exec rails stats
+----------------------+--------+--------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+--------+--------+---------+---------+-----+-------+
| Controllers | 8829 | 6599 | 55 | 534 | 9 | 10 |
| Helpers | 6711 | 4923 | 1 | 395 | 395 | 10 |
| Jobs | 112 | 88 | 3 | 9 | 3 | 7 |
| Models | 19585 | 14174 | 102 | 1502 | 14 | 7 |
| Libraries | 20353 | 14511 | 149 | 1180 | 7 | 10 |
| Controller tests | 0 | 0 | 0 | 0 | 0 | 0 |
| Helper tests | 4150 | 3224 | 21 | 283 | 13 | 9 |
| Model tests | 0 | 0 | 0 | 0 | 0 | 0 |
| Mailer tests | 0 | 0 | 0 | 0 | 0 | 0 |
| Integration tests | 10351 | 7269 | 101 | 575 | 5 | 10 |
| System tests | 1504 | 1022 | 10 | 65 | 6 | 13 |
+----------------------+--------+--------+---------+---------+-----+-------+
| Total | 71595 | 51810 | 442 | 4543 | 10 | 9 |
+----------------------+--------+--------+---------+---------+-----+-------+
Code LOC: 40295 Test LOC: 11515 Code to Test Ratio: 1:0.3
Mastodon
commit 728eb6a15387da9074ccc024c9002f74ff829470
規模の割にspecを細かく概念を分けています。
$ RAILS_ENV=development bundle exec rails stats
+----------------------+--------+--------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+--------+--------+---------+---------+-----+-------+
| Controllers | 12340 | 9255 | 247 | 1533 | 6 | 4 |
| Helpers | 1612 | 1295 | 0 | 144 | 0 | 6 |
| Models | 12884 | 8600 | 168 | 1170 | 6 | 5 |
| Mailers | 399 | 298 | 4 | 38 | 9 | 5 |
| Views | 310 | 201 | 0 | 0 | 0 | 0 |
| JavaScript | 32723 | 26838 | 0 | 489 | 0 | 52 |
| Libraries | 7756 | 5732 | 94 | 286 | 3 | 18 |
| Chewy specs | 124 | 100 | 0 | 4 | 0 | 23 |
| Config specs | 106 | 75 | 0 | 1 | 0 | 73 |
| Controller specs | 15700 | 11988 | 0 | 43 | 0 | 276 |
| Fabrication specs | 14 | 10 | 0 | 0 | 0 | 0 |
| Feature specs | 434 | 271 | 0 | 1 | 0 | 269 |
| Generator specs | 27 | 20 | 0 | 0 | 0 | 0 |
| Helper specs | 1326 | 1038 | 0 | 7 | 0 | 146 |
| Lib specs | 9841 | 7667 | 0 | 10 | 0 | 764 |
| Locale specs | 35 | 28 | 0 | 0 | 0 | 0 |
| Mailer specs | 602 | 445 | 3 | 27 | 9 | 14 |
| Model specs | 8655 | 6733 | 0 | 15 | 0 | 446 |
| Policy specs | 1245 | 991 | 0 | 0 | 0 | 0 |
| Presenter specs | 404 | 318 | 0 | 0 | 0 | 0 |
| Request specs | 5575 | 4089 | 0 | 4 | 0 | 1020 |
| Routing specs | 207 | 161 | 0 | 0 | 0 | 0 |
| Serializer specs | 324 | 253 | 0 | 0 | 0 | 0 |
| Service specs | 7600 | 6043 | 0 | 16 | 0 | 375 |
| System specs | 45 | 31 | 0 | 0 | 0 | 0 |
| Validator specs | 762 | 592 | 0 | 3 | 0 | 195 |
| View specs | 48 | 35 | 0 | 0 | 0 | 0 |
| Worker specs | 1704 | 1280 | 0 | 1 | 0 | 1278 |
| App Libraries | 8680 | 6416 | 158 | 984 | 6 | 4 |
| Presenters | 348 | 273 | 13 | 34 | 2 | 6 |
| Services | 6593 | 4788 | 91 | 590 | 6 | 6 |
| Validators | 537 | 385 | 19 | 59 | 3 | 4 |
| Workers | 2305 | 1635 | 87 | 172 | 1 | 7 |
+----------------------+--------+--------+---------+---------+-----+-------+
| Total | 141265 | 107884 | 884 | 5631 | 6 | 17 |
+----------------------+--------+--------+---------+---------+-----+-------+
Code LOC: 65716 Test LOC: 42168 Code to Test Ratio: 1:0.6
Forem
commit ca6e6355493ddb49a31f697dd64baa2904356df6
dev.toの見た目は控えめにいって素朴な印象。それに対応してフロントのコードが少ないです。
$ bundle exec rails stats
+----------------------+--------+--------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+--------+--------+---------+---------+-----+-------+
| Controllers | 10858 | 8513 | 167 | 851 | 5 | 8 |
| Helpers | 1280 | 951 | 0 | 142 | 0 | 4 |
| Models | 8526 | 5840 | 113 | 676 | 5 | 6 |
| Mailers | 285 | 225 | 5 | 28 | 5 | 6 |
| Views | 21570 | 19904 | 0 | 0 | 0 | 0 |
| JavaScripts | 5672 | 4138 | 0 | 163 | 0 | 23 |
| Stylesheets | 17530 | 14900 | 0 | 0 | 0 | 0 |
| JavaScript | 21635 | 15225 | 0 | 338 | 0 | 43 |
| Libraries | 2803 | 2387 | 148 | 170 | 1 | 12 |
| Controller specs | 110 | 90 | 0 | 0 | 0 | 0 |
| Decorator specs | 1317 | 1111 | 0 | 1 | 0 | 1109 |
| Form specs | 70 | 59 | 0 | 0 | 0 | 0 |
| Generator specs | 5 | 4 | 0 | 0 | 0 | 0 |
| Helper specs | 1294 | 1050 | 0 | 1 | 0 | 1048 |
| Initializer specs | 293 | 244 | 0 | 1 | 0 | 242 |
| Lib specs | 2510 | 2011 | 2 | 10 | 5 | 199 |
| Liquid_tag specs | 3301 | 2719 | 0 | 59 | 0 | 44 |
| Mailer specs | 729 | 575 | 5 | 24 | 4 | 21 |
| Model specs | 11286 | 8998 | 1 | 7 | 7 | 1283 |
| Policy specs | 1452 | 1061 | 0 | 1 | 0 | 1059 |
| Query specs | 1431 | 1101 | 0 | 2 | 0 | 548 |
| Refinement specs | 10 | 9 | 0 | 0 | 0 | 0 |
| Request specs | 29241 | 23601 | 1 | 80 | 80 | 293 |
| Routing specs | 152 | 124 | 0 | 0 | 0 | 0 |
| Sanitizer specs | 66 | 58 | 0 | 0 | 0 | 0 |
| Serializer specs | 264 | 218 | 0 | 0 | 0 | 0 |
| Service specs | 15712 | 12645 | 0 | 49 | 0 | 256 |
| System specs | 6155 | 4930 | 0 | 23 | 0 | 212 |
| Task specs | 78 | 59 | 0 | 0 | 0 | 0 |
| Uploader specs | 335 | 264 | 0 | 0 | 0 | 0 |
| Validator specs | 184 | 137 | 0 | 6 | 0 | 20 |
| View_object specs | 87 | 68 | 0 | 0 | 0 | 0 |
| View specs | 403 | 323 | 0 | 1 | 0 | 321 |
| Worker specs | 3185 | 2556 | 0 | 3 | 0 | 850 |
+----------------------+--------+--------+---------+---------+-----+-------+
| Total | 169829 | 136098 | 442 | 2636 | 5 | 49 |
+----------------------+--------+--------+---------+---------+-----+-------+
Code LOC: 72083 Test LOC: 64015 Code to Test Ratio: 1:0.9
Discourse
commit 4f8d52bbcb3d6e55eec7c8a789bd4f22b491c4aa
一番フロントヘビーなリポジトリ。コード量もGitLabの次に多いです。
$ bundle exec rails stats
+----------------------+--------+--------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+--------+--------+---------+---------+-----+-------+
| Controllers | 19897 | 15856 | 111 | 1035 | 9 | 13 |
| Helpers | 1203 | 958 | 0 | 120 | 0 | 5 |
| Jobs | 8510 | 6768 | 202 | 425 | 2 | 13 |
| Models | 40354 | 29270 | 271 | 2447 | 9 | 9 |
| Mailers | 1136 | 949 | 9 | 51 | 5 | 16 |
| Views | 3131 | 2821 | 0 | 0 | 0 | 0 |
| JavaScripts | 254029 | 214041 | 0 | 5185 | 0 | 39 |
| Stylesheets | 38779 | 33250 | 0 | 0 | 0 | 0 |
| Libraries | 75276 | 58686 | 602 | 4917 | 8 | 9 |
| Helper specs | 1078 | 869 | 0 | 2 | 0 | 432 |
| Import_export specs | 246 | 189 | 0 | 1 | 0 | 187 |
| Initializer specs | 129 | 93 | 0 | 0 | 0 | 0 |
| Integration specs | 3273 | 2762 | 0 | 12 | 0 | 228 |
| Integrity specs | 371 | 295 | 0 | 9 | 0 | 30 |
| Job specs | 10087 | 7982 | 5 | 36 | 7 | 219 |
| Lib specs | 74837 | 60645 | 49 | 296 | 6 | 202 |
| Mailer specs | 1916 | 1576 | 0 | 1 | 0 | 1574 |
| Model specs | 44934 | 36027 | 6 | 115 | 19 | 311 |
| Multisite specs | 834 | 676 | 2 | 10 | 5 | 65 |
| Request specs | 60027 | 48287 | 1 | 78 | 78 | 617 |
| Script specs | 1137 | 967 | 1 | 6 | 6 | 159 |
| Serializer specs | 5139 | 4099 | 0 | 11 | 0 | 370 |
| Service specs | 15175 | 12244 | 0 | 38 | 0 | 320 |
| System specs | 7181 | 5564 | 47 | 419 | 8 | 11 |
| Task specs | 674 | 507 | 0 | 5 | 0 | 99 |
| View specs | 108 | 84 | 0 | 0 | 0 | 0 |
+----------------------+--------+--------+---------+---------+-----+-------+
| Total | 669461 | 545465 | 1306 | 15219 | 11 | 33 |
+----------------------+--------+--------+---------+---------+-----+-------+
Code LOC: 362599 Test LOC: 182866 Code to Test Ratio: 1:0.5
GitLab
commit bcdfc757c31b2e71adaea24a2b9343aaac252a65
一番巨大なリポジトリ。セットアップでつまずく部分多数。
$ bundle exec rails stats
+----------------------+---------+---------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+---------+---------+---------+---------+-----+-------+
| Controllers | 32803 | 24239 | 345 | 3179 | 9 | 5 |
| Helpers | 18669 | 14554 | 1 | 1803 | 1803 | 6 |
| Models | 83027 | 59217 | 725 | 7643 | 10 | 5 |
| Mailers | 2492 | 1857 | 10 | 258 | 25 | 5 |
| Channels | 142 | 97 | 4 | 11 | 2 | 6 |
| Views | 1046 | 814 | 0 | 0 | 0 | 0 |
| JavaScripts | 134131 | 99295 | 0 | 968 | 0 | 100 |
| Stylesheets | 47170 | 38792 | 0 | 0 | 0 | 0 |
| Libraries | 251796 | 184939 | 3550 | 19198 | 5 | 7 |
| Bin specs | 717 | 574 | 0 | 0 | 0 | 0 |
| Channel specs | 103 | 74 | 0 | 0 | 0 | 0 |
| Command specs | 672 | 525 | 0 | 0 | 0 | 0 |
| Component specs | 2630 | 2046 | 10 | 28 | 2 | 71 |
| Config specs | 923 | 748 | 0 | 2 | 0 | 372 |
| Contract specs | 1799 | 1538 | 1 | 17 | 17 | 88 |
| Controller specs | 63009 | 48053 | 1 | 366 | 366 | 129 |
| Db specs | 835 | 636 | 0 | 16 | 0 | 37 |
| Dependency specs | 26 | 20 | 0 | 0 | 0 | 0 |
| Experiment specs | 411 | 298 | 0 | 0 | 0 | 0 |
| Feature specs | 93841 | 70386 | 0 | 528 | 0 | 131 |
| Finder specs | 21757 | 16461 | 0 | 31 | 0 | 529 |
| Graphql specs | 32982 | 24901 | 0 | 192 | 0 | 127 |
| Haml_lint specs | 228 | 161 | 0 | 0 | 0 | 0 |
| Helper specs | 29231 | 23025 | 0 | 54 | 0 | 424 |
| Initializer specs | 4053 | 3054 | 0 | 31 | 0 | 96 |
| Lib specs | 335839 | 261381 | 18 | 1194 | 66 | 216 |
| Mailer specs | 5032 | 3915 | 0 | 10 | 0 | 389 |
| Metrics_server specs | 260 | 191 | 0 | 0 | 0 | 0 |
| Migration specs | 11854 | 9121 | 0 | 24 | 0 | 378 |
| Model specs | 162193 | 123999 | 4 | 286 | 71 | 431 |
| Policy specs | 13495 | 10241 | 0 | 22 | 0 | 463 |
| Presenter specs | 8461 | 6458 | 0 | 8 | 0 | 805 |
| Rack_server specs | 82 | 60 | 0 | 2 | 0 | 28 |
| Request specs | 146710 | 113690 | 0 | 473 | 0 | 238 |
| Routing specs | 2332 | 1683 | 0 | 3 | 0 | 559 |
| Rubocop specs | 14034 | 11662 | 319 | 281 | 0 | 39 |
| Script specs | 4542 | 3534 | 0 | 5 | 0 | 704 |
| Serializer specs | 12556 | 9752 | 0 | 6 | 0 | 1623 |
| Service specs | 146314 | 112737 | 2 | 389 | 194 | 287 |
| Sidekiq specs | 20 | 16 | 0 | 0 | 0 | 0 |
| Sidekiq_cluster specs | 122 | 96 | 0 | 0 | 0 | 0 |
| Spam specs | 31 | 25 | 0 | 0 | 0 | 0 |
| Support_spec specs | 2761 | 2150 | 0 | 29 | 0 | 72 |
| Task specs | 7182 | 5470 | 0 | 28 | 0 | 193 |
| Tooling specs | 7347 | 5791 | 3 | 6 | 2 | 963 |
| Uploader specs | 4005 | 3051 | 1 | 10 | 10 | 303 |
| Validator specs | 1813 | 1279 | 0 | 8 | 0 | 157 |
| View specs | 10235 | 7583 | 0 | 27 | 0 | 278 |
| Worker specs | 28213 | 21376 | 0 | 87 | 0 | 243 |
+----------------------+---------+---------+---------+---------+-----+-------+
| Total | 1749926 | 1331565 | 4994 | 37223 | 7 | 33 |
+----------------------+---------+---------+---------+---------+-----+-------+
Code LOC: 423804 Test LOC: 907761 Code to Test Ratio: 1:2.1
統計の比較
個々の統計が出揃ったので比較してみます。
コード部分
コード全ての行数. GitLabが外部ライブラリーに切り出している部分があるのは事実なんですが、思ったよりDiscourseは巨大なリポジトリですね。
モデルのクラス数。ニアリーイコールテーブル数。
テストを除くアプリコードの内JavaScriptの割合。この値はアプリケーションの種類によってばらつきが出る部分。
モデルLOC/コントローラーLOC。サービス層他のレイヤーもあるのでノイズはありますが、ファットモデルになっているか。ABCサイズの比較だとGitLabは悪い値ではないので、そっちで見た方が負債具合は正しく評価できるかもしれません。
テスト部分
Code to Test Ratio。GitLabは高くRedmineは低い。
テスト分布。ControllerやGraphQLはRequest specに参入、Feature SpecもSystem Specに参入。
オープンソースプロジェクトはしばしばクローズドソースより品質が高くセキュアで安定していると言われることがあります。沢山の目によってウォッチされているので最終的に良いものに収束していくと。
バグが出て修正する時、セットでテストを書くことが多いと思います。そういう意味で
沢山の目によってウォッチされているオープンソースプロジェクトにおいてテストコード量はアプリケーションの複雑さと比例している可能性はあると思っています。
テストピラミッドとトロフィー
一般にテストコードを増やす戦略としてピラミッドまたはトロフィーの形が正解とされることが多いです。
テストピラミッドの場合は
- Unitテスト
- Integrationテスト
- End-to-Endテスト
でUnitテストが一番厚く、End-to-Endテストが一番薄いのが良いとされます。End-to-Endテストは一番実行時間が長く、画面のテストの場合、デザインの変更に弱いからです。
別の意見として、特にフロントエンドにおいてTesting Trophyというものがあります。
End to End, Integration, Unit, Staticに分けEnd to Endが薄く、Unitテストは厚く、IntegrationはUnitテストに負けるが厚くといった感じでしょうか。
ただトロフィーはピラミッドより不定形です。
実際作者の元案だとIntegration部分が太った形みたいですね。ピラミッドよりトロフィーは造形次第で形が変わるという印象はあります。
Railsもといバックエンドのリポジトリでテスト勾配はどうするのが正解でしょうか?
Testing Rubyのご提案
オープンソースRailsリポジトリから学べることは
- Modelテスト
- Requestテスト
- Systemテスト
でモデルのテストはリクエスト(Controller)テストより微妙に少ないくらいでした。モデルのテストはユニットテストの一部でしかないのはあるのですが、思ったよりモデルのテストは少ない印象です。しいていうならTesting Rubyでしょうか。
サービス層が必要な程度にアプリケーションが複雑になってくると複雑さがモデルのレイヤーよりコントローラーやサービスなどよりアプリのハンドリング部分のレイヤーに移転するのはあると思います。実際モデルコードの比率は規模が大きくなるほど減っています。
フロントとバックエンド両方厚くあるプロダクトの場合、テストコードの実行時間のコスパで言えば以下のようになると思います。1が一番高速で壊れにくいです。
- Modelテスト
- Requestテスト
- Integration(APIシナリオ)テスト
- End-to-Endテスト
一方でテストのコスパとは別の視点で、そもそもアプリケーションの規模が大きくなってくると同一テーブルを複数のユースケースで取り回すケースが増えてきて、そもそもモデルのレイヤーの比率が減る、それによって複雑性に対応するテストコードを書いていくと収束点としてTesting Rubyになる可能性はあります。
Discussion