個人的な採用技術の標準セット

書いてみる。

OS: Linux

開発も動かすのも基本Linux。この話題を書くと絶対荒れるので書きたくないが、でも書くが、サーバサイドのプログラミングするならば絶対Linuxが楽だと思う。ほとんどの開発環境はLinuxで動作するし、環境構築も楽だし。動作させる環境と開発環境を一致させるのも楽。デスクトップ環境も昔と比べてずいぶん楽になって使いやすくなった。ドライバが対応してないみたいな事も殆ど経験しなくなった。

最終的にはTheiaみたいなブラウザ上で動く開発環境上でどのデバイス・OSからでも高速に動作するLinux環境とシェルを触れる、みたいなところが理想だと思う。Theiaに期待。Cloud9?聞いたことないですね…。

言語処理基盤: AdoptOpenJDK

30億のデバイスで動くJava(ただし動くとは言ってない)(ただし31億にはならない)は、さすが30億なだけあってやっぱりなんだかんだでいろんな環境でちゃんと動きます。Windowsちゃんでは主にファイルシステムの違いからたまにエラーを吐くコードもありますが…。べつにそんなに可搬性を重視してるわけではないですが、いろんなデバイスで動くので使っている人も多く、JVMで動くライブラリも沢山あります。ユーザーが多いので情報も多いです。なので細かいところで悩んでも答えがネットですぐに見つかります。とくにこだわりがあるわけでもないので、私はそのような点を重視します。

JDKも今は沢山ありますがお気に入りはAdoptOpenJDKです。更新頻度が高く、バージョンも8, 11, 14と揃っています。そしてそのそれぞれについてHotSpotとOpenJ9の2つの実装を選べ、さらにプラットフォームもLinux/Win/Mac/Arm他と揃ってます。至れり尽くせりです。ちなみにOpenJ9はたまにコアダンプして死ぬのを見ます。

言語: Scala

言語は私はScalaが好きです。なるべく少ない仕様で多彩な表現力を持たせようという思想が個人的には好きだからです。パターンマッチ大好きです。関数型は一時期勉強してましたが、最終的には基本オブジェクト指向言語+細々としたところで関数型、for文とモナド、という程度に使っています。今の所私の結論は、ガチな関数型のエッセンスは正直そこまで使う機会無いけど、Option/Future/Eitherとfor式くらいは普通のプログラマでも覚えたらすごく便利と思う、という感じです。最初は2.13で仕様が結構変わってついてきてないライブラリも多かったけど、最近は2.13対応も進んで普通に使えてます。

Scalaを打ってると不思議と「あまり考えなくて良い」という錯覚に陥ります。頭の中の思考と実際に書いているソースが結構一致してるイメージですね。頭の中で考えてることを手続き的に書いていくために一回翻訳が必要な言語は使ってて疲れます(すごく感覚的な説明ですが)。

大規模処理: Akka Stream

何と書いたら良いのか分からんので大規模と書きましたが、直感的にはマルチコア全部使い切ってパフォーマンス出したい、みたいな場合にすごく効力を発揮するのがAkka Streamです。

Akka Streamを使うと何が嬉しいのか?それは前述の通り「マルチコア全部使い切ってパフォーマンス出したい」みたいなコードを比較的楽に書けるからです。すごくパフォーマンスを引き出したい処理の多くはすごくOutOfMemoryErrorになりがちでもありますが、Akka Streamならbackpressureで特に複雑なことをしなくても流量制御してくれるので基本的にはOOMEから開放されます。

その代わり、当然ですがStreamでデータを扱うのでコードによっては実装が大変になります。大変になる例としては巨大なインスタンスの中に実質的にグローバル変数の役割になってるような値が沢山あったりする場合などでしょうか。一方で、そのようなデータ処理をすることを意図して実装することは、モジュールごとの依存を小さくして見通しがよくテスト可能性も高いコードに結果的になるという副次的な効果もあります。

HTTPサーバ: Akka HTTP

Akka Streamを内部に使用したコンパクトなHTTPサーバです。DSLでルーティングルールを書くこともできます。個人的にDSLはあんまり好きじゃないですが、Akka HTTPのDSLはよく使っています。分かりやすいからかな?もちろん、DSLを使わずに書くことも出来ます。

感心したのが、HTTPのRequest BodyがStreamのSourceになることですね。考えてみればこの設計は自然なのですが言われるまで気づきませんでした。これは何を意味するかと言うと、「必要になるまでHTTPストリームからデータを引っ張り出さない」ということです。でかいデータを取り扱う時にこれがあると有り難いです。

後述しますが、最近はサーバ機能とルーティング機能だけしか無いようなマイクロHTTPフレームワークを好んで使うことが多いです。RESTサービスを意識したマイクロWebフレームワークは数多ありますが、パフォーマンスや記述の簡潔さ、APIのわかりやすさ、Scala対応、あとはAkkaブランドでAkka HTTPを使用してます。

ORM: Quill

私がORMに求めることは、

  • 基本的にSQLを書かなくてもいいこと
  • マッピング先のクラスは特殊な作り方をしなくても良いこと(例えばアノテーション書きまくりとかは嫌だ)
  • 実行しているSQLが分かること・予想できること
  • いざとなったら自分でSQL文を書けること
  • SQLのビルダが使えること

です。Scalaで使えて上記を満たしているORMはQuillだったのでこれを使っています。Slickもとても使いやすくて好きだったのですが、Slickは確か3からIOモナドの世界に行ってしまわれたので使うのを止めました。コンピュータというのはIOや状態があるのは当然なので、ある程度はそれを前提として副作用を許容したほうが分かりやすいと思います。IOをモナディックに扱わないとすっげえ困るというケースは私は今まで遭遇したことがないので必要性を感じません。私の経験が浅いと言われればそれまでかも知れませんが。

Quillの良い所はcase classとの親和性が高く、case classとDBスキーマ(というかテーブルの名前と属性の名前)さえ一致していればクエリをマクロによって自動生成してくれるという点です。リフレクションは基本的に使いません。また、コンパイルした時にInfoとして実行するSQLを表示してくれるのもいい感じです。変なSQLになっていないかどうかは実行せずともコンパイルすればすぐに分かります。

データベース: Maria DB

元々私が最初に触ったのがMySQLでした。不満はあれどそこまで困ったことはないのでそれを使い続けています。なのでMaria DBが発表されたときもそれを使い続けました。

MySQLとMaria DBの名前はいずれも製作者の一人の娘の名前だそうです。ミーとマリア。このスライドに二人の写真が沢山載っています。なんとなくこの話が好きで使い続けてるというのもあります。

データベースマイグレーション: Flyway

そもそもこれ以外の選択肢を知らないし、これで困ったことも無いので。コマンドラインでもコード上でも使えるので便利だし。

JSON: Jsoniter

JSONシリアライザ・デシリアライザは正直どれが良いのか未だによく分かりません。元々はplay-jsonを使ってました。その後はcirceを使ってましたが、catsもそこまで使いこなしてたわけでもなく、もうちょっと手軽にチャチャっと書けるものを探してて行き着いたのがJsoniterでした。こちらもquillと似た作りになっていて、case classを作ってcodecを定義しておけばすぐに使えます。パフォーマンス的にも優れているらしいです。

Javaの他にGolangでも使えるらしいです。

ファイル操作: Better Files

JavaのFileクラスがあんまり親切じゃないのでいろんな便利メソッドを追加したのがBetter Filesです。どういう便利さがあるのかは上のGitHubのリンクからドキュメントを見て下さい。

とりあえずFile IOが一つでもあるならこれを使う価値があると思ってます。なのでもう私はデフォで依存性に追加してます。「でもそういうクラスってJavaのFileを要求するAPIと遭遇した時面倒だよね…」と思われるかも知れませんが、当然相互変換するメソッドが用意されています。何も悩む必要はありません。使わない理由はないと思います。

設定ファイル: Config

元々はTypesafeが作っていたConfigというライブラリなのでTypesafe Configと読んでいましたがTypesafeがLightbendに変わったので(個人的にはTypesafeの方が好きだったな…と今でも思う)Lightbend Configと呼ぶべきなのでしょう。でも、Lightbendに変わってからもしばらくソースコード他にTypesafe Configの名前が入っていてネット上の情報もTypesafe Configでヒットする割合のほうが高い気がするのでどうなんだろうな〜〜〜と。多分名前を付けた人も今頃は失敗したと思ってるんじゃないかな。Configじゃなくてもっと固有の名詞を付けてれば良かったのにね。でも、個人的にはちっちゃいの食堂の「ラーメン」というメニューみたいな感じで硬派かつシンプルでそれはそれで好きです。

Configの利点は何と言ってもその書式です。HOCON(Human-Optimized Config Object Notation)という書式なのですが、その名前の通り人間が見て直感的に分かりやすいJSONライクな書式になっています。よくあるJSON類似フォーマットの中でもこれはずば抜けて分かりやすいです。

Scala関連のライブラリではConfigを利用するものも多いのでScalaを使っていて何か設定ファイル的なものを利用したいと思ったら第一選択肢はConfigで決まりじゃないでしょうか。

フロントエンドフレームワーク: Vue.js

フロントはVueを使い続けています。UIの一つのあるべき姿はMVVMだと個人的には思っています。UIはその後ろにいる状態を表すための表現であって、状態から見た目に変換するための定義だけ書いておけば勝手に変更を相互に伝えてくれるというのが理想形だと。状態から見た目、あるいは見た目から状態に変更する過程であまりにもその翻訳のための手続きが煩雑だと作ってて嫌になってきますしバグの温床でもありますからね。

昔はKnockout.jsを使っていたのですがこれが速攻で廃れてしまったので一番似ているVueを使っています。

以前は大したUIを持たないシンプルなページであればjQuery他でも良いんじゃない?と思ってました。今も思ってますしそういう主張をする人もよく見かけます。ただ、一回Vueに慣れるとjQueryでやるコスト以下のコストでVueプロジェクトを開始して開発を進められるので結果的にjQueryを使う事が無くなりました。というかむしろ静的なページを作るだけでも後述するUIフレームワークやvue-cliやのサポートがある分楽じゃね?とも思います。

プロジェクトの作成と開発はvue-cliに頼りっぱなしです。おじさんなので変化の早いフロントエンドの技術の変遷まで追えません。でもvue-cliを使えば最低限、プロジェクト構成や使用する技術の組み合わせが変なことにはならないので助かります。裏ではnpmやwebpackやnodeがいい感じに動いていますが、基本気にしなくていいです。必要になったらググってwebpack.config.jsなどをちょろっといじればOKです。フロントエンドの開発が専業ではないエンジニアはこういうようなライトな使い方が増えていくんじゃないかなーと思ってます。

というわけで2〜3のコマンドを覚えれば「devserverが立ち上がって勝手にブラウザで開発中のページが表示され、コードを編集した瞬間画面にインタラクティブに反映され、ビルドすれば勝手にminify+uglifyされて必要なディレクトリに入る」という仕組みが実現できます。devserverではプロキシ機能も含まれてるのでバックエンドがなんであっても特別なCORSの設定も無しに強調して動かすことが出来ます。

2010年代のカオスなフロントエンド開発事情はかなり成熟されて変化が嫌いなおじさんにもかなり使いやすくなってきていると思うので、毛嫌いしてた人もチャレンジしてみて下さい。

UIフレームワーク: Element UI

見た目がそこまで重要じゃないWebページの生産性を考えたら、まあ普通はUIフレームワークを使うことになりますよね。

以前はBootstrapを使っていましたが、最近はElement UIを使っています。以前から記事やTwitterでも私はElement UIを使ってきたんですが、これ良いんですよ。すごいんですよ。何が良いかというと、UIコンポーネントがすごく豊富であることと、あとカラムが24分割なので細かい指定ができるあたりですね。

特に私が感動したのがTableコンポーネントで、これはよくある「ヘッダは固定しろ」「ページングしろ」「横スクロールできるようにしろ」「列でソートできるようにしろ」「中に入力欄とかボタンを設けろ」みたいな、テーブル大好き日本人がよく言うあるあるな仕様にも完全対応してくれてるんですね。中国生まれのフレームワークですが、もしかしてこの辺りの細かさって日本じゃなくてアジアで共通だったりするのかな…。

さらにさらに素晴らしいのがvue-element-adminというプロジェクトで、これは所謂ダッシュボードでよくあるような機能を全て網羅しています。

余談

以前からそうでしたが、私はWebにおいてフルスタックなフレームワークって要るかな?という疑問を持ってました。ORMもHTMLレンダラもHTTPサーバもルーティングもDIも何でもかんでも入ってるというフレームワークは、そのフレームワークが想定している使い方をする限りは非常に高い生産性を発揮します。これがメリットだと思ってます。一方でデメリットは学習コストの高さ、適材適所的な使い方をできない柔軟性・モジューラビリティのなさ、ユーザからは1個の継ぎ目のない巨大なブラックボックスに見えるのでトラブルが発生したときに対処が難しいなどが挙げられます。

で、メリットとデメリットを比較した時にデメリットのほうが上回るケースのほうが経験上多かったというのがその理由です。ほとんどの開発プロジェクトでは「そのフレームワークが想定している通りの使い方」では対応出来ないことが出てくるからです。それが少数であれば生産性の高さのほうが上回りますが、個人的にはあまりそういうケースは見たことが無いです。

「我々の用途ではフレームワーク標準のライブラリ以外のものが必要だったので別のライブラリを採用している」というのが多くなってくると、フレームワークのカスタマイズ方法を調べたり、その実装を調べたりするのに時間を採られ、一番のメリットである生産性の高さがどんどん失われていきます。

そうして出来上がった一連のコードは、たまに「なんか上手く動かない」という状況に陥りがちです。こうなってくるとなぜ上手くいかないのかを調べるためにフレームワークそのもののデバッグが必要になってきます。

こういった諸々のせいで「フルスタックフレームワークの生産性が高い」ということがメリットになった経験って正直私にはあんまり無いのです。各々のフレームワークは独立していて簡単に組み合わせができるようになってる方が好ましいと個人的には思います。

また、このページで挙げたフレームワークはいずれも別物ですが、相互運用で困るケースはありません。単体で提供しているライブラリやフレームワークが他のフレームワークと相互運用性を考慮してないわけがないので当たり前と言えば当たり前ですが…。