この投稿は、静岡 Advent Calendar 2019の2日目のものです
はじめに
静岡でRailsのWebエンジニアをしているkazuomatzです。2019年も残り1ヶ月を切りましたが、どうやら最近は業界的にRailsはオワコンらしいです。
僕は、2007年くらいからRailsを使ってきたので、かれこれ10年以上Railsを触ってきたことになるわけですが、オワコンなんて言われると、このままRailsを使い続けるか、それとも新しい言語に乗り換えるかの岐路に立たされているような気にもなってきます。
Railsが世の中に登場したのが2004年、2019年現在、Railsのバージョンは6.0です。2007年の12月にRailsのバージョン2が公開されていますから、かれこれ、5世代のRailsを使ってきたことになります。
Web2.0とRailsとの出会い
ちょっと昔話になりますが、2000年半ばに「Web」はバージョン1からバージョン2にバージョンバップされました。「Web 2.0」です。
これまでのWeb(=Web 1.0)は、ユーザーが情報を取得するだけのものでした。それがWeb 2.0になると、ユーザーがWebを通じて情報を発信することができるようになりました。そして、Webをプラットフォームとした様々なサービスやビジネスが生み出される時代に向かっていきました。
今となっては、Web 2.0とは結局何だったのかという議論もありますが、ともかく2007年11月に「Web 2.0 Expo」が盛大に開催されました。Web 2.0を提唱したティム・オライリーの講演をはじめ、当時の先進的なWeb関連のセッションが盛りだくさんでした。
当時、僕はJavaプログラマでマッシュアップ関連のWebサービスをいろいろ作っていましたが、そのWeb 2.0 Expoの会場で、Sun Microsystems(当時)のティム・ブレイの衝撃的な一言を聞くことになります。
「Ruby on Railsに触れたJavaプログラマーは、もうJavaには戻ってこないだろう」
Javaの開発元であるSun Microsystemsの中の人がこれ言っちゃったわけですから、大変なことです。少なくともWebが大きく変わるぞって時に、この発言によってRailsは脚光を浴びることになります。恐らく、これきっかけでRails始めた人はかなりいたはずです。
僕もその一人です。あの時からこの10年あまり、Railsに魅了され、RailsでいろいろなWebサービスを開発してきました。
Go勉強会へ
前置きが長くなりました。
ともあれ、Railsオワコン説を聞く中で、10年Railsを触ってきた身として、確かにいろいろ辛いことが多くなってきたなぁとも思います。そんな中見つけたのが、Shizuoka.goの勉強会でした。
勉強会の運営者も以前の仕事仲間(@secondarykey)だったので、Rails10年選手がGoを模索しているというネタで何かしゃべらせてとお願いして、参加することにしました。
ここからは、発表した内容の抜粋です。
Railsのここがよかった
- 設定より規約 / DRY ( Don’t Repeat Yourself )の哲学
- フルスタックなWebフレームワーク MVC / ORM / Asset
- 便利なライブラリ(Ruby Gems)が豊富!!
- お手軽な動的型付言語 Ruby
- 開発効率が半端ない ような気がしてた・・・
とにかくRailsに魅了されていた過去の自分。
Railsがオワコンの原因について
- フロントエンドのフレームワークがいろいろ出てきた
- Webpackなどのモジュールバンドラーが便利
- Railsでイケてたところのほころびが出てきた
僕もVue.jsは結構使うんですが、フロントエンドのフレームワークとRailsとがうまく調和しなくなってきてますね。Webpackについては、Rails向けのWebpackerがありますが、これもいまいち、むしろWebpackをそのまま使う人も多しね。Sprocketsもnode対応したり、ES6対応したりでこれでいけないこともないし・・・。
むしろ、僕が困ったと思っているのは、イケてたところのほころびが出てきたと書いたところ。
RailsってRubyGemsにものすごく多くの便利なライブラリーたちがあって、これをサクッと導入することで、いろいろなことが簡単にできます。もちろん、Ruby/Rails以外の言語やフレームワークでもそのような仕組みはあるわけだけど、Railsはちょっと探すと大抵のやりたいことは、Gem入れれば実現できちゃった。それが、当時のRailsって開発効率いいねという神話を作っていたのかもしれません。
こんなの見ちゃうと、Railsのレールに乗っているイケてる感に拍車かかりますしね・・・。
ところが、10年もいろいろWebサービスを作ってくると、サービス中止になるものもあれば(まぁこちらの方が多いわけですが)、セキュリティ対応やRailsのバージョンアップをしながら保守し続けるサービスもあります。この後者のサービスの延命が辛いんです。
Railsのバージョンアップは痛みを伴います。むしろ作り替えてしまった方が早い場合もあります。いや、作り替えられればまだいいかもしれません。データベースに貯まった様々なデータが特定のライブラリー依存の形で保存されているとかなり辛いです。
例えば、PaperClipという便利なGemがあります。画像ファイルをアップロードすると、Amazon S3などのクラウドストレージにアップロードしてくれる便利なライブラリです。これ本当によく使いました。でも、このPaperClipは、Rails 5.2以降では使えません。開発者の方が開発中止を宣言して、Rails5.2以降では、Railsに標準装備されたActiveStorageを使ってねということになっています。
これから新規に開発するサービスであれば、Rails 6でActiveStorageを使えばよいですが、すでにPaperClipを使ってデータも保存されているサービスをRails 6に移行することは簡単にはできません。
これは、Railsが悪いわけでも、開発を止めてしまったデベロッパーが悪いわけではありません。むしろ便利なGemを使うことでイケてる感を出してきたツケが回ってきたのかなと思います。
今のRuby / Railsの状況
- 2〜3年前に開発したWebサービスがサポート外のRailsで動いている
- とはいえ、Railsのバージョンアップする時間・費用がない
- 慣れ親しんだライブラリーが更新されず、最新のRails環境で使えない
こんな状況をGoは救ってくれるのか
ここからやっとGoの話。こんな状況をGoは救ってくれるのかを検証してみた。
これからもWebで食べていく僕がやっていくことは、こんなこと。
- Webサービスの開発(バックエンドもフロントエンドもやる)
- アプリ開発におけるバックエンドの開発(iOS / Androidアプリも作る)
- AWSを上手に使ってマイクロサービス指向で行く(Azure、GCPにも手を出す)
願うこと
- Web開発に必要なよいフレームワーク
- シンプルに開発ができること
- リファクタリングしやすく保守性が高いこと
発表しながら気づいた。第一にフレームワークを求めちゃうのは、フルスタックな全部入りのRailsにずいぶん甘やかされてきたんだなぁと。Rubyの世界ではWebフレームワークと言ったらほぼRails一択だけど、Goの場合は、軽量級なものからフルスタックなものまで、いろいろなフレームワークが存在する。Routerはこれで、ORMはこれをといった選択することも可能。Railsやってるとこの感覚は確かにない。
デモアプリをGoで作ったよ
僕は、この発表をするにあたり、簡単なWebサイトのモックをGoで作ってみた。
静岡市が運営している市民協働のポータルサイト「ここからネット」。このサイトには市民活動団体の活動情報や開催する講座やイベント情報、ボランティア情報などが掲載されている。そしてこれらデータはオープンデータとして公開されており、Web APIでデータを取得できる。APIのリファレンスもGithubで公開されている。
ここからネットに登録されているデータの中から静岡市の保育施設の一覧を取得して表示するだけのシンプルなものを作ってみた。
できあがりはこんな感じ。
APIからの戻りはこんな感じのJSON。
{
"status": 200,
"page": 1,
"per_page": 2,
"all_page_num": 133,
"count": 2,
"all_count": 266,
"pois": [
{
"id": 3450,
"name": "静岡大学教育学部附属幼稚園",
"prefecture_name": "静岡県",
"city_name": "静岡市葵区",
"address1": "大岩町1-10",
"address2": "",
"tel": "054-245-1191",
"url": "http://fzk.ed.shizuoka.ac.jp/youchien/",
"option_items": [
{
"display_name": "分類",
"attribute_name": "分類",
"value": "従来どおりの幼稚園"
},
{
"display_name": "設立",
"attribute_name": "設立",
"value": "国立"
},
{
"display_name": "受入",
"attribute_name": "受入",
"value": "3歳~就学前"
},
{
"display_name": "情報登録日",
"attribute_name": "情報登録日",
"value": "H29.9.1"
}
],
"updated_at": "2018/05/09 22:01:39 +0900",
"lat": 34.9913406,
"lng": 138.3794399
}
]
}
GoでAPIをリクエストしてJSON Parseしてクライアントに返すプログラムはこんな感じ。
動的言語のRubyと違って静的型付言語のGoにおいては、APIで受け取るJSONの構造体をキチンと書く。
// PoiData 受信データ
type PoiData struct {
Status int `json:"status"`
Page int `json:"page"`
PerPage int `json:"per_page"`
AllPageNum int `json:"all_page_num"`
Count int `json:"count"`
AllCount int `json:"all_count"`
Pois []struct {
ID int `json:"id"`
Name string `json:"name"`
Kana string `json:"kana"`
Description string `json:"description"`
ZipCode string `json:"zip_code"`
PrefectureName string `json:"prefecture_name"`
CityName string `json:"city_name"`
Address1 string `json:"address1"`
Address2 string `json:"address2"`
Tel string `json:"tel"`
URL string `json:"url"`
ImageURL string `json:"image_url"`
OptionItems []struct {
DisplayName string `json:"display_name"`
AttributeName string `json:"attribute_name"`
Value string `json:"value"`
} `json:"option_items"`
StartAt string `json:"start_at"`
EndAt string `json:"end_at"`
LocationID string `json:"location_id"`
ActivityID string `json:"activity_id"`
PostPhotoID string `json:"post_photo_id"`
InformationID string `json:"information_id"`
UpdatedAt string `json:"updated_at"`
OrganizationID string `json:"organization_id"`
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
Marker2X string `json:"marker2x"`
Marker string `json:"marker"`
Type string `json:"type"`
} `json:"pois"`
}
// GetKinderGarten 保育園情報を取得する
func (controller *KinderGartenController) GetKinderGarten() {
url := beego.AppConfig.String("endPointURL") + "/map/search.json"
page := controller.GetString("page")
url += "?page=" + page
dataSet := controller.GetString("type")
if len(dataSet) == 0 {
dataSet = "1,2,3,4,5"
}
url += "&data_set=" + dataSet
response, err := http.Get(url)
if err != nil {
controller.CustomAbort(500, "Internal server error")
return
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
controller.CustomAbort(500, "Internal server error")
return
}
jsonBytes := ([]byte)(body)
data := new(PoiData)
if err := json.Unmarshal(jsonBytes, data); err != nil {
controller.CustomAbort(500, "Internal server error")
return
}
controller.Data["json"] = data
controller.ServeJSON()
}
フロントエンドはVue.jsを使っている。モジュールバンドルはWebpackだ。ここはGoとは直接関係ない世界。
選択したWebフレームワーク
そして、肝心のWebフレームワークはいろいろ探した結果、最終的にBeego Frameworkを選んだ。
BeegoはRubyのWebフレームワークSinatraを意識して開発されたとのこと。SinatraはRubyのWebフレームワークではRailsの次に使われているもので、フルスタックなRailsよりも軽量なもの。
Beegoは、ORM、Routerも使えて、MVCモデルもいける。CLIもあってコマンド叩いていろいろできる。
実は、自分の発表の前に、元仕事仲間(@secondarykey)がGoのWebフレームワークをいろいろ紹介してくれていた(資料)。彼の所感では、Beegoは「老舗っぽくフルスッタックで色々機能がありそう。重そう。」とのこと。やっぱり、Railsのフルスタック脳がBeegoを選んだ結果は納得できる。
まとめ
Go言語とRubyを比較すると、静的と動的の議論も確かに出てきます。やっぱり静的の方が安全だよねという風潮もありますし、動的の柔軟さが開発効率をあげているのも事実です。
このあたりはトレードオフなので、要件に合わせてどちらが最適かを見極めていくことも必要かなと思います(今まで動的万歳だった自戒も込めて)。
今、自分がサクッとWebサービスを作るとしたら、やはりRailsを選ぶと思います。Rails 6のActiveStorageはよくできていると思いますし、まだまだ使えるGemも多い。自前のライブラリーやTipsも豊富にあったりするので、ユーザビリティや品質の高いWebサービスはまだまだRailsで作れます。
ただ、今回、Shizuoka.goに参加して、Goの魅力にも気づけたことも事実です。
自分が今後やっていきたいことの中で、「AWSを上手に使ってマイクロサービス指向で行く」をあげていますが、こちらはRubyよりもGoが向いているんだろうと思います。AWSでなくて、GCPなのかもしれませんが・・・。
ということで、今までやってきたアーキテクチャーとは違うものにチャレンジする際に、Goを選択することをここに誓います。
今回のShizuoka.goではWebフレームワークの話のほかに、@hogedigoさんのGoでのテスト手法についての話(資料)、@cupperさんのAWSとGoの話(資料)、@hrs_sano645
さんのGo製便利ツールの紹介(資料)など、興味深い話がたくさん聞けました。
最後に、Shizuoka.goのみなさんありがとうございました。また参加します。Shizuoka.go最高!