はじめに

今回はWSA(Web System Architecture)研究会第1回12/23(土)向けの予稿です。「ミドルウェア開発で一般に通ずるかもしれない体系化」をテーマに他OSSのスタイルなども例に挙げながら、それらを体系化することに挑戦できたらと思います。節の最後にできるだけ一般化にまつわる箇条書きを残すよう心がけました。

OSSの「ミドルウェア開発」ということで、OSS・チーム体制で作っていくソフトウェアだと当てはまるポイントが多いかと思います。その他、製品やサービスとしてリリースしていく立場的なこと、要素技術について記述しました。

といっても論文のような形式ではなく、ブログでよくある開発方法の紹介みたいなスタイルになっちゃっています。 私が主に得意とする専門領域はタイトルにあるようにミドルウェア付近にあります(ただしユーザランドの範囲で)。 もしくは、ソフトウェアスタックにおけるバックエンド側にあたるでしょう(ハードウェアは除く)。 また、Jubatusの開発コミュニティとしてはApacheプロジェクト及びLinuxに大きく影響を受けていたプロジェクトであるといえます。 それらを前提に読み進めて頂けるとコンテキストの理解がスムーズに進むと思います。

※ここに書いてあることの大半は当時の私の記憶からたどったものです。現在のJubatus開発チームを代表した意見ではありません。ご注意ください。

当日発表で話すこと

長文になってしまったので箇条書きで当日の発表予定のテーマをまとめておきます。 以下の点に注目して読んで頂けるとWSA研参加者の方には負担が少ないと思います。

  • チームのパフォーマンスの最大化と単純作業のルーチン化・ふりかえり
  • 開発プロジェクトにおける意思決定、議論の仕方
  • ソフトウェアの品質の担保

Jubatus紹介

Jubatusとはなんぞやという方にざっくり説明すると、機械学習(多クラス分類、レコメンドなど、もっとたくさん)のアルゴリズムを実装したミドルウェアです。利用形態としては、機械学習のエンジンの乗ったサーバが起動して、ユーザ(開発者)からはクライアントライブラリ(RPC)を介してエンジンを利用します。

機械学習がなんぞやという方は、スパムフィルタとか記事のレコメンド(ECサイトで商品の推薦)を実現するときに使われる技術と考えてください。

ウェブとの関わりについては、ウェブのアクセス解析なども統計的な分析手法が用いられたり、A/Bテストが普及していったり、、、は機械学習そのものを示すわけではありませんが、上で述べたスパムフィルタ・記事のレコメンドなどはウェブ技術として当然のように使われることも多いのではないでしょうか。そういう要素技術と、加えてJubatusはミドルウェアとしての立ち位置で、ウェブと関わりを持ちうるソフトウェアということになります。ウェブを構成するミドルウェアという視点からWSA研へ、Jubatusの開発のチームビルディングや開発の形態といったミドルウェア開発の体制について有益な情報を提供できればと思います。 もし機械学習のテストや、サービス運用的な側面を期待されていたら申し訳ないですが、そういった事柄については記述しません。最近流行のDeep Learningとも毛色の違う話です。

開発チームへの参加と、チームビルディング

Jubatusは2011年10月にOSSとして公開され、私は2011年11月Preferred Infrastructure入社でした。私は2012年2月頃からJubatusチームに参加し、2014年頃までチームで開発をしていました。

チームに参加して最初に感じたのは、

  • 「機械学習よくわからん」
  • 「使い方もよくわからん」
  • 「インストールの仕方もよくわからん」

あたりだったと思います。今でも機械学習は初級者くらいなほうだと思います。

決まってないことが多い初期段階はつらいこともある

初期のコード・ビルドスクリプト及び開発体制は、結構しんどいものでした。 というのは、ささいなことから重要なことまで決まっていないものが多く、それを決めるのが大変だったように思います。 以下はプロジェクト固有のことばかりですが、当時はこんなものでした。

  • コーディングスタイル(C++03)がない(ファイル名・クラス名の命名規則がない)
  • コードやテストが構造的でない部分がある
  • 不適切な継承関係
  • wafでのビルド方法が確立されていない(使用方法のノウハウが確立せずにpkg-configの使用)
  • チーム内のメンバーの役割が明文化されていない
  • その他(あげるまでもないが、積もり積もって身動きが取れない)

これらの欠点にも理由があり、研究駆動で、アルゴリズムを実証していくという立場の少人数チームであれこれしていて他にメンバーもおらず手が回っていないという事情もありました。後から追加で入ったメンバー(私も含む)も一緒になり、粛々とこれらをチームで解決していきました。

チームとしてやっていくための形を整える

私の心に秘めたミッションとしては、OSSとしてちゃんとやっていくことだった気がしています。 当時チームメンバーで実行したこととをざっくり書くと、以下のようになります。特に★をつけた箇所は、他のOSSでも共通な事柄だと思います。

  • ★C++のコードスタイルの制定・統一・cpplint(Google, in Python)の利用
    • コードが綺麗に近づくのはもちろん、保守性にも影響します
    • 今やり直すならclang-formatが便利です
  • ★コードレビュー(PR: Pull Request送り合う)の徹底
    • コンポーネントごとに担当者(リーダー)を決めて、必ずその人も他の人にレビューをお願いするレビュー体制を作りました
  • ★週定例会議の議事録をWikiに公開するようにした https://github.com/jubatus/jubatus/wiki
    • 議事録も公開
      • できるだけ事前に決定事項・相談事をメモっておくスタイルです
      • ミーティングというのはあらかじめ共有していたことを、決めたり相談する場所です
      • 一般公開する狙いはチーム外部の貢献者へ情報共有できればと思っていました(効果のほどはわかりませんでした)
  • ★リリースのバージョン番号をセマンティックバージョニングに近い物を採用
  • 開発環境やビルド方法の統一
    • ビルドの再現性を上げるための文書化や、Vagrant/Packerの使用も試しました。今の運用は確認できていませんが、自分ならDockerを使いたいです
  • テストも増やした、たくさんリファクタリングした(ペアプログラミングもした)、継続的インテグレーション(Jenkins, Travis CI)の整備
    • こちらは、開発している以上当たり前のことですね

コードレビューについては海野さんの発言も参考になります。

これらはJubatus特有の問題点のように見えますが、他のプロジェクトでも類似点が見つかればどんどん適用できるものだと思います。議事録の公開は Mozilla Firefoxの議事録 を私が参考にしたものですし、コードレビューの体制はGitHubのPR活用に伴い、フローがGitHub Flowへ変わっていったタイミングでした(他プロジェクトでの採用はわかりませんが、GitHub Flowの翻訳が出てきたのも2012年です)。また、ここ数年普及してきたGo言語は標準でコーディングスタイルのフォーマッタ(gofmt)を提供しており、フォーマッタ適用事例数を示す証拠はありませんが、ここ数年でフォーマッタを使う慣習は進んできたのではないでしょうか(JavaのIDEにもフォーマッタはありますね)。

その他、開発チームとしては外部からのPRも受け付けるけれど、Linuxみたいなチーム体制が望ましいのかもしれないと相談していました。すなわち、困ったときはリーダー(独裁者)が意思決定を行うということです。規模の差はあれど、Linuxの開発スタイルは、小さなOSSにも大きく影響していると思います。

ここでのまとめとしましては、★をつけたところや、先人の知恵あるいは事例としてOSS開発を行っている組織のやり方というのは参考になる、無視できないものだと思います。

Contoributor License Agreement (CLA)

有名なOSSや組織(Apache, フリーソフトウェア財団, GoogleのOSS)などには、コミットにあたってCLAというものの提出を求めています。 Jubatusは http://jubat.us/ja/developers/cla.html にあります。

まず、このページをあらかじめ読んでおいて欲しいです。

開発チームが企業のような営利組織であるときは、OSS以外のビジネス展開も考えたときCLAがあると都合がよいことがあります。著作権の権利者が自分たちであると、OSSとは別に、それをベースにした製品を作ったり(※1)異なるソフトウェアライセンスに変更して配布することもできます。もちろん、CLAなしでライセンスの変更ができないわけではないですが、パッチをマージした後になって貢献者全員にライセンス変更の許可を取るのは大変な作業でしょう(※2)。

ここは体系化というよりかは、OSSの開発チーム(組織)を運営していったり、自社の製品をOSS化していくならば自分たちがどういうスタンスであるかに依存することではあります。

開発で苦労したところ

こちらもJubatus開発固有の話が多くありますが、

  • 使っているユーザー(開発者)層が見えない、みえづらい
    • ニーズではなくシーズベースでJubatusを作った弱みでもあります。できればニーズがはっきりしたものを作れるといいですね
    • ML(Google Groups)がありますが、基本的にユーザ同士の盛り上がりは少ないです
  • 外部からの貢献(PR)は嬉しいが、扱いに困ることも
    • 開発思想(文書化が難しい)の共有が難しく、PRの完成度が高いけどポリシーが異なるからと断るのも気が引くこともあります(普通はCONTRIBUTION.mdのようなファイルをあらかじめ公開し、レビューして修正を依頼しますが、変更量が多いとお互いつらいです)
    • もちろん、開発チーム外からのPRは積極的に受け入れていきたいという気持ちもあります

といったことがありました。 特に中後半は、私の経験上、こういった4,5人の小規模を超えたくらいのチームでのOSS活動をしたのがこれっきりなので一般化して良いか悩みますが、OSS開発をチームでやるなら覚えておくべきことだとは断言できます。

チームでの決定事

意思決定ポリシーもおおよそチームが組み上がっていくに従って自然と決まりました。 ミーティングで議論するのは物事の決定でよくあることですが、その主目的の議論が、自転車置き場の議論にならないよう迅速に進めるためにも枠組みが作られました。 以下のようなものです。

  • 特定のドメイン専門のメンバー約2名で集まって相談、決定する
    • 集中的なリファクタリングも、約2名ごとのグループを作って担当分けして実施していました
  • 困ったときはリーダーが決める

OSSで開発方針を相談するとき

Jubatusの開発では、大きな機能追加や変更をするときに、簡単な文書を書いて共有する習慣がありました。デザインドキュメントに近い物ですね。オフラインで議論をするということももちろんありましたが、ここではデザインドキュメントについて紹介します。

デザインドキュメントとはその名が示すように、設計の指針となる文書で、ソフトウェア開発の設計をまとめたものです。設計上の選択肢を洗い出したり、実装上のポイントなどを取り上げてチームで議論したりします。厳密に書かれる仕様書ではありません(でも重要な文書です)。

デザインドキュメントを書く効果としては、執筆者の頭の中で人に説明するための項目が整理されることに加え、執筆者の考えはもちろん設計指針を他者に共有でき、議論できる状態になる点にあります。実際の設計や実装を始める前に議論したり、チーム内でレビューできるようになるというのは重要なポイントです。私が過去に所属していたチームでは、特にデザインドキュメントを書くことを重視することが多かったように思います。

Jubatusはおいておいて、最近個人的に注目していたのがGo言語の開発スタイルです。 貢献者用のドキュメントに “Discuss your Design”という項目があるように、作業を進める前に文書を書いて共有したり、issueを作るというものです。

また、Go言語のGoogle Groups (Forum)を見ると、過去にデザインドキュメントが共有され、それについて議論するということが行われていました。 こちらにも過去のデザインドキュメントがまとまっています。

Go言語はミドルウェアとはいわないですが、設計指針が重要なところでデザインドキュメントを書いて共有したりレビューする体制を入れるのはOSSに関わらずソフトウェアをチームで開発する上で良い習慣だと思っています。

OSSで公開する意義

なぜOSSのLGPL 2.1にしたのか、チームで聴いたような気はするのですが覚えていません(ということにしておいてください)。 Apache License 2.0にしない理由はありまして、特許をもし取っていても無償で利用許可を与えてしまうという点があります。なので、特許を取得している場合はApache Licenseは見過ごせないライセンスです。ご注意ください。

OSSを採用ではなく、OSSとしてソフトウェアを公開する側の利点はいくつかあります。

  • 広告の効果が大きい
    • 伝家の宝刀みたいなものなので、使いどころ(プレスリリースのタイミングと場所)が重要です
  • 誰でも無償で試せる(使うリテラシーがあれば)
  • 公開しちゃったら、後はOSSの権利と法の範囲でなんでも試せる
  • 外部からの貢献の可能性
  • 自分が所属する企業のソフトウェアを、自分が退職しても利用したいとき
    • Google発のOSS製品でこういう話を耳にすることがあります

逆に期待できないこともあります。

  • セキュリティ向上、それに伴うバグの修正
    • 最近は重大な脆弱性がOSSから発見されることも多く、OSSだからチェックされていてセキュアというわけではない
  • 広告の効果の過信
    • 1度やると2度目以降のリリースは有名なソフトウェアなど余程期待されてない限りメディアから注目されない(経験的に。ニュースメディアからウォッチされてニュースになるというのは、取り上げる価値があると、記者側からして盛り上がってみえているという認識です)
  • 外部からの貢献・issueが作られる可能性
    • 面倒な人に絡まれるとやっかい。自分たちだけでワイワイ内輪でやっていきたい場合、OSSは不向き
    • バグレポートに熱心なユーザや、パッチを投稿してくれる開発者がいないと貢献は期待できない

これらは私だからこそ書けるといったことや、主張できることはありません。一般論ですね。

リリースマネジメント

リリースマネージャーをやっていたので、それにまつわる話も書きたいと思います。厳密に以下の定義でやっていたわけではありませんでしたが、ソフトウェアの開発チームそれぞれに適したリリースマネジメントのやり方があると思います。

リリースについて、チームで合意していたことして次のことがあります。

  • 事前に定例会議でリリース日を決定する(月1のマイナーリリースと決まっていたので、リリースが終わった次の会議で日程を決定していた)
    • リリース日にあわせてコードフリーズの日程を決定する
    • 例えば2営業日前にコードフリーズする
  • リリース日にやるべきこと、担当のメンバーを割り当てる
    • リリースビルドなどはできるだけ自動化(人がやらなければならないルーチンワークだけ人が行う)
    • ChangeLogの作成、git tagの作成はほぼ手動(必要に応じてコメントを付ける)
    • リリース報告のメール(英語)やブログ(英語/日本語)を書いて公開する

あるとき、リリース日程のことばかり優先して、解決したissue/バグが少ないままリリースしそうになったこともありました。その時は事前に話し合い、次のリリースまでにより多くの問題を解決してリリースするという判断で延期になったこともあります。

ルーチン化も悪いところがあり、それは作業がマンネリ化したり思考停止したりすることがあるところです。毎回のリリースが終わってから、ふりかえりということでKPT(Keep, Problem, Try)などを集計していましたが、それでも何回も繰り返すとネタが尽きることもあります(Problemがないのは良いことですが)。

これに対する素敵な解決策を私は持っていませんが、定期的にイベント(勉強会)や大きめのリリースを検討したり、技術的な雑談(最新技術の共有や、ソフトウェアの理想論)をするなどして頭のオンオフを切り替えていけるのが理想だと思いました。言語化しづらいですが、遊び心や心の余裕を持つということですね。

まとめると、

  • リリース前にするべきこと、リリース中にやるべきこと、リリース後にやることを明確化し、作業としてこなせる体制を作る
  • 作業の自動化/ルーチン化が進むと、単なる作業になってしまうこともあるので、ソフトウェアのゴールや、最新技術の適用・効率化などにアンテナをのばして議論する

フォークしたライブラリとそのメンテナンス

Jubatusではサーバ・クライアント間の通信にはRPCを利用します。プロトコルにはMessagePack-RPCを採用しています。当初はpficommonという内製のC++ 03向けライブラリ同梱のものを使っていたのですが、いまいち使い勝手がよくなかったため、移行することにしました。移行先のC++ライブラリ(MessagePack-RPC C++)は公式版が開発当時既にメンテナンスを終了しており、依存する非同期ライブラリ(mpio)含めてチームでフォークしたものをメンテナンスすることになりました(当時は gRPC はありませんでした)。バックエンドのライブラリの自らメンテナンスをするというのはある種負荷が高い作業であり、本来の開発作業を圧迫しかねない事態です。しかし、自らメンテナンスする能力と時間(マンパワー)があるのであれば依存ライブラリ(OSS)のフォークも時として有効だと思います。

こういった依存ライブラリをメンテナンスするノウハウは、フォークとまではいかなくてもOSSを自社製品(プロプライエタリ、ソースコード非公開)に組み込んで提供する場合には身につけておきたい、というよりむしろ必須スキルだと思います。最終製品を提供するのは自分たちなので、バグが自分たちの書いたコードのせいでなくても、お客様にとっては結局不具合(バグ)です。Linuxあるあるで、商用ソフトウェアが公式にサポートする環境としてRHELを挙げていることがあるのは、開発・提供する側としては責任やバグの追及先を便利に限定して扱えるという利点(理由)もあると思います。

そういうわけで、以上は製品提供的な話になってしまいましたが、ウェブサービスを提供する側として何らかのミドルウェアを利用したとき、そのミドルウェアの直接のお客様はサービスプロバイダである自分自身達という形になります。 OSSは無料で利用することもできますが、バグの責任を取ってくれるわけでもなく、バグがない保証はありません。もちろん、何らかの有償製品を利用していてバグが直るということがあったとしても、バグが発生した時点で何らかのアクション(傍観も含む)を取ることに迫られるでしょう。 そんなとき、OSSでかつ、その内部に詳しく自分たちでもメンテナンス可能なエンジニアが自分たちであれば、解決はスムーズかもしれません。

大手LinuxディストリビューションベンダがLinuxカーネルコミッタを抱えるように、社内でミドルウェアのコミッタを多数抱えるという事情としてこういった理由もあるのではないでしょうか。 特にApacheソフトウェア財団は多くのソフトウェアを擁しています(最近はある種墓場っぽい印象も受けますが)。また、Linuxとその周辺ディストリビューションも開発・運用基盤として無視できない物であり、自分たちがOSS(ミドルウェア)開発する際のスタイルとしても影響を受けざるを得ないものだと思います。

  • OSSミドルウェアの開発だからといっても、依存ライブラリのバグにも気を遣う必要があったり、場合によっては自分たちでメンテナンスまですることがある
  • OSS開発、歴史に学ぼうとするとApacheソフトウェア財団やFSFを参考にする部分も出てくる
    • それは開発チームのことでもあり、実装方法やリリースに至るところも過去に学ぶことがあるということです

どこまでテストするとよいのか問題

上の依存ライブラリの話から、議題を変えてテストをどこまでやるかといった品質保証であるとか、、誰・何が可用性を担保するのか(SLA, Service Level Agreement)というテーマもあると思います。 今回の予稿では時間がなかったため、問題解決までいかず、問題提起と既存のアプローチの紹介をさせてもらいます。

ソフトウェアの品質保証について言及したとき、

  • 開発時期の終盤にいくにつれてテストの失敗数・報告されたバグの数を数えたり(数の減少をひとつの指標としたり)
  • カバレッジをあげていくのを見たり
  • ベンチマークやストレステスト(非機能要件)

などなどやるべきこと、見るべき点は多くあるように思います。 プログラムやシステムは書いたとおりには動きます。 作ったシステムが正しく動いたとき、テストを書いたから動いているのではなく、書いたとおりに正しく動いたからテストが通ったともいえるでしょう。 そして、作ったソフトウェア(システム)の可用性は誰が面倒みてくれるのか? というのも厄介で、人によっては楽しい問題(クイズに近いもの)かと思います。

セキュリティの文脈ではTrusted computing base (Wikipedia)という概念があります。 どこまでをコンピュータの基盤(ハードウェア・ソフトウェア)の中で信頼し、どこからが自分達のソフトウェアの振る舞いの範囲(責任)かという考え方として参考になるでしょう。

Linuxやウェブサーバはバグのない振る舞いをする、もしくはバグが見つかったとしても可能な限り早い時間で修正できる、というような仮定を持ってアーキテクチャを組んでいくことは可能です。 また、複数のシステムがいくつか組み合わさることで、問題がシンプルになることもあります。 そういった側面から、ソフトウェアのバグを減らす、可用性(ここではSLAもひとくくりにします)を上げる取り組みを考えるのもアプローチの一種かと思います。

運用のような文脈でいうとChaos Monkeyのように障害を意図的に発生させて、それに対するシステムの対応を見るアプローチがあります。 また、ソフトウェアテストでは単純なランダムテストよりも、ランダムさに加えてあえて境界領域などを攻めるようなデータとをテストデータに使うFuzzingというテクニックもあります。 そして、プログラミングの技芸(テクニック)的なものとして、ランダムさを取り入れるアプローチなどあります。

品質をあげる取り組みに対する問いについてWSA研で直接フィードバック頂いたこと

後で別文書でWSA研に対するまとめを書きたいですが、この文書内に注釈する形で頂いたコメントを記述しておきたいと思います。

  • テストはもちろんするが、品質の上げ方としてOSSとして出して利用してもらったり、アカデミックに論文にしていく by matsumotoryさん
  • Chaos Monkeyは単に障害を起こすだけではない。それらについては Chaos EngineeringというO’Reillyの本にまとまっている by y_uukiさん
  • ソフトウェア工学で昔からやられているところが役立ちそう。例えばISO25000というものがある by masayoshiさん
    • http://www.meti.go.jp/policy/it_policy/softseibi/metrics/product_metrics_appendix.pdf
    • なぜそういった指標が使われていないのか?(使われていなかったのか)
      • → 柏原:チームメンバー全員でissue/チケットなどにバグを正しく報告する必要がある。私はJenkinsを好きでやってるからJenkinsおじさんを継続できているが、それと似ていて、テスト指標に関する実施を強い意志を持って実行する人間がいてチーム全体で合意してやっていかないと正しく実施するのは難しいのではないか(と思っている)。
    • アジャイル的な開発との親和性は難しいのかもしれない
      • →柏原:そう思います
      • その他私の現在の意見として:私自身バグの報告は正しくラベル付けなどできてないかもしれない。ソフトウェア工学のちゃんとした文書を読解するのは私の今後の課題。

まとめ

基本的にチームにおけるミドルウェアやソフトウェア開発全般で大事なことは、チームのパフォーマンスを最大限発揮するかということにあると思います。そのためには、いかにしてチーム体制を整えるか、そして余計なことを考えずにゴールに向かっていくことが重要ではないでしょうか。

OSSは公開されていますから、チームや社外部からのメッセージや貢献の可能性もあります。そのためにも、

  • 意思決定のために機械的に処理(反応)できる部分と、人手の作業をわけるポリシーを決めておく
  • 開発メンバー間で、Pull Requestなどを利用したレビュー体制や、開発スタイル・ノウハウの共有メカニズムを確立する(後から開発者が加わる場合でも参照しやすいようにドキュメント化しておく)
  • 必要に応じて、依存するライブラリを自分たちでフォークしメンテナンスすることもある。また、それが製品全体の質(デバッグしやすさ)につながることもある

という点に気をつけて体制を作ればよいと思います。 加えて、WSA研的に体系化したい点としましては、

  • チームの典型的な作業(開発、リリース作業、人の出入り、外部からの貢献含む)はなるべく定型化し、自動化ないし頭で考えない範囲の作業に落とし込む(考えるべきところで頭を使う)
  • コードレビューないしは、他の手段でもいいので、設計指針、実装の技法を共有する
  • リリース毎ないし定期的にふりかえりを行い、組織体制なり開発体制などを見直す
  • ソフトウェアデザインドキュメントを書いて共有、レビュー、議論する
  • (当たり前ではあるが)依存ライブラリも、リリースする最終製品やサービスの質に影響を与える可能性はあり、緊急時は依存ライブラリのバグもカバーできる体制を作れるようにしておく

といったところになります。 書いてみて、OSS以外の開発グループにも適用できそうな気がしてきました。 一部出典・参考文献がないのが寂しいところです。

この文書を読んだからといって既存のOSS開発組織にすぐ参加できるかは別の話で、その組織の受け入れ体制を確認することがまず第一でしょう。

最後に、WSA研の主査副査の方に事前に原稿のチェックをお願いしました。おかげでWSA研の方向性からあまりブレずに済んだと思います。ありがとうございました。元の文書から方向転換したことで、文書の読みやすさや各節のまとまりの不自然さへ影響もあるかもしれませんが、それら原稿・テーマの質・正誤含め文責は柏原にあります。