GitHub 時代のデプロイ戦略

少し前までアプリケーションのデプロイと言えば capistrano などをコマンドラインから叩いてデプロイ、みたいなことをやっていたが、最近は少し様子が違うのでそのやり方、KAIZEN platform Inc. での事例を紹介する。

GitHub のイベントを契機に CI as a Service にデプロイを担当させる

GitHub で Pull Request を送って開発するのが前提になっているのは以前にも紹介した。

最近は Travis CI や CircleCI などに代表される CI (Continuous Integration) as a Service があって、CI も自分たちで環境を構築しなくてもクラウドに任せることができる。KAIZEN では CircleCI を積極的に使っている。

これらの CI as a Service は基本的に GitHub と連携することが前提になってて、Github に差分を push するとそれを契機にインテグレーションテストを実行してくれたりする。

で、CI という文脈でいくとこの自動テストの実行が主なユースケースとして想像されやすいのだけど、CI as a Service にはそのテストの実行が終わったあとのデリバリーの機能もある。

Deployment - Travis CI なんかを見るとよくわるのだけど、master に push したらそれをきっかけに Heroku にデプロイを行うとか、そういうことができる。

CircleCI のデプロイオプション

この CI as a Serivice のデプロイオプションは結構色々細かく設定できて、例えば「deployment/production という名前のブランチに変更が push されたらこれを実行する」なんて設定ができる。

設定はレポジトリに置いた circle.yml ファイルに記述する。例えば以下のような感じ。

deployment:
  production:
    branch: deployment/production
    commands:
      - ./script/assets_precompile.sh:
      - bundle exec cap production deploy:migrations:
          
  edge:
    branch: deployment/edge
    commands:
      - ./script/assets_precompile.sh:
      - bundle exec cap edge deploy:migrations:

これでリモートの deployment/production ブランチにコードを push するとそれを契機に production へデプロイが行われるし、deployment/edge なら edge へ、てな感じになる。

実際のデプロイは、(まだ Mutable Infrastructure なので) capistrano を実行している。

CircleCI にデプロイを実施させると、テストを実行してその結果が緑だったらデプロイさせるというようなこととか、デプロイのログが共有しやすくなるとか、キャンセルが簡単とかいろんなメリットを享受できる。

Pull Request でデプロイする

「ほうほう、なるほど。じゃあデプロイしたいときは master から deployment/production へ merge して、git push するのか」というところだけど、ここでもう一個工夫する。

デプロイしたくなったら、master から deployment/production へ Pull Request を作るんである。

この Pull Request を merge すると、CircleCI がそれを検知して結果的に CircleCI 上でデプロイが走る。キャンセルしたいときは merge しないで close する。

なんで直接 push せずにわざわざ Pull Request にするかは、上の画面を見ればだいたいおわかりのとおり

  • デプロイの見える化
  • 何がデプロイされるかのコミットログの可視化

など本当にいろんな利点がある。

production デプロイの際には、この Pull Request のスレッドを使って QA の最終確認をしたりする。コミュニケーションが Pull Request 上に集約されることになるし、デプロイ直前の hotfix が行われたコミットなんかも全て見えるようになる。

今思うと、手元でエイヤで cap deploy の時は、このあたりのワークフローが全部暗黙知化していた。アプリケーションへの変更を Pull Request ベースにして色んなものが見えるようになるのと同じで、デプロイもまた Pull Request で改善できるところがたくさんある。

hubot へデプロイをお願いする

仕上げは、この Pull Request を作る方法である。つい先日までは Pull Request を GitHub 上でマニュアルで作っていたが、ここまできたら GitHub API を使って Pull Request 作成は自動化してしまえばい。

そして、日頃 HipChat を使ってるのだから、チャット上で hubot にデプロイをお願いしたら Pull Request を作ってくれる、とすれば「デプロイ始めまーす」みたいな宣言いらずで一石二鳥である。

というわけで作った。

hubot にお願いするとデプロイ用 Pull Requet が作られて、CircleCI がデプロイしてくれる、そんなワークフローができあがった。万歳。

GitHub や * as a Service を前提にしたワークフローの時代

GitHub や CircleCI のようなサービスがあるという前提でワークフローを構築していくとこんな感じで工夫次第でいろいろ面白くできる。Web サービスと Web サービスを繋げて、さらにそこに自分たちの開発フローを組み込んで一つのフローとして完成すると、なかなかに爽快である。

この辺の開発は、もうこういったWebサービスのような、一見するとプログラム部品としては見えないようなものものも、開発のための一つのプログラマブルな部品であると認識できるかどうか、ライブラリやフレームワークと同じ類のものであるとフラットに見れるかどうかそういう感覚が要求されるように思う。

それは Web 2.0 で Web API だなんだと言われてきたころからあったことなのだけれども、その感覚が、フロントエンドだけではなくて、こうして開発プロセスのワークフローの組み上げににも適用できるようになったというのが、自分としては昨今この領域が面白く感じる理由のひとつでもある。

逆に言えば、こういう領域に開発リソースをうまく継続的に投資できるかどうかということが今後の開発組織のあり方として重要な要素だと思っていて、KAIZEN では会社が立ち上がった当初から CTO と話して "Developer Productivity" という、この手の仕事をミッションにしたチームを構成するようにしてきて、その形が固まってきたところ。それがこういう形で面白い成果に繋がっている、と思っている。

Cask

昨年 ELPA で elisp を管理 - naoyaのはてなダイアリー に書いたとおり、昨今は Emacs にもパッケージ管理システムが搭載されいて、どこからか elisp をコピペしてきてその後管理できなくなる・・・みたいなことはなくなった。

ただ、じゃあ ELPA で全て解決したかというとそんなことはなくて、ELPA はパッケージのインストール自体は簡単にしてくれるけれども、それだけだった。

elisp の管理も Bundler のように入れたいパッケージ一覧を書いて bundle install すれば全部まとめて入るみたいな、そういうのが欲しい・・・と常々思っていた。

と思っていたら、Cask というのを見つけた。これがずばりそのものだった。

(source gnu)
(source melpa)
(source marmalade)

(depends-on "ag")
(depends-on "anything")
(depends-on "auto-complete")
(depends-on "browse-kill-ring")
(depends-on "color-theme")
(depends-on "elscreen" :git "git@github.com:knu/elscreen.git")
(depends-on "flycheck")
(depends-on "git-gutter")
(depends-on "pbcopy")
(depends-on "popup")
(depends-on "popwin")
(depends-on "powerline")
(depends-on "quickrun")
(depends-on "recentf-ext")
(depends-on "zlc")

;; prog modes
(depends-on "coffee-mode")
(depends-on "go-mode")
(depends-on "js2-mode")
(depends-on "json-mode")
(depends-on "less-css-mode")
(depends-on "motion-mode")
(depends-on "puppet-mode")
(depends-on "rhtml-mode")
(depends-on "ruby-mode")
(depends-on "sass-mode")
(depends-on "slim-mode")

(depends-on "rubocop")
(depends-on "ruby-block")
(depends-on "ruby-electric")
(depends-on "ruby-end")

(depends-on "go-autocomplete")

こんな感じで Cask というファイルを用意しておいて

$ cask

これで、.cask ディレクトリ以下に所望のパッケージがインストールされる。

$ cask update

でまとめてパッケージをアップデートすることもできる。

途中

(depends-on "elscreen" :git "git@github.com:knu/elscreen.git")

とあるように、パッケージソースを GNU や melpa ではなく Github レポジトリにできる。これがかなり嬉しい。

Cask で入れた elisp にロードパスを通すには二つやり方があって

  • cask exec emacs で起動する
  • init.elcask-initialize を呼んでやる

の二つ。

元々 Cask は Bundler に同じく elisp の依存関係を特定のディレクトリ内に封じ込めてやって、ライブラリの開発などで無用のトラブルを避けるために開発されたもの。前者はそのユースケースのための方法。

一方、Cask は普段使いの Emacselisp 管理にも使えて、後者がその方法だと思われる。

Cask をワンライナー ($ curl -fsSkL https://raw.github.com/cask/cask/master/go | python) で入れた場合

$ cask init

すると ~/.cask ディレクトリが作成されてそこに cask.el が入ってるので、これを使って

;; cask
(require 'cask "~/.cask/cask.el")
(cask-initialize)

と、init.el の中で呼んでやる。すると普段使いの emacs~/.emacs.d/.cask ディレクトリ以下を読むようになる。このディレクトリ用の Cask ファイルは ~/.emacs.d/Cask になる。

あとはこの Cask ファイルと init.el を git で管理すれば十分、ということになる。実際には Cask で入れられないパッケージ化されてない elisp は相変わらずロードパスに置かれたままだけど、その辺はおいおい整理していこう。

上記手順は記憶を頼りに適当に書いてるので詳しくは http://cask.github.io/usage.html などを参照されたし。

Emacs を使い始めてはや15年、ようやくまともな elisp 管理ができるようになった。

HBFav の不具合が直りました

HBFav で新着ブックマークが正しく取得できない不具合が発生しています - naoyaのはてなダイアリー で報告しました HBFav の不具合ですが、はてなブックマーク本体が修正されたことにより HBFav 側もこれまでどおり動作するようになりました。

ご利用の皆様にはご迷惑をおかけしました。また、修正にあたったはてなスタッフの皆様に感謝申し上げます。

引き続き HBFav をよろしくお願いいたします。

JAWS DAYS 2014、Immutable Infrastructure について

何もかも投げ棄てて Dark Souls II をやりたい気持ち抑えながら JAWS DAYS 2014 で Immutable Infrastructure について話してきました。以下、資料です。(Embed できないのでリンクです)

https://speakerdeck.com/naoya/immutable-infrastructure-number-jawsdays

Immutable Infrastructure トラックのトップバッターだったので、そもそも Immutable Infrastructure とは何か、どのような背景でこのような概念が提唱されるに至ったのか、そして現在は。またこれから何が変わるのかみたいな、大枠の話にフォーカスして話しました。会場は Immutable Infrastructure トラックは立ち見が出てるくらい盛況で、やはりこの分野に注目が集まってるのだなと実感しました。

続けて 3/25 にも Immutable Infrasturcute Conference #1 があります。こちらの方は聴衆の顔ぶれがまた JAWS DAYS と少し異なりそうなので、開発プロセスと関連づける形で話してみようかなと思います。とはいえ、こちらもオーバービュー的な役割だし結構かぶるかもしれない。

ちなみに、講演中 Immutable Infrastructure / Infrastructure as Code と発音するのに10回以上噛みました。

HBFav で新着ブックマークが正しく取得できない不具合が発生しています

HBFav で一昨日ほどから、新着ブックマーク取得時に正しく取得できない、具体的にはすでに表示済みのエントリが二重に表示されたり、新着で取得したものが更新すると消えてしまう、という現象が発生しています。

こちら、昨晩にはてなブックマーク本体側から報告のあった以下の不具合が原因である可能性が高いです。

HBFav ははてなブックマーク本体の、お気に入り機能の API (RSSフィード) からデータを取得している都合上、本体側での不具合の影響を受けることがあります。

経過は以下の Github Issue でも議論されています。

ご不便をおかけしますが、回復までしばらくお待ち下さい。

# 中の人ガンバレ

些末なコードレビュー

朝起きて布団から出るのがつらいので、HBFav をつらつらと眺めていた。

あるサービスの JavaScript が重いとか、そのコードが難読化されてないとか、担当者とおぼしき人間が書いたコメントがそのまま残ってるから消しましょうよとか、そんなことが書かれていた。JavaScript が重い、という話は結局そのサービスの JavaScript が重かったのではなく、ユーザーが自分で導入した広告が重いというだけの話だった。

コードが難読化されていない、趣味の製品ではなく会社の製品なのでコメントそのまま残ってるから消しましょう・・・実にくだらない。

ところで話は変わってコードレビューについて。

コードレビューに慣れないチームが、何の考えもナシにコードレビューを始めるととにかく気になったこと大小様々な指摘が行われることになる。一見、いろいろな指摘が出て議論が活発になっているように見えるが、だいたい議論が紛糾しているのは「コードのインデント幅が違う」とか「return が省略されてる。俺は return があったほうが好み」とか「その場合は字下げをした方が綺麗にみえるんでは」とか、そんな些末なことばかりである、ということが多い。必ず一度は通る道である。

そんなことを延々議論していたって、はっきり言って何の意味もない。何の意味もない、は言い過ぎにしても、そんなところを改善したところで実質的な品質は何ひとつ上がらないわけだし、どうしても揃えたいなら lint ツールか何かで機械的にチェックすればよいことであって人間がやることではない。

やらなければいけないのは、「その設計は拡張に対して開いていないから開くべき」とか「これではエッジケースが想定されていないからこういう不具合につながるのでは」とか「そのテストでは後日見返したときに第三者が要求仕様を解釈しづらい」とかそういう指摘である。これらはちゃんとコードを読んで、コードの構造を把握して、そこに書かれているコード以外のシステムの全体感が頭に入っていて、初めてできる指摘である。当然、それをするにはレビュワーにも知識とスキル、そして真摯な態度が要求される。

JavaScript にコメントが残っているからダメだ、なんてのはインデント幅が2じゃない、4にしろ、と言っているようなものである。自転車置き場の議論を読むべき。難読化は、しなければいけないというものではない。仮に難読化ではなくソース圧縮だって速度的にそこが律速ならするべきだが、多少の JavaScript の文字数を減らしたところで、他のファイルとのサイズあるいは gzip 圧縮との相対感からいくとその効果はハナクソみたいなものであることがほとんどだ。

インデントがとか、コメントがみたいな指摘をしているのを目にすると、くだらない、と妙に腹が立つ。

なんで腹が立つのだろうか。

それは過去の自分がまさにそれそのものだったからだ。人間、他人に腹を立てるときは、そいつがあまりにも自分に似ているか、そいつが過去の自分・・・自己嫌悪の対象だった自分と重なってみえるからだ。自分の実力のなさをごまかすため、自分を大きくみせたいがために、些細な指摘をする。そのコメントは必要ないとか、if 文の条件の書き方がなってないとか、そこの括弧は省略できる、とか。そしてそんなことで優位に立ったと、自分を大きく見せられたと、思いこんでいる。やがて時が経ちいろいろ経験を積んだ頃になって、当時周りの先輩たちは、お里の知れてる自分を生暖かく見守ってくれていただけであることに気づくのである。

追記

自分は元のエントリを明確に批判しているが、それが原因で話があらぬ方向に行くのを避けようと思い敢えてどのエントリかは書かなかったが、この記事が拡散される中、それこそ要らない誤解を招きそうなので追記する。

そしてはてなブックマークのコメントへの反応その他を見ていると「コメントが残っていたことではなく、コメントの内容に問題があったのが問題なのでは」と指摘があった。自分は、上記エントリを読んだがコメントの「内容」のどこに問題があるのか全く理解できなかった。

パスワードなど、何かユーザーやこのサービスに不利益になることがコメントに書かれているのか? 古いコメントが残っていてコードの動作と反するような記述になっているのか? コメントが原因で、そのほか悪影響を及ぼすようなことはあるのか。見た限り、そんなことはなさそうだ。もしそのようなコメントであれば、それは当然コードレビューでも指摘するべき。パスワードが云々のような本当の意味で致命的なものなら、第三者がインターネット上などで指摘することは重要だと思う。

一方、このエントリに書かれているのは「恥ずかしいコメントを書いちゃだめ」「趣味ではなく会社の製品なんだからコメント消しましょう」「コピペと言わずに共通化」と言いましょうというような指摘で、いずれもどうしてそうしなければいけないのか自分にはわからない。むしろ「些細なエラーなので無視する」というコメントなどは現在 console に出ているエラーは無視しても構わないと開発者が判断しているというヒントになるのであり、そのコメントがあることが有益であるとすら感じた。

はてなブックマークの方では「コメントの内容が致命的にひどい」とあるが、本当に致命的にひどいのであれば、何か致命的な問題が起こるだろう。そのようなことが起こっているのか。また、一企業がコメントで意図をあけすけにするのは微妙だから、というのは全く賛同できない。なぜ隠す必要がないようなことまで、無難な方に倒して、隠す必要があるのか。

コードレビュー中にコーディング規約に類することには意味がない、と書いた。一方「意味がないとも言わないが」とも書いた。意図としては、本来設計そのほかもっとレビューしなければいけない項目があるのに、規約についてばかり議論していてもしょうがない、それは木を見て森を見ずだということを述べている。当然、初学者の新人がインデントもコメントもぐちゃぐちゃなコードを書いてきたらそれはレビューで指摘するべきだし、レビュイー本人の成長にもつながる。それは程度の問題である。

本記事を読む各人が置かれている環境・・・まわりに規約を全く守れない開発者がいる、規約すらない、などいろいろな状況があるとは思う。その自分の環境に重ねていろいろ想像されることは各人の自由だと思うので、そのことについては特に思うことはない。すくなくとも、自分の場合も、自分の周囲の開発者のスキルや傾向などを想定・前提として書いている。

自分が暗黙的に想像としているサービスは自社開発のWebサービスであり、顧客に納品するミッションクリティカルな業務アプリケーション、ではない。元になったエントリもまた、自社開発のWebサービスについて論じている。(この一文は対象ドメインが違うと作法も異なるであろうという意図で書いたが「ミッションクリティカル」は余計な要素だと感じたので del とする。決して自社開発Webサービスは品質を犠牲にしてよいと言っているわけではない)

追記2

上記2エントリのうち、後者 id:hevohevo さんに関しては、コメントの内容については問題がなく肯定的であると論じています。それに関して、上記2エントリどちらもが批判的なエントリを書かれていると、本エントリの読者の方に受け取られかねない記述をしてしまったことをお詫びいたします。

id:hevohevo さんのエントリで私が賛同できない部分は、「会社のプロダクトなのでコメントは隠そう」「miniftyすべき」という点です。その賛同できない理由については前述の通りです。また、私は賛同できないが id:hevohevo さんにはコメントを隠す、minify するということには理由があろうということは追記されていました。(いずれにせよ、私はそれにはあまり賛同できません。minify すべきか、コメントを隠すべきかはその開発チームのポリシーや優先度で決まることであり、どちらが良いかという一般論ではないと思っています。)

ご迷惑おかけしました。

Rebuild #29 でアプリのレビューをして欲しいと話したらたくさんレビューが来た

@miyagawa の Podcast (まあ、このブログを読んでくれてる人にはわざわざ説明するまでもない) である Rebuild #29 で、iOS アプリのレビューの話になって、自分が作ってる HBFav も☆5個のレビューくださいね、と冗談半分で言ってたら、きづいたらレビューが50件近くついてました・・・!

Podcast、出てみるもんですね! w

引き続き、☆5レビューをお待ちしてます。