Elixir 開発環境 2017 in ACCESS

- [Elixir のチームでの開発環境について](https://qiita.com/melpon/items/1502c403b7b5f37e325f)
- [エムスリーでの Elixir 開発環境 ver.2017 #m3dev - エムスリーテックブログ](http://www.m3tech.blog/entry/elixir-development-environment-2017)

上記の2つのElixir開発環境記事ブームに触発されてこの記事は書きました。

ACCESS(某オフィススイートの方ではなく、そのような名前の会社の方です)では2年ほど前から Elixir をプロダクションで利用するようになり、複数の案件のサービスを Elixir で開発・運用しているので、その開発環境に関して書きます。
別のエンジニアの方が環境整備した部分が大部分なので、それぞれのツールを選んだ理由とかは書けません。

# 開発環境の統一

Erlang, Elixir, Node, Rubyのバージョンは、リポジトリ内に[asdf](https://github.com/asdf-vm/asdf) の`.tool-versions`ファイルを置いて、開発者は皆 asdf を利用するという方法で揃えています。

プロジェクトによっては ElasticSearch なども使いますが、私の参加したプロジェクトで ElasticSearch 使った時はサーバ開発者が2人だったので docker 化はしてなかったですが、少人数でもメリットはあるので docker 化してもよかったかもしれません。

# コーディングルールとツール

大まかには下記に従っており、一部異なる部分のルールが markdown に書かれています。あとは既存のコードを読んで雰囲気を掴んで書きます。

- [A community driven style guide for Elixir](https://github.com/levionessa/elixir_style_guide)
- [Credo's Elixir Style Guide](https://github.com/rrrene/elixir-style-guide)

Credo の利用に関しては、プラットホームコア的な OTP Application のリポジトリの`.credo.exs`を、それを利用する全プロジェクトのリポジトリからシンボリックリンクで参照し、同じ設定を利用しています。基本的に credo でのチェックしてからPRします。

少し特徴的なコーディングスタイルとして、社内に開発者が存在する [Croma](https://github.com/skirino/croma) を関数定義・Struct定義・エラー処理に頻繁に利用します。

# コンパイルオプション

warnings_as_errors を true に設定しています。

# Dialyzer

パブリック関数(`def`)は`@spec`を書き、プライベート関数(`defp`)もよほどシンプルでなければ`@spec`を書きます。上記の Croma の`defun`で`@spec`を生成することが多いです。

そして、PR前に [dialyze](https://github.com/fishcakez/dialyze) でチェックします。type指定を間違えた時に気づけるように`unknown`オプション付きで実行するので、`mix dialyze --unknown`として実行します。14000行ほどのプロジェクトで30〜40秒ほどかかります。他の作業がしにくいこの微妙な時間が少し辛いです。

# ユニットテストとカバレッジ

外部のサーバに接続する部分などは [meck](https://github.com/eproxus/meck) でモックすることがほとんどですが、プロジェクトによっては一部実際に ElasticSearch に接続するようなテストケースも書きます。

ユニットテストをどの程度書くかはプロジェクトによりますが、基本的にはステートメントカバレッジとしてはエラーの箇所も通るようにします。
カバレッジのパーセンテージは気にしておらず、例えば、コントローラーやロジックレイヤーは大体97%ぐらいはいきますが、ほぼロジックを含まないファイルのテストケースをサボったりすると全体は90%ぐらいに落ちたりしますが、余り気にしません。気にしていません。
そのプロジェクトではテストコードと実装コードの割合は大体2:1でした。

カバレッジは以前は [coverex](https://github.com/alfert/coverex) を使ってましたが、確かマクロ部分の結果をもっと見やすく表示できるということで、現在は [excoveralls](https://github.com/parroty/excoveralls) を使っています。
前述したようにパーセンテージは見ておらず、むしろ実装した部分のエラー処理を含めたテストケースを書いた時に、 excoveralls から出力された html を確認してエラー処理部分のコードを含めてきちんと「赤」ではなく「緑」になっており、テスト時に実行されていることを確認するのが目的です。

# CI/CD

トピックブランチが master ブランチにマージされると、jenkins により開発環境のサーバにデプロイ準備＆デプロイがされます(release ブランチにマージするとプロダクション環境にデプロイされます)。
デプロイ準備では各種チェック＆テストが走り、1つでも失敗したらメール&HipChatに失敗の通知がされ、デプロイは行われません。

1. Webフロントエンドを持つサービスの場合、npm からデプロイ前のフロントエンド側のチェック＆テスト実行(この中の処理はプロジェクト毎に大きく異なり、ある1つのプロジェクトでの内容を記述する)
  - [ESLint](https://eslint.org/) の実行
  - [stylelint](https://github.com/stylelint/stylelint) の実行
  - [Flow](https://flow.org/) のチェックの実行
  - [Mocha](https://mochajs.org/) の JavaScript のユニットテストの実行
  - [webpack](https://webpack.js.org/) で結合した JavaScript/CSS ファイルの作成
1. Elixir 側の`mix test`の実行(コンパイル時の warnings_as_errors は true)。このとき excoveralls でカバレッジ取得＆html出力もしており、各プロジェクトのテストカバレッジ状況はブラウザから見れるようになっています。
1. Webフロントエンドを持つサービスの場合、上記で結合された JavaScript/CSS と画像のファイルに関して、ファイルの内容のハッシュを計算し、そのハッシュ付きのファイル名で S3 にファイルがアップロードされ、 CloudFront からアクセスできるようになります。ハッシュをファイル名に付けるのは、内容が変わらない限りブラウザにそのファイルを長期にキャッシュさせるためです。
1. [relx](https://github.com/erlware/relx) をリリースに利用して Hot code upgrade でデプロイ実行。この辺りは OTP Application によってAWS EC2インスタンスのローリングアップデートでデプロイする場合もあり、詳しくはこの部分を実装したエンジニアが [tokyo.ex #8](https://beam-lang.connpass.com/event/70973/) で発表した資料 [Hot code upgrade in ACCESS](https://skirino.github.io/slides/hot_code_upgrade_in_access.html) に少し書かれています。

# 最後に

関数型でパターンマッチが強力で OTP が付いてる Elixir での開発は相当楽しいです。