Javaのパッケージ名の命名には2019年でも所持ドメインの逆順を使うべきなのか調べた

# 発端

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Java6までは言語仕様の中に命名規約の章があったけどJava7からはなくなった。あとパッケージ名もドメイン名逆順にしろというのが、ローカルでの利用を意図してるなら単一の識別子だけでいいということになった。そのころからlombokみたいにプロダクト名だけのパッケージ名を見るようになった気がする</p>&mdash; きしだൠ (@kis) <a href="https://twitter.com/kis/status/1087733625356013569?ref_src=twsrc%5Etfw">2019年1月22日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今までjavaパッケージの命名はドメイン名の逆順というのが通例だと思っていたが、たしかにそれに沿わないライブラリも特にScala系のライブラリで見ることが増えてきた。
そもそもこの命名規則は確かに衝突を避けるためには合理的ではあるもののコード中の記述量が増えるため後発の言語でもまったく真似されていない。

最近のJavaでより短いパッケージの命名が許容されるならとても嬉しいと思い、本当にJavaのFQDN逆順命名規約は無視していいのか調べた。

# 調査内容

## Java言語仕様を当たる

発端となったtweetにも書いてある通り、パッケージの命名規則は言語仕様レベルで記述がある。
そこでJava SE 6から最新の11までの仕様書を調べてみた。

関係する文章をすべて引用・翻訳すると長くなってしまうため以下では要約しつつ翻訳する。また、差分がわかりやすいようにgitコミット化もしてみた。

### [Java SE 6言語仕様](https://docs.oracle.com/javase/specs/jls/se6/html/j3TOC.html) ([gitコミット](https://github.com/bigwheel/package-naming-in-java-spec/commit/2975775694e70f5fa9a6e4ce5ff4e2153d3f08e4))

「6 名前」下の「6.8 命名規約」下の[「6.8.1 パッケージ名」](https://docs.oracle.com/javase/specs/jls/se6/html/names.html#6.8.1)

> 広く配布する意図がある場合、パッケージ名は7.7節で述べられる規約に従うべきです。
> 逆に局所的にしか使わない場合でも、トップレベルのパッケージ名は小文字から始まるべきです。

「7 パッケージ」下の[「7.7 一意なパッケージ名」](https://docs.oracle.com/javase/specs/jls/se6/html/packages.html#7.7)

> 所有しているインターネットドメイン名を各レベルに分割して順序を逆にしたものをパッケージ名に使用する。
> その意図は異なるライブラリ間のパッケージ名[^1]の衝突を避けることである。

[^1]: より正確に言うとクラスまたはインターフェイスの完全修飾クラス名

### [Java SE 7言語仕様](https://docs.oracle.com/javase/specs/jls/se7/html/index.html) ([gitコミット(前回との差分)](https://github.com/bigwheel/package-naming-in-java-spec/commit/eccb7cc5aa101a9da08c09da247dd857ae003c3b))

Java SE 6の「7.7 一意なパッケージ名」および「6.8 命名規約」の項目がなくなり、その中の記述が[「6.1 宣言」](https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.1)下の「パッケージ名」へ移っている。

### [Java SE 8言語仕様](https://docs.oracle.com/javase/specs/jls/se8/html/index.html) ([gitコミット(前回との差分)](https://github.com/bigwheel/package-naming-in-java-spec/commit/4000defc1746c25e25c048ec0c4584231a4750b6))

「6.1 宣言」下「パッケージ名」の項目はまったく変わっていない。

### [Java SE 9言語仕様](https://docs.oracle.com/javase/specs/jls/se9/html/index.html) ([gitコミット(前回との差分)](https://github.com/bigwheel/package-naming-in-java-spec/commit/74a4f894045035eeaf3b8cfd7119c5f68758558f))

モジュールの追加により項目名が「パッケージ名とモジュール名」に変わった他、モジュール名の命名規約についても書かれている[^2]。

[^2]: 詳細はリンク先の言語仕様参照

また非常に重要な変更として次の一文が削除されている

> Names of packages intended only for local use should have a first identifier that begins with a lowercase letter
> 利用が局所的なパッケージの名前も最初の識別子の最初の文字は小文字であるべきです。

### [Java SE 10言語仕様](https://docs.oracle.com/javase/specs/jls/se10/html/index.html) ([gitコミット(前回との差分)](https://github.com/bigwheel/package-naming-in-java-spec/commit/374e0137e6415d13a57d51c9ee3ccc2165adc5b7))

関連個所はJava SE 9と全く変わっていない。

### [Java SE 11言語仕様](https://docs.oracle.com/javase/specs/jls/se11/html/index.html) ([gitコミット(前回との差分)](https://github.com/bigwheel/package-naming-in-java-spec/commit/3837ab1768489720986a4d20e99c3276cb17b1dc))

関連個所はJava SE 10と全く変わっていない。

### [[補足] Java SE 8の時点で書かれたチュートリアル内の「パッケージの命名」項目](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html)

ほぼJava SE 8の「6.1 宣言」下「パッケージ名」の項目と同じだが、**局所的な利用**についての記述はない。

### 言語仕様調査まとめ

言語仕様としては依然ドメイン名の利用を推奨しており、むしろ**局所的な利用**に関する言及が削除されたりとドメイン名の利用推奨を強化する動きになっている。

最初のtweetの

> Java6までは言語仕様の中に命名規約の章があったけどJava7からはなくなった。

はそこ単体では事実ではあるものの記述された内容は他所へ移っただけなので語弊がある。

> あとパッケージ名もドメイン名逆順にしろというのが、ローカルでの利用を意図してるなら単一の識別子だけでいいということになった。

これに関してはだれがいいということにしたのか明確に書かれていないが、少なくともJava言語仕様レベルではそのような記述はなくむしろローカルでの利用に関する記述は削除されている。


## 実際のライブラリをあたる

実際に世の中でよく使われているライブラリがこの命名規則に従っているか調べた。

### maven repositoryのpopularからの調査

maven repositoryは公開されているライブラリの相互参照数を調査して、よく他のライブラリから参照されているものを[popularとして表示してくれる](https://mvnrepository.com/popular)。

そこで、最も他のライブラリからの参照が多いトップ100においてパッケージの命名規則がドメインの逆順に従っているかを調べた。

#### 調査のために書いたコード

https://github.com/bigwheel/package-naming-in-java-spec/tree/master/mavenrepository

#### 調査結果

https://docs.google.com/spreadsheets/d/1HFjxTjeyDbkjhCquz3u5FWck_n-e3-zyhqLUGXL5e3E/edit?usp=sharing

| jarが取得できたライブラリ数 | 規約に従わないライブラリの数 |
|---|---|
| 92 | 9 |

一部素直にjarが取れなかったライブラリは調査から除いている。
具体的に規約に従わないライブラリは以下だった。

| トップレベルパッケージ名 | ライブラリ名 |
|:--|:--|
| clojure | clojure-1.10.0.jar |
| clojure | tools.nrepl-0.2.13.jar |
| complete,project.clj | clojure-complete-0.2.5.jar |
| scala | scala-library-2.13.0-M5-1775dba.jar |
| scala | scalajs-library_2.12-1.0.0-M6.jar |
| scala | scala-reflect-2.13.0-M5-1775dba.jar |
| kotlin | kotlin-stdlib-1.3.21.jar || ライブラリ名 | 
| lombok | lombok-1.18.6.jar |
| okhttp3 | okhttp-3.13.1.jar |

### 参考: 別角度からの調査

[The Top 100 Java Libraries in 2018 \- Based on 277,975 Source Files \| OverOps Blog](https://blog.overops.com/the-top-100-java-libraries-in-2018-based-on-277975-source-files/)

似たようなJavaライブラリの人気度調査を行っている記事があったので、こちらでリストアップされているライブラリについても同様の調査を行った。

#### 調査結果

https://docs.google.com/spreadsheets/d/1uoP6h9AAkiAxQjO14TWSjSK061e1k5h_FMmVlQgn9BI/

| ライブラリ数 | 規約に従わないライブラリの数 |
|---|---|
| 100 | 2 |

具体的に規約に従わないライブラリは以下だった。

* backtype.storm
* junit.framework


# 結論

今回の調査では、Pure Javaの領域ではFQDN逆順命名規約を無視する流れは見つけられなかった。
言語仕様書上は依然所持ドメインの逆順規約を強く推奨しており ローカルでの利用の項目はむしろ最近削除されている。
また有名なライブラリを対象にpackageの命名を調べたが、clojure, scala, kotlinなど別言語用のライブラリを除けば命名規則を無視しているのはlombok, okhttp3, junitなど[^6]全体の3%未満と非常に少なかった。

ただし、ぱっと調べた限りScalaにおいては[人気上位のライブラリ](https://github.com/topics/scala?o=desc&s=stars)においてこの規約を無視しているライブラリは多い[^5]。
Pure Java以外ではそういった流れがある可能性はあるのでこの点については追加調査が必要。

[^5]: play framework, scalaz, sbt, shapeless, sangriaなど

[^6]: lombok, okhttp3, junitなどがどういった背景でこの命名規則を無視したのかは気になるところではある
<!-- ※2 今回は言語仕様書と人気のあるごく一部のライブラリしか調べていないので、まだ普及していない新しいライブラリにおいてドメインの逆順規約に従わない流れがある可能性は否定できない -->