株式会社クリアコード > ククログ

ククログ

タグ:

クリアなコードの作り方: 変わらない値とわかるようにする

問題を調査する場合やプログラムを改良する場合、修正する場合などプログラムの挙動を把握しなければいけないことは多々あります。プログラムの挙動を把握する方法はいろいろあります。処理の流れを追う方法や特定の値に注目する方法や状態の変化を追う方法などです。

処理Aと処理Bの間でおかしくなっていることがわかっている場合は処理の流れを追う方法がよいでしょう。AからBの間の処理の流れを追っていけば問題の箇所を見つけられるからです。

特定の値がおかしくなっていることがわかっている場合はその値に注目して把握する方法がよいでしょう。その値が変わる可能性がある箇所に注目すれば問題を特定できます。

メインループ(GUIのイベントループや言語処理系の評価器のループなど)内の処理を把握する場合は状態の変化を追う方法がよいでしょう。状態を変えながら何度もループする処理では状態によって実行する内容が変わります。状態を把握することで実行する内容も把握しやすくなります。

ここでは特定の値に注目してプログラムの挙動を把握する場合にこんなコードだったら把握しやすいというコードを紹介します。これはコードを読む人視点の言い方です。コードを書く人視点で言うと、挙動を把握しやすいコードの書き方を紹介します、になります。

特定の値に注目する

「特定の値に注目する」とはどういうことでしょうか。それはその値が変わるタイミングに注目するということです。

たとえば、次のようなクラスがあり、@ageの値に注目するとします。

class User
  def initialize(age)
    @age = age
  end

  def age
    @age
  end

  def age=(new_age)
    @age = new_age
  end
end

@ageが変わる可能性がある箇所は以下の2箇所です。

  def initialize(age)
    @age = age
  end

  def age=(new_age)
    @age = new_age
  end

そのため、この2つのメソッドが呼ばれる箇所に注目します。なぜなら、ここでしか@ageは変わらないからです。ここに注目しておかしな値を設定している処理を見つけることで問題を特定できます。

users = {}
users[:user1] = User.new(-1) # 注目
users[:user2] = User.new(14) # 注目
puts(users[:user1].age)
users.each do |key, user|
  if user.age == -1
    user.age = 29 # 注目
  end
end

この例では@ageを変更できる箇所は2箇所でしたが、もっと変更できる箇所がある場合は注目する箇所も増えます。注目する箇所が増えるということは挙動を把握することが難しくなるということです。逆に言うと、変更できる箇所が少ないと挙動を把握しやすくなるということです。

ここまではコードを読む人視点の話です。

特定の値に注目しやすいコードの書き方

それでは、コードを書く人視点の話に入ります。

値を変更できる箇所が最小限になっているとコードを読むときに挙動を把握しやすくなるのでした。つまり、値を変更できる箇所が最小限なコードを書くと挙動を把握しやすいコードになります。

それでは、値を変更できる箇所を少なくする方法をいくつか紹介します。

セッターメソッド(ライターメソッド)を提供しない

前述の例ではコンストラクターとセッターメソッドが@ageを変更できる箇所でした。

  def initialize(age)
    @age = age
  end

  def age=(new_age)
    @age = new_age
  end

変更できる箇所を少なくするならage=メソッドを提供しなければよいです。

class User
  def initialize(age)
    @age = age
  end

  def age
    @age
  end
end

これで変更できる箇所が1つ減りました。age=メソッドがなくなったので使い方も変わります。

users = {}
users[:user1] = User.new(-1) # 注目
users[:user2] = User.new(14) # 注目
puts(users[:user1].age)
users.each do |key, user|
  if user.age == -1
    # ↓userを変更するのではなく新しくインスタンスを作ることになった
    users[key] = User.new(29) # 注目
  end
end

セッターメソッドを無くしても注目する箇所は3箇所から減っていないのですが、コードを読む側としてはうれしいことが増えました。それは、一度作ったインスタンスでは@ageは変わらないということです。

インスタンスは1度作られるといろいろな箇所で使われるでしょうが、インスタンスが作られる箇所はそれよりは少ないことが多いです。たとえば、以下の箇所でインスタンスを使っています。

users = {}
users[:user1] = User.new(-1)
users[:user2] = User.new(14)
puts(users[:user1].age) # 使っている
users.each do |key, user|
  if user.age == -1     # 使っている
    users[key] = User.new(29)
  end
end

あれ、使っている箇所(2箇所)より作っている箇所(3箇所)の方が多いですね。。。実際のプログラムでは使っている箇所の方が多いはずです。。。

使っている箇所が多い前提で話を進めると、多いと@ageが変わる可能性があるかを検討することが大変になります。そのため、検討する箇所が少なくなるのはコードを読む側にとってうれしいです。

コンストラクターでだけ値を設定する

セッターメソッドを無くした結果、コンストラクターでだけ値を設定するようになりました。

class User
  def initialize(age)
    @age = age
  end

  def age
    @age
  end
end

@ageを変更する箇所を減らすということであれば、次のようにすることもできました。

class User
  def initialize
  end

  def age
    @age
  end

  def age=(new_age)
    @age = new_age
  end
end

使うときはこうなります。

user = User.new
user.age = 29

これよりもコンストラクターで値を設定するコードの方がよい理由は次の通りです。

  • @ageが変更されるタイミングはインスタンス作成時だけということが明確になる。(=インスタンスを作った後に変更されない。)
  • @ageは必ず設定されていることが明確になる。(セッターメッソドを使う場合だとインスタンスを作った後に設定を忘れると設定されない。)

つまり、コードを書いた人の意図が明確になるので読むときに助かるということです。

「変更しない値はコンストラクターでだけ設定する」はコードを書いた人の意図を明確にする書き方なので、「このクラスのインスタンスは値を変更しないぞ!」という意図でコードを書いているときは積極的に活用したい書き方です。

変更できないようにする

これまでの例では、あえて値が数値になるようにしていました。これは、Rubyでは数値は変更不可能なオブジェクトだからです。文字列は変更可能なオブジェクトなので事情が変わってきます。

たとえば、次のコードはmessageメソッド経由でも@messageの値を変更できます。

class Commit
  def initialize(message)
    @message = message
  end

  def message
    @message
  end
end

次のようにすると変更できます。

commit = Commit.new("Hello")
puts(commit.message) # => Hello
commit.message << " World!"
puts(commit.message) # => Hello World!

これも防ぐようなコードにするかどうかはケースバイケース(オーバースペックの場合の方が多い)ですが、たとえば、次のようすればmessageメソッド経由では@messageの値を変更できなくなります。

class Commit
  def initialize(message)
    @message = message.dup
    @message.freeze
  end

  def message
    @message
  end
end

commit = Commit.new("Hello")
commit.message << " World!" # 例外:can't modify frozen String (RuntimeError)

Rubyは値(オブジェクト)を変更不能にする方法はfreezeですが、方法は言語ごとに違います。たとえば、CやC++ではconstを使います。

まとめ

コードを書くときに「変わらない値」とわかるような書き方にすることで、読むときに挙動を把握しやすいコードになるということを紹介しました。

いくつか書き方を紹介しましたが、中でも、「変更しない値はコンストラクターでだけ設定する」書き方は書いた人の意図を明確にしやすい書き方なのでそのような場面になったら活用してください。

なお、この話題についてまとめるきっかけになったのは次のようなコードを見たことがきっかけでした。外部で作られているオブジェクトに新しくroute_keyというデータを付与したいというコードです。

route = create_output
route.instance_eval do
  route.singleton_class.class_eval do
    attr_accessor :route_key
  end
end
route.route_key = key

このコードは最終的に次のようなコードになりました。セッターメソッドで値を設定するのではなくコンストラクターで値を設定するようになっています。

class Route
  attr_reader :output
  attr_reader :key
  def initialize(output, key)
    @output = output
    @key = key
  end
end

output = create_output
route = Route.new(output, key)
2016-08-17

WEB+DB PRESS Vol.86特集「1年目から身につけたい! チーム開発 6つの心得」の全文公開が始まりました

約1年前に執筆した記事の全文が、技術評論社Webサイトの開発者向け記事のコーナーで公開され始めています

全7章を短期集中連載の形式で順次公開していく予定で、本稿執筆時点では第6章までが公開済みとなっています。(5月2日追記:現在は全章を公開済みです。)

各章の概要

特集は全7章ですが、各章の紹介である第1章を除いた第2章から第7章までが、タイトルの「6つの心得」に対応しています。

前半の第2章から第4章は、*「チームで開発する際の、プログラムそのものの良い設計」*をテーマにしています。

一般に、開発者向けで新人さんなどを対象にした記事というと、GitHubなどのサービスやGitなどの個別のプロダクトの使い方を紹介する物が多いのではないでしょうか。 これらの章では敢えてその方向には振らずに、サービスやツールに依存しない一般的な知見の紹介に努めています。

開発者向けのサービスやツールは流行り廃りが激しいため、ともすれば、トレンドを追う事だけに振り回されてしまうというような事に陥りがちです。 しかし、ここで述べている基本的な設計の仕方や考え方についての理解を深めれば、いたずらにトレンドを追わされるのではなく、目指す設計や開発の仕方をサポートするための物として、トレンドの中から適切な道具を選び取っていけるようになるでしょう。

後半の第5章から第7章は*「開発やメンテナンスにおける、チーム内での良いコミュニケーション」*をテーマにしています。

チーム開発は文字通り「チームでの作業」ですから、チームメンバー同士でどのように連携するかが大切になってきます。 どんなに有能なメンバー達を揃えていてもコミュニケーションが取れていなければ期待したような成果は上げられません。

後半の章では、チームメンバー間でのコミュニケーションを円滑にし、より良い形で作業を進められるようにするための、考え方や施策を紹介しています。 新しいメンバーをチームの一員として迎え入れ、お互いの長所を活かし合って最大限のパフォーマンスを引き出すための最初の一歩として、これらの事が役に立つのではないでしょうか。

記事の再利用について

この特集記事を公開するにあたって、本文と画像はすべてクリエイティブ・コモンズのライセンスを設定しました。 ライセンス種別はCC-BY-NC-SAです。

クリエイティブ・コモンズは作品の使い方について許可する利用形態をあらかじめ明示するライセンスで、CC-BY-NC-SAは以下を再利用の条件として定めています。

  • BY:作者名を明記すること。例えば、作者名を勝手に書き換えて自分1人の著作であると言ったり、著者不明の公共物と言ったりして再配布することはできません。
  • NC:実費も含め、対価を得ないこと。例えば、紙に印刷した物を配布するときにコピー代を受け取ったり、有料の電子書籍として販売したりすることはできません。必ず無料で配布する必要があります。
  • SA:同一の条件で配布すること。例えば、内容を改良した物を配布する時に「この改良版に対する変更は禁止します」のように新しい条件を付け加える事はできません。内容を変更した版も、元の版と同じ条件で再配布する必要があります。

言い換えると、これらの条件に則った使い方であれば、完全に無許諾での再利用が可能です。 例えば以下のような利用方法が考えられます。

  • 社内での研修資料として無料で配布する。
  • 有料のセミナーで資料として使う。(対価を得てはならないのは資料自体の配布についてなので、セミナー自体の参加費用を求める事は問題ありません。一方で、セミナー内で資料を印刷した物が必要な人には追加で100円を支払ってもらう、というような形だと、資料の配布自体に対価が発生している事になるため不許可となります。)
  • ツールの移り変わりなど、最新の状況に合わせて内容を更新した物を公開する。
  • 「これ以外にももっと大事な事がある!」と思う内容を新しい章として追加して公開する。
  • 「ここ、凄く良い事言ってる!」と思う部分を抽出して、発展させた新しい記事を書き、公開する。(※その場合、「SA」の条件があるため、発展版の記事も同一の条件で再配布する必要があります。)

また、著作権法上の「私的利用」や「引用」の要件を満たす使い方であれば、いわゆるフェアユースにあたるため、上記の諸条件は適用されません。例えば以下のようなケースが該当します。

  • 手元に置いておくために、印刷して保存しておく。あるいは、ページをダウンロードして自宅のファイル共有サーバに保存しておく。
  • 自分で書いた記事の中での意見の補強材料として、あるいは反対意見の紹介として、この記事の一部を引用する。また、その記事を有料の電子書籍として販売する。

なお、これらはあくまでこちらへご連絡をいただかずに再利用される場合の条件です。 例えば有料の電子書籍としての販売などをお考えの場合は個別の対応が可能ですので、お問い合わせフォーム等にてご連絡下さい。

まとめ

WEB+DB PRESSの特集記事「1年目から身につけたい! チーム開発 6つの心得」が公開されました。 再利用しやすいライセンスを設定していますので、広くお使いいただいて、良い開発・良いコミュニケーションの促進に役立てていただければ幸いです。

2016-04-28

「チームメンバーのリーダブルコードを見つけて共有」を体験 - ピクシブさんでリーダブルコードワークショップを開催

2015年8月11日にピクシブさん会場提供でピクシブの開発者(7名)と永和システムマネジメントの開発者(1名)に参加していただき、「リーダブルコードワークショップ」を開催しました。

このワークショップは「チームメンバーのリーダブルコードを見つけて共有」することを体験する内容になっています。体験した内容が有益だと思ったらチームに取り入れていって欲しい、という趣旨です。

この内容は「メンバーのリーダブルコードを見つけて共有することが習慣になっているチームはリーダブルコードが当たり前になっていく」という仮説をベースとしています。図にすると次の通りです。

フィードバックループ

メンバーのリーダブルコードを見つけるためにはメンバーのコードを読まないといけません。書く側は「メンバーに読まれている」とわかっていればリーダブルコードを書こうという気持ちが強くなり、リーダブルコードが増えていきます。リーダブルコードが増えると見つかるリーダブルコードも増えていきます。

自分が見つけたリーダブルコードは、次に自分が書くコードに取り入れます。チームの誰かがリーダブルコードを書くことにより、リーダブルコードが徐々に他のメンバーにも共有されていきます。新しいリーダブルコードの書き方を見つけた・編み出したらそれを使ってコードを書く。リーダブルコードを書くことによりチームでリーダブルコードを共有できる。そんなフィードバックループです。

さらに進んだフィードバックループ

このフィードバックループが回り始めると「リーダブルコードが当たり前のチーム」になっていくのではないかということです。

5時間程度でこのフィードバックループを1周体験するのがこのワークショップです。今後、このワークショップをいろんなチームに提供していくべきかどうか、有用性を確認するためにピクシブさんと永和システムマネジメントさんに協力してもらいました。後述する通り、貴重なフィードバックをもらいました。ありがとうございます。

参加者視点でどうだったかについては、ピクシブさんがまとめてくれています。興味のある方はチームにとってのリーダブルコード - pixiv insideもあわせてご覧ください。

背景

どうしてこの内容のワークショップを提供しようと検討しているかについて、簡単に背景を説明します。

リーダブルコードの解説を書いてからは「リーダブルコード(=読む人が読みやすいコード)」というテーマでコードについて他の人に伝える機会が増えました。

クリアコードは社名の通り「クリアなコード(=書いた人の意図が明確なコード)」を大事にしています。「クリアなコード」と「リーダブルコード」はどちらも大事にしていることは同じです。読んで理解できるコードです。

「クリアなコード」を大事にするというのは、元々は「自分たちが書くコードはクリアなコードにしよう!」という気持ちでした。しかし、「リーダブルコード」というテーマでコードについて他の人に伝える機会が増えるうちに「他の人たちがクリアなコード・リーダブルコードを書くことも手伝いたい!」という気持ちになってきました。

開発は実際に手を動かす作業がたくさんあります。説明を聞いて頭で理解するだけよりも、実際に体験して体でも感じられた方が、より多くのことを伝えることができそうです。そのため、ワークショップの提供を検討しています。

対象者

ワークショップ開催前にピクシブさんからヒアリングをしました。こちらが想定している内容が参加予定の開発者にとって有用そうかを検討するためです。

ヒアリングの結果、想定していた内容を変えて、もっと突っ込んだ内容にした方がよいことがわかりました。想定よりも対象者のレベルが高かったのです。(ヒアリングしてよかったです。)

最終的にワークショップの対象者は次のような開発者にしました。

  • 世の中で言われているベストプラクティスが自分にあったものかどうかを取捨選択できる人
  • 書籍「リーダブルコード」を読んで日々リーダブルコード(=読む人が読みやすいコード)を書いている人、または、書籍を読んでいないが日々リーダブルコードを書いている人。(本人が「リーダブルコードを書いている」と考えていれば書いているとする。)
  • 自分だけでなくチーム全体でもリーダブルコードを書きたい人

ざっくりとまとめると、自分はリーダブルコードを書ける自立した開発者で、チーム全体でも同様のことを実現したい人です。

内容

ワークショップは次のような内容にしました。

  • 概要説明
  • 個人で開発を体験
  • ペアで開発を体験
  • チームで開発を体験

体験が3つにわかれているのは、1人→2人→3人以上というように段階的に体験するためです。

「個人で開発を体験」するフェーズでは「後で他のメンバーがリーダブルコードを探すために読む」という前提で開発します。それを意識して「リーダブルコード」を書きます。これは、フィードバックループでいう「読まれるしリーダブルコードを書こう!」という部分の体験になります。

読まれることが前提でリーダブルコードを書こう!

「ペアで開発を体験」するフェーズでは「個人で開発を体験」するフェーズで開発したコードの中からリーダブルコードを見つけて共有します。これは、フィードバックループでいう「リーダブルコードはないかな?」と「お、リーダブルコードだなぁ。自分が書く時に取り入れよう!」の部分の体験になります。

リーダブルコードはないかな?お、リーダブルコードだなぁ。自分が書く時に取り入れよう!

「チームで開発を体験」するフェーズでは参加者全員(今回は8人)のコードの中からリーダブルなコードを見つけて共有します。これは、これまで個人(1人)・ペア(2人)でやったことをチーム(3人以上)にスケールさせる体験です。

チームでフィードバックループ

これらの内容を通じてフィードバックループを1周体験し、「メンバーのリーダブルコードを見つけて共有」が自分たちのチームにとって割にあうか(コストよりメリットが大きいか)を判断してもらおうという狙いです。

なお、内容の詳細はGitHub上で公開しており、CC BY-SA 4.0のライセンスで自由に利用できます。

フィードバック

チームにとってのリーダブルコード - pixiv insideの「感想」のところにもある通り、参加者のみなさんから貴重なフィードバックをもらいました。ありがとうございます。

開催側からの視点として整理します。

普段はレビューを通して、良くない部分や直すべき部分を指摘する・される事が多いのですが、今回の*「良い部分を共有する」という体験は新しく*、それを意識づけるきっかけができて良かったです。

この感想をもらえたことは、このワークショップの内容が開催側の狙いを実現するための内容として適切だったということと捉えました。

最近は、コードを読むというと「コードレビュー」としてコードを読むという場面を思い浮かべることが多そうです。Webの記事や雑誌の記事でコードレビューについて書かれていることが多いからです。コードレビューは「コードに問題がないことを確認する」ことが1番の目的です。コードを共有する側面もありますが、それは副次的な目的です。

一方、このワークショップでの読み方の1番の目的は「リーダブルコードを見つけて共有する」ことです。問題が見つかることもありますが、それは副次的な効果です。

目的が違えば読み方も変わります。

「問題がないことを探す」ときは漏れがないか、特殊なケースでも大丈夫か、などといった観点で読むことでしょう。言い方はアレですが、少しイジワルな視点で読むということです。

「リーダブルコードを探す」ときは「自分がすぐに理解できたか」を意識して読まないといけません。意識しないと見つけられないからです。(意識しなくてもすぐに理解できるようなコードがリーダブルコードです。)「いいこと探し」視点で読むということです。

この違いを体験できたという感想をもらえたので、ワークショップの内容として方向性は間違っていなそうです。

レビューする側もされる側も人間なので、指摘を通してある種のすまない気持ちや否定された気持ちを持ってしまう事があるのですが、良い部分を共有するという行為を習慣化する事で、コードのためだけではなくチームメンバーの感情のためにも良い影響がありそう

今回のワークショップは技術的な理由だけから「チームメンバーのリーダブルコードを見つけて共有」を体験する内容にしたので、感情的な側面については考えていませんでした。たしかに感情的な側面でもよい効果がありそうなので、新しい視点を得られた貴重なフィードバックでした。

なにかしら仕組みを作ることで感情的な側面の効果をより活かすことができそうです。GitHubベースで考えるなら、GitHubではコミットやコードに対してコメントをつけることができるので、よい部分を見つけたらコメントしておき、別途用意した「よい部分を見つけた!」コメントを収集して一覧表示するツールを使ってチームで共有するという仕組みを思いつきました。

他にも、すでにコードレビューでうまく回っているチームならこのワークショップは必要ないかもという知見も得られました。

まとめ

クリアコードは「チームメンバーのリーダブルコードを見つけて共有」を体験する「リーダブルコードワークショップ」の提供を検討しています。

ピクシブさんと永和システムマネジメントさん協力のもとこのワークショップの有用性を検証してみました。それぞれの開発者が自立してリーダブルコードを書いているが、チーム全体の取り組みにはまだ届いていないというチームには有用そうです。

「チームメンバーのリーダブルコードを見つけて共有」するという取り組みは技術面だけでなく、チームの感情的な側面で有効かもしれないという仮説が得られました。

ピクシブさん・永和システムマネジメントさんご協力ありがとうございました。

自分たちのチームでも「チームメンバーのリーダブルコードを見つけて共有」を体験して、チームでリーダブルコードのフィードバックループを回すか検討する材料にしたい、という方はお問い合わせください。1回10万円(最大8名)で「リーダブルコードワークショップ」を提供します。

チームでフィードバックループ

2015-08-13

SEゼミ2015 - OSS Hack 4 Beginnersを開催 #sezemi

2015-06-27にOSS開発参加未経験学生向けOSS開発イベントであるOSS Hack 4 Beginnersを開催しました。このイベントについて、内容を作った立場からどうしてこのような内容にしたのかについて紹介します。また、今回の内容の課題と今後の解決案についてもまとめます。

目標

このイベントの目標は「OSS開発参加への不安を取りのぞくこと」にしました。参加者から不安が取りのぞかれていれば目標達成ということです。

この目標にした理由は、学生から「OSSの開発に参加したい気持ちはあるけど、漠然とした不安があって行動に移せていない」という声を聞いたからです。実際、イベントに参加した学生も同様の理由でOSSの開発の参加に至っていないということでした。

OSSの開発に参加することが当たり前の人視点では「とりあえず手を動かせばいいのに」と考えてしまいます。そのため、OSSの開発の参加に至っていない理由が前述の理由だということには思いもよりませんでした。勉強になりました。

OSSの開発に限らず、「漠然とした不安」で行動に移せていないケースは他にもあるのかもしれません。

内容の方針

「漠然とした不安」を取りのぞくため、次の方針の内容にしました。

  • 選択肢を減らす
  • 安全な環境を用意する
  • 実際に経験する

選択肢を減らす、特に、選択肢をオススメのもの1つにすることにより、「間違った選択肢を選んでしまう」という不安を排除し、正しい方向へ進んでいる感を与えます。選択肢がなくなった部分に関して自分で考えることが減ってしまうというデメリットがありますが、それは不安が解消し、余裕をもって行動できるようになってからでよいと割りきります。

なお、選択肢を減らすという方針はpixivこしばさんからのリーダブルコード勉強会へのフィードバックが元になっています。ありがとうございます。

安全な環境を用意するのは「うまくいかなくても大丈夫感」を与えて不安を排除するためです。選択肢を減らすのは「うまくいっている感」を与えて不安を排除するためでしたが、逆方向からも不安を排除するということです。

上述のように最初の1歩の敷居をさげ、実際に経験します。実際に経験することで、よくわからない「OSSの開発に参加」というものが具体的なものになります。具体的なものになれば、「漠然とした不安」は「心配することではなかった」と「具体的な困っていること」になります。「具体的な困っていること」は個別に具体的な対策を立てて解消できます。

内容

この方針を元に次の内容にしました。

  1. オススメの手順を「1つだけ」伝える
  2. 手順をやっているところを見せる
  3. 実際に手順をやってみる
    • 手順をやるときはそばでメンターがサポートをする
    • 手順をやるときは失敗しても大丈夫なOSSに対してやる

「1.」と「2.」は「選択肢を減らす」方針の実装です。1.で手順を伝え、2.で見本を見せ、「これを真似すればよい」ようにします。

「3.」の箇条書き2つは「安全な環境を用意する」方針の実装です。メンターがそばでサポートするので、自分ではうまく進められていなくても軌道修正できます。失敗しても大丈夫なOSSの開発に参加するので、メンターのサポートをすり抜けて失敗してしまっても大丈夫です。なお、失敗しても大丈夫なOSSとは「メンターが開発に関わっているOSS」です。開発者がその場にいるので失敗しても大丈夫だよ、というわけです。

「3.」は「実際に経験する」方針の実装です。↑の不安を排除する施策を実施した上で実行します。

OSSの開発に参加するオススメの手順

実際に紹介したOSSの開発に参加するオススメの手順を説明します。

それは「まず動かす」ことです。開発者としてではなくユーザーとして動かします。

開発するぞ!という意気込みで着手しようとするといきなり開発者として関わりたくなるものですが、そうではなく、まずはユーザーとして関わることがオススメです。

理由は、まずユーザーとして関わることで次のことがわかるからです。(逆に言うと、次のことをわかるためにユーザーとして動かします。)

  • 何をするソフトウェアなのか
  • どうやって使うか

これがわかったかどうかは「他の人に教える」ことで確認できます。他の人に何をするソフトウェアか、どうやって使うかを教えて、その人がそのソフトウェアのことをわかって使えるようになったら自分がわかっているということです。

さて、これらのことがわかるためには次のことをする必要があります。

  1. 「このOSSについて」のようなドキュメントを読む
  2. そのOSSをインストールする
  3. インストールしたOSSを動かしながらチュートリアルを実行する

多くのOSSでは、これらをする中でなにかしらつまづきます。

今までOSSの開発に参加していなかった人は、つまづいたら次のような行動をとります。

  • グチる
  • 使うことをやめる
  • Webを検索して解決策を見つけて解決し、先に進む

OSSの開発に参加するオススメの手順では違う行動をとります。つまづいたところをメモに残します。(今回のイベントではGitHubのissueを使いました。つまづいたところ1つに対して1つのissueです。)

メモに残すことは「直す」あるいは「報告する」ためです。「直す」も「報告する」もOSSの開発に参加していることになります。つまり、自分がつまづいたところを「OSSの開発に参加するチャンス!」だと捉えて行動するということです。これまで「OSSの開発に参加するきっかけなんてないよ…」と思っていた人も、見方を変えるだけで実はきっかけはたくさんあったことに気づきます。

グチったり、使うことをやめたり、回避策を見つけて先に進む代わりに、「次の人が同じようにつまづかないように」します。それが「直す」あるいは「報告する」です。

この時点では「直す・報告する」対象はコードではなくドキュメントのことが多いでしょう。それでいいのです。ドキュメントの改良も大事なOSSの開発です。ドキュメントの書き方でつまづかなくなる人がいるのですから。自分のちょっとした行動で、次の人がつまづかなくなるってステキなことじゃないですか。

一通り動かして、このOSSのことがわかったら、溜まっていたメモを元にupstream(開発元)にフィードバックします。

フィードバック方法については、次のような例を示し、個別のケースについてはメンターのみなさんにお任せしました。(もっと具体的な手順を示した方がよかったかもしれない。)

  • フィードバック時には次の情報を含める(「バグレポートに必要な情報」が指標)
    • 何をしたか
    • 期待した結果はなにか
    • 実際の結果はどうだったか
  • ↑の情報をまとめるときは次の手順でまとめる
    1. まずは自分がわかるようにまとめる
    2. 自分がわかるようにまとめたものを、開発者に伝わるようにまとめる(開発者にとってリーダブルなようにまとめる)

なお、オススメの手順を説明した資料は次の資料です。

結果

ほとんどの参加者が漠然とした不安を払拭できたようです。(イベントの最後に全員に挙手してもらって確認しました。)

この内容は今回のイベントのために考え、初めて実行したものです。今回の結果だけを考慮しただけですが、「漠然とした不安」を解消するやり方として、「オススメの手順を1つだけ示し」、「安全だと感じれる環境」で、「実際に経験する」というのは有効なやり方と言えそうです。

機会があれば、同様のアプローチで「OSSの開発に参加する人を増やす」ことを試してみたいです。(OSSの開発に参加する人を増やしたい方はご相談ください。)

よかったこと

今回のイベントでよかったことおよび次も続けたいことをまとめます。

  • メンターが楽しそうだったこと
    • 楽しいポイントがどこだったのかはわかりませんが、楽しかったのは非常によいことです。(参加者にも「楽しんでOSSの開発に参加して欲しい」と伝えました。)
  • メンターが参加者の隣に座れるイスがあったこと
    • イスに座ってサポートするとより深くサポートできるのではないかという気がしました。
    • pixivさんが提供してくれた会場には脇にたくさんイスがあり、すぐにイスを追加できて便利でした。

課題と解決案

今回の内容は、優秀なメンターがいたから成り立っていました。そのため、同様の内容を再現することが難しいのが課題です。

また、失敗しても大丈夫なOSS(メンターが開発者なOSS)は必ずしも題材として適切ではありません。たとえば、難易度が難しいとか必要な前提知識が多すぎると適切ではありません。適切な題材となりえるOSSを用意できるかどうかということも課題です。

解決案は…今のところないです。どうするといいでしょうねぇ。

まとめ

今回初めて開催したOSS Hack 4 Beginnersについて内容を考えた立場から紹介しました。OSS Hack 4 Beginnersは続くOSS Hack Weekendのためのイベントでした。OSS Hack Weekendでのびのびと楽しくOSSの開発に参加するために漠然とした不安を解消したのです。

なお、OSS Hack WeedendはOSS Hack 4 Beginnersに参加していなくても参加できます。引き続き優秀なメンターが多数参加するので、OSSの開発に参加したそうな学生さんに教えてあげてください。OSS Hack Weekendでは学生が希望するOSSの開発に参加します。OSS Hack 4 Beginnersはイベント開催側が対象OSSを選びましたが、OSS Hack Weekendでは野生のOSSの開発に参加します。「このOSSの開発に参加してみなよ」という案を持っている方はそれも一緒に学生に教えてあげてください。

  • OSS Hack Weekend:2015-07-11と07-12の2日間開催(07-06申し込み締め切り)

今回の勉強会に参加したメンターの感想に興味のある方は次のWebページを見てみてください。

2015-07-02

SharetaryのためのGitHubアクティビティ定点観測の設定・改

先日、「GitHub上のアクティビティを定点観測してみませんか?」と題してfluent-plugin-github-activitiesSharetaryという2つのソフトウェアを紹介しましたが、Sharetaryの改善に伴っていくつか設定の仕方に変化がありましたので、改めて、最新バージョンでの推奨設定について解説します。 ClearCode Inc. Organizationに所属しているアカウントの公開アクティビティを収集するSharetaryの運用サンプルも、この解説と同じ手順でSharetary 0.5.0に移行しました。

なお、この記事はSharetaryの利用のための技術情報が主ですが、Fluentdのストリームにおける1つの入力を元にして、fluent-plugin-groongaで複数テーブルにレコードを追加する方法の解説にもなっています。 fluent-plugin-groongaを使ったGroongaサーバーのレプリケーションに対して、in_groongaを使わずに直接レコードを登録するやり方に興味がある方も、参考にしてみて下さい。

Sharetary 0.4.0(0.5.0)での変更点

バージョン0.4.0以降のバージョンには様々な変更点がありますが、最大の変更点はスキーマ定義の変更です。

バージョン0.3.0までではほとんどの情報がEventsテーブルに集中していましたが、これにはデータの重複が増えてしまうという欠点がありました。 また、Eventsテーブルのカラム数が増えれば増えるほど管理が煩雑になってしまうという問題もあります。 そこでバージョン0.4.0からは、イベントに直接関係ない情報は別のテーブルに分けて管理するように改めました。

また、それに伴ってイベントに複数のタグを紐付けられるようにし、イベントの属するscope(GitHubのアクティビティに対応するイベントであれば、リポジトリの情報など)はタグの1つとして管理するようにしました。 これにより、「このイベントはfooというリポジトリに関連付けられており、barというハッカソンの期間中に行われた」というような複数の切り口での分類が可能になりました。

(図:Events, Actors, Tagsの各テーブル同士の関係)

このスキーマ変更によって、これまでにクロール済みのイベント情報はそのままでは使えなくなっています。 後述の手順に則って、データを移行する必要があります。

Sharetary 0.3.0からのアップグレード手順

既に設置済みのSharetaryのバージョンを0.3.0から0.4.0以上に更新する手順は以下の通りです。

  1. SharetaryのHTTPサーバ、クローラを停止する。
  2. スキーマ定義変更およびデータ移行用スクリプトをダウンロードする。
  3. ダウンロードしたmigrate-0.3.0-to-0.4.0を、Bashスクリプトとして実行し、データを移行する。
  4. クローラの設定を、新しいスキーマに合わせて更新する。
  5. クローラの動作を再開させる。
  6. sudo npm install -g sharetaryで最新バージョンのSharetaryをインストールする。
  7. SharetaryのHTTPサーバを起動する。

データ移行用のスクリプトは、以下のオプションを指定できます。

  • -h <hostname> : Groongaサーバ(またはDroongaクラスタ)のホスト名。省略時はlocalhost
  • -p <port> : Groongaサーバ(またはDroongaクラスタ)のHTTPサーバがlistenしているポート番号。省略時は10041
  • -t <追加で設定するタグ> : 移行された全てのイベントに固定で付与するタグ。
  • -d : 指定するとdry-runモードとなり、実際にはデータベースに変更を行わないで、どのような変更が行われるのかをシミュレートする。

例えばGroongaサーバがgroongaというホストの10041番ポートで動作していて、収集済みのイベントに「2015-sezemi-readable-code」というタグを付与して移行する場合、コマンドラインは以下のようになります。

$ wget https://raw.githubusercontent.com/clear-code/sharetary/master/bin/migrate-0.3.0-to-0.4.0
$ chmod +x ./migrate-0.3.0-to-0.4.0
$ ./migrate-0.3.0-to-0.4.0 -h groonga -t 2015-sezemi-readable-code -d

意図しないデータ破壊を避けるためは、まず1回dry-runして、適用される変更を先に把握しておくとよいでしょう。

追加するタグの指定は必須ではありませんが、指定しておくのがお薦めです。 過去の運用で何かのハッカソンに関係するイベントを収集済みなのであれば、そのハッカソンの名前をタグとして追加しておくと、それ以後収集するイベントを古いイベントと区別しやすくなります。

クローラの設定をどのように更新するかは、どんなクローラを使っているかによって変わります。 以下は、先日のエントリで紹介したfluent-plugin-github-activitiesの場合の設定(Fluentdの設定)の解説となります。 設定の参考にしてみて下さい。

Sharetary 0.4.0以降のバージョン向けの、fluent-plugin-github-activitiesの設定

先のエントリでも解説していますが、fluent-plugin-github-activitiesは本質的にはSharetaryとは全く独立した存在です。 fluent-plugin-github-activitiesはGitHubのアクティビティをクロールして取得してきてFluentdのストリームに取り込むだけの物で、それを加工してSharetary用のイベント情報としてデータベースに格納するのは、他のプラグインの仕事という事になります。

ここでは、Sharetaryのリポジトリに含まれている設定例の内容を引き合いに出しながら、fluent-plugin-github-activitiesの出力をどのように加工すればSharetary 0.4.0以降のイベント情報として使えるのかを解説します。

レコードの取得

まず最初にfluent-plugin-github-activities用の<source>の設定ですが、ここは先のエントリで紹介した通りで変化はありません。

<source>
  type github-activities
  access_token access-token-of-github
  users ashie,cosmo0920,kenhys,kou,min-shin,myokoym,okkez,piroor
  clients 4
  interval 1
  base_tag github-activity.
  pos_file /var/log/td-agent/github-activities.json
</source>

これによってレコードとして取り込まれたアクティビティ情報を加工する指定が、この後に続く事になります。

レコードを「イベント情報用」「人物用」「タグ用」の3つに複製し、ストリームを分岐する

FluentdでSharetary用のデータベースにレコードを格納するにはfluent-plugin-groongaを使いますが、fluent-plugin-groonga、特にその中でFluentdのストリームからGroongaサーバへの書き込みを行うout_groongaは、流入してきたレコードを特定のテーブル1つのレコードとして格納するという物です。

しかしSharetary 0.4.0以降用には、1つのイベントにつき、イベント情報そのものを格納するEventsテーブル、人物のメタ情報を格納するActorsテーブル、タグのメタ情報を格納するTagsテーブルという具合に、最大で3つのテーブルに同時にレコードを追加する必要があります。

(図:Events, Actors, Tagsの各テーブル同士の関係)

そこで、ストリームを「イベント用」「人物用」「タグ用」の3つに分岐して、それぞれ別々の設定のfluent-plugin-groongaによってレコードとして各テーブルに格納することになります。

(図:Events, Actors, Tagsの各テーブル用にストリームを分岐する様子)

このようなストリームの分岐には、Fluentdに標準添付されているcopyというプラグインを使います。

<match github-activity.**>
  type copy

  # Eventsテーブル用のレコード
  <store>
    type        record_reformer
    enable_ruby false
    tag         event.${tag}
  </store>

  # Actorsテーブル用のレコード
  <store>
    type        record_reformer
    enable_ruby false
    tag         actor.${tag}
  </store>

  # Tagsテーブル用のレコード
  <store>
    type        record_reformer
    enable_ruby false
    tag         repository-tag.${tag}
  </store>
</match>

copyプラグインは、<store>に書かれた別のプラグイン用の指定に基づいて加工を行ったレコードを、Fluentdのストリームに出力します。 この例では、レコードを加工するプラグインであるfluent-plugin-record-reformerを使って、それぞれのタグ名を「何に使うためのレコードか?」という事を示す物に変えてから出力しています。 3つの<store>が書かれているため、これでストリームは3つに分かれることになります。

人物用のレコードを作成し、Actorsテーブルに格納する

話を単純にするために、外部テーブルの方から解説していきます。 まずはActorsテーブルです。

このテーブルのレコードは_keyが人物の名前で、その人物のプロフィールページのURIとなるuri、アイコン画像のicon、人物の重要度を示すclassの3つのフィールドを持ちます。

copyプラグインによって分岐された3つのストリームのうち、Actorsテーブル用に出力しているレコードについて、fluent-plugin-record-reformerの変換ルールを以下のように更新し、上記のフィールドを持ったレコードを出力するようにします。

<match github-activity.**>
  type copy
  ...
  # Actorsテーブル用のレコード
  <store>
    type         record_reformer
    enable_ruby  true
    renew_record true
    tag          sharetary.actor
    <record>
      _key  ${(actor || committer)["login"]}
      uri   https://github.com/${(actor || committer)["login"]}/
      icon  ${self["$github-activities-related-avatar"]}
      class major
    </record>
  </store>
  ...
</match>

この時の設定のポイントは以下の通りです。

  • Acotrsテーブル用のレコードはゼロから組み立てるので、renew_record trueとして、加工元レコードのフィールドを引き継がないようにしています。
  • enable_ruby trueは、セキュリティや速度などの点でデメリットがありますが、今回の用途では必要な設定です。 というのも、fluent-plugin-record-reformerは、プレースホルダーを使って加工元のレコードのフィールドの値を参照できますが、fluent-plugin-github-activitiesが出力するレコード(GitHubのAPIが返すJSONそのままの形式)のようにそれぞれのフィールドが階層化されている場合、欲しい情報を取り出す事ができません。 ですので、必要な情報を参照できるようにするために、この設定を有効化しています。
  • Actorsテーブル用のレコードにはアクティビティの種類を示す情報は必要なくなるので、タグはsharetary.actorに統一しています。
  • 出力するレコードのフィールドのうち_keyuriは、どちらも元のレコードに含まれているユーザアカウント名を使いたいのですが、個々のコミットに対応するレコードと、それ以外のアクティビティのレコードとでは、持っている情報の構成が若干異なるため、参照先を決め打ちできません。 ですので、ここでは||を使って、レコードに存在する方のフィールドを取り出しています。
  • アイコン画像のURIを参照するために、fluent-plugin-github-activitiesが独自に出力している$github-activities-related-avatarというフィールドを参照していますが、値を参照するために${$github-activities-related-avatar}と書くとエラーになってしまいます。これは、フィールド名の先頭の$が特別な意味を持ってしまうためです。ここでは、各フィールドの値を保持している名前空間オブジェクトのOpenStruct自身の[]メソッドを明示的に呼び出す事で、フィールドの値を参照しています。
  • classフィールドの値は、簡単のためmajorに決め打ちしています。元レコードの情報に基づいてclassの値を適宜切り替えると、「受講者」と「メンター」のように、人物によってイベントの表示の仕方に強弱を付ける事もできます。

次は、できあがったレコードをfluent-plugin-groongaを使ってActorsテーブルに格納するための設定です。

<match sharetary.actor>
  type groonga
  store_table Actors

  protocol http
  host localhost

  buffer_type file
  buffer_path /var/spool/td-agent/buffer/groonga-sharetary-actor
  flush_interval 1s

  <table>
    name Actors
    flags TABLE_HASH_KEY
    key_type ShortText
  </table>

  <mapping>
    name uri
    type ShortText
  </mapping>

  <mapping>
    name icon
    type ShortText
  </mapping>

  <mapping>
    name class
    type ShortText
  </mapping>
</match>
  • このテーブルの情報はSharetaryでは直接検索する対象にはしないので、インデックスは作っていません。
  • buffer_pathに指定するパスは、この設定のための固有のパスにする必要があります。 他のテーブル用のfluent-plugin-groongaの設定との間でこの設定の値が衝突すると、どちらか片方の設定しか有効化されませんので、注意して下さい。

これで、人物の情報がアイコン画像のURIなどのメタ情報を伴って保存されるようになります。

タグ用のレコードを作成し、Tagsテーブルに格納する

人物の次は、イベントのタグ付けのためのタグそのものを格納するTagsテーブルです。

fluent-plugin-github-activitiesで取得したアクティビティから取り出せる情報を使って適切なタグを自動設定すれば、収集したイベントを効率よく分類できるようになります。 最もベタな例は、「そのアクティビティに関連しているリポジトリ」という切り口でのタグ付けでしょう。

このテーブルのレコードは_keyがタグの名前で、タグ固有のアイコン画像を示すiconというフィールドを持ちます。 copyプラグインによって分岐された3つのストリームのうち、Tagsテーブル用に出力しているレコードについて、fluent-plugin-record-reformerの変換ルールを以下のように更新し、上記のフィールドを持った「そのアクティビティに関連しているリポジトリ」のタグにあたるレコードを出力するようにします。

<match github-activity.**>
  type copy
  ...
  # Tagsテーブル用のレコード
  <store>
    type         record_reformer
    enable_ruby  true
    renew_record true
    tag          sharetary.tag
    <record>
      _key  ${tag.end_with?(".commit") ? url.match(/repos\/([^\/]+\/[^\/]+)\/commits/)[1] : tag.end_with?(".fork") ? payload["forkee"]["full_name"] : repo["name"]}
      icon  ${self["$github-activities-related-organization-logo"]}
    </record>
  </store>
</match>
  • Acotrsテーブル用のレコードと同様に、Tags用のレコードもゼロから組み立てるので、renew_record trueとしています。
  • 先述の通り、複雑な構造のレコードから必要な情報を取り出す必要があるので、enable_ruby trueとしています。
  • Tagsテーブル用のレコードにもアクティビティの種類を示す情報は不要なので、タグはsharetary.tagに統一しています。
  • 出力するレコードのフィールドのうち_keyの値は、ほとんどのアクティビティではrepo["name"]で参照できますが、コミットに対応するレコードとForkに対応するレコードだけは違う値を使う必要があります。ここでは、3項演算子を使って、元レコードのどのフィールドの値を使うかを振り分けています。

次は、できあがったレコードをfluent-plugin-groongaを使ってTagsテーブルに格納するための設定です。

<match sharetary.tag>
  type groonga
  store_table Tags

  protocol http
  host localhost

  buffer_type file
  buffer_path /var/spool/td-agent/buffer/groonga-sharetary-tag
  flush_interval 1s

  <table>
    name Tags
    flags TABLE_HASH_KEY
    key_type ShortText
  </table>

  <mapping>
    name icon
    type ShortText
  </mapping>
</match>
  • このテーブルもSharetaryでは検索対象にしないので、インデックスは作っていません。
  • 先述の通り、buffer_pathに指定するパスはこの設定のための固有のパスにする必要があります。

これで、タグの情報も保存できるようになりました。

イベント用のレコードを作成し、Eventsテーブルに格納する

最後に、イベントそのものの情報を格納するEventsテーブルです。

GitHubのアクティビティは様々な種類があるため、Sharetary用のイベント情報に変換するためのルールはアクティビティの種類分だけ定義する必要があります。 全てのアクティビティについて完全な変換ルールを定義するのは煩雑なので、共通化できる所はなるべく共通化する方向で設定を組み立てていく事にします。

共通フィールドを埋める

まず最初に、多くのアクティビティに共通している情報を使って共通のフィールドを埋めます。

copyプラグインによって分岐された3つのストリームのうち、Eventsテーブル用に出力しているレコードについて、fluent-plugin-record-reformerの変換ルールを以下のように更新し、共通のフィールドを埋めた状態でレコードを出力するようにします。

<match github-activity.**>
  type copy

  # Eventsテーブル用のレコード
  <store>
    type        record_reformer
    enable_ruby true
    tag         event.${tag}
    <record>
      type        ${tag_suffix[-1]}
      actor       ${actor && actor["login"]}
      source_icon https://github.com/favicon.ico
      timestamp   ${created_at}
      created_at  ${time}
      _repository ${repo && repo["name"]}
    </record>
  </store>
  ...
</match>

設定のポイントは以下の通りです。

  • この後でアクティビティごとに個別の変換ルールを適用するにあたって、元のレコードの各フィールドをその時に参照したいので、今度はrenew_record trueは指定していません。 <record>で定義した各フィールドは、元のレコードへのフィールド追加、または同名フィールドの値の置き換えになります。
  • イベントの種類を示すtypeフィールドの値は、fluent-plugin-github-activitiesが出力したレコードがgithub-activity.pushのようなタグになっている事を利用して、タグの最後のパートをそのまま使います。 tag_suffix[-1]というプレースホルダを使うと、タグ名の最後のパートを簡単に参照できます。
  • イベントの主体となった人物を示すactorフィールドの値はActorsテーブルに格納したレコードの_keyと同じ値を設定する必要があります。 大多数のアクティビティではこれはactor["login"]の値ですが、個々のコミットに対応するレコードでだけは、違うフィールドを参照しなくてはなりません。 そのような特例については、個々のコミットに対応するレコードの変換ルールを定義するときにまとめて処理する事にして、ここではシンプルに「フィールドを参照できるならその値を使い、参照できなければnilにしておく」という書き方にしています。
  • イベントの由来となっているサービスを表すsource_iconの値は、GitHubのfaviconのURIで決め打ちにしています。 他のサービス由来のイベントも収集する場合は、適切な値をその都度指定しましょう。
  • イベントの日時を示すtimestampフィールドの値は、元レコードのcreated_atの値があればそれを使い、無ければnilにするという書き方にしています。 これも、例外となるアクティビティについては別途値を設定します。
  • イベント情報が記録された日時を示すcreated_atフィールドの値は、現在時刻の代替として、Fluentdのレコードに紐付けられている日時情報を参照しています。 (※元レコードのcreated_atと名前が同じですが、意味合いが異なる物で、こちらはあくまでSharetary用のEventsテーブルのカラム名です。)
  • イベントへのタグ付けのために、Tagsテーブルに格納したレコードの_keyと同じ値をtagsフィールドに設定する必要があります。 これもactor同様に、アクティビティの種類によって参照先のフィールドを変えなくてはなりません。 ただ、tagsフィールドの値は後から少し加工をしたいので、ここでは一旦_repositoryという仮のフィールドに値を設定するに留めています。 (後加工の段階で、これを使ってtagsフィールドの値を埋める事になります。)
アクティビティの種類ごとに固有のフィールドを埋める

次に、個々のアクティビティ用の変換ルールを定義します。

例えば、git pushに対応するイベントの変換ルールは、fluent-plugin-record-reformerを使って以下のように書けます。

<match event.github-activity.push>
  type         record_reformer
  enable_ruby  true
  renew_record true
  keep_keys    type,actor,source_icon,timestamp,created_at,_repository
  tag          sharetary.event
  <record>
    _key        https://github.com/${repo["name"]}/compare/${payload["before"]}...${payload["head"]}
    class       minor
    title       Push
    description Pushed ${payload["size"].to_s} commits to ${repo["name"]}
    uri         https://github.com/${repo["name"]}/compare/${payload["before"]}...${payload["head"]}
    reply_uri   https://github.com/${repo["name"]}/compare/${payload["before"]}...${payload["head"]}#commits_bucket
  </record>
</match>
  • この段階では共通のフィールドが埋まった状態のレコードが渡ってくるため、ここではpushに固有の情報を埋めるだけの変換ルールになっています。
  • Eventsテーブル用のレコードはfluent-plugin-github-activitiesが出力するレコードとは全く異なる形式になるため、renew_record trueとして、ゼロからレコードを組み立てるようにしています。 ただし、前の段階ですでに加工を終えた共通のフィールドについてだけは、keep_keys type,actor,source_icon,timestamp,created_at,_repositoryとして値を引き継いでいます。 このようにする事で、「前の段階で共通のフィールドを埋めておいて、この段階では固有のフィールドだけを埋める」という効率の良い設定が可能となります。
  • Eventsテーブル用のレコードとして形式を変換済みということで、タグはsharetary.eventに統一しています。
  • push以外にも、Issueの投稿、コメントの投稿など、多くのアクティビティの変換ルールは同様の要領で定義する事ができます。

別の例として、git commitに対応するイベントの変換ルールも示します。

<match event.github-activity.commit>
  type         record_reformer
  enable_ruby  true
  renew_record true
  keep_keys    type,source_icon,created_at
  tag          sharetary.event
  <record>
    _key        ${html_url}
    class       normal
    title       Commit ${stats["total"].to_s} changes
    description ${commit["message"]}
    extra_description ${files.collect{|file| "#{file["filename"]} (#{file["status"]})" }.join("\n, ")}\n\n${files.collect{|file| file["patch"] }.join("\n\n")}
    actor       ${committer["login"]}
    uri         ${html_url}
    reply_uri   ${html_url}#new_commit_comment_field
    timestamp   ${commit["committer"]["date"]}
    parent      ${self["$github-activities-related-event"] && "https://github.com/#{self["$github-activities-related-event"]["repo"]["name"]}/compare/#{self["$github-activities-related-event"]["payload"]["before"]}...#{self["$github-activities-related-event"]["payload"]["head"]}" || "" }
    _repository ${url.match(/repos\/([^\/]+\/[^\/]+)\/commits/)[1]}
  </record>
</match>
  • 基本的にはpushの場合と同じなのですが、keep_keysでそのまま流用している共通フィールドが、type,source_icon,created_atだけになっています。 これは、commitのイベントの元となるGitHub APIの戻り値が、ここまでで参照してきたアクティビティの共通フォーマットとは異なる形式を取っているからです。 actor, timestamp, _repositoryの各フィールドの値は、ここで改めて、commit専用のフォーマットになっているレコードから適切な値を参照するようにしています。

Sharetaryリポジトリにある完全版の設定ファイルのサンプルには、fluent-plugin-github-activitiesが対応している他のアクティビティも含めた全ての変換ルールが記述されています。 そちらも併せてご参照下さい。

データベース格納前の微調整を行う

アクティビティごとの変換を終えたら、今度はレコードの内容の微調整のための後処理を行います

まず、仮フィールドの_repositorytagsフィールドに変換して、イベントをタグ付けします。 これは、Fluentdに標準添付のrecord_transformerプラグインを使って、以下のような<filter>で実現できます。

<filter sharetary.event>
  type        record_transformer
  enable_ruby false
  remove_keys _repository
  <record>
    tags ["${_repository}"]
  </record>
</filter>

record_transformerの設定は、元になったfluent-plugin-record-reformerと同じような書式で記述します。

  • tagsフィールドの値を、_repositoryフィールドの値が単一の要素になっている配列として定義しています。
  • それと同時に、不要になった仮フィールドの_repositoryを、remove_keys _repositoryでレコードから削除しています。

これだけだと、この段階で変換を行う意味はあまり無いように見えます。 実際、共通フィールドを埋める段階や、各アクティビティ固有のフィールドを埋める段階でも、tagsフィールドを配列として定義することは可能です。

しかし、このようにtagsフィールドの変換を最後の段階に分けておくと、元の情報に含まれていなかった情報を使った、固定のタグの追加が可能になります。 例えば下のようにすれば、各イベントには関連するリポジトリ名のタグだけでなく、sezemi-2015-readable-codeというタグも固定で付与されるようになります。

<filter sharetary.event>
  type         record_transformer
  enable_ruby  false
  remove_keys  _repository
  <record>
    tags ["${_repository}", "sezemi-2015-readable-code"]
  </record>
</filter>

SEゼミの2015年度の取り組みでは4つのサブイベントを開催しますが、サブイベントの名前のタグをこのようにして自動付与すれば、後からサブイベントごとの情報を効率よく検索できるようになります。 また、1つのサブイベントが終わったら上記の後加工段階のフィルタだけを設定し直す事で、参加メンバーが同じだとしても、別のサブイベントの情報を簡単に識別できます。

(最初からtagsフィールドを配列型にしておいて、後処理の段階でtagsの配列に要素を追加する、という形にできればそれが最もスマートなのですが、残念ながら、そのような変換を行えるFluentdプラグインは現時点では無い模様です……そこで、次善の策としてこのようなやり方を考えてみた次第です。)

タグ付けが終わったら、最後の仕上げとして、日時を示すフィールドの値をunixtimeを表す整数値に変換します。 これは、fluent-plugin-filter_typecastプラグインを使って以下のように実現できます。

<filter sharetary.event>
  type  typecast
  types timestamp:time,created_at:time
</filter>

これによって、timestampcreated_atのフィールドの値が、2015-06-15T00:00:00のような文字列から、対応するUTCのunixtimeに変換されます。 Groongaでは日時はunixtimeの整数で表現されますので、この変換は必ず行わなくてはなりません。 (でないと、日付の範囲を指定した絞り込みができません。)

イベント情報をEventsテーブルに格納する

後処理を終えて全てのフィールドが揃ったら、後はEventsテーブルにレコードを格納するだけです。

<match sharetary.event>
  type groonga
  store_table Events

  protocol http
  host localhost

  buffer_type file
  buffer_path /var/spool/td-agent/buffer/groonga-sharetary-event
  flush_interval 1s

  <table>
    name Events
    flags TABLE_HASH_KEY
    key_type ShortText
  </table>

  <table>
    name Timestamps
    flags TABLE_PAT_KEY
    key_type Time
  </table>

  <table>
    name Tags
    flags TABLE_HASH_KEY
    key_type ShortText
  </table>

  <table>
    name Actors
    flags TABLE_HASH_KEY
    key_type ShortText
  </table>

  <table>
    name Terms
    flags TABLE_PAT_KEY
    key_type ShortText
    default_tokenizer TokenBigram
    normalizer NormalizerAuto
  </table>

  <mapping>
    name type
    type ShortText
  </mapping>

  <mapping>
    name class
    type ShortText
  </mapping>

  <mapping>
    name title
    type ShortText
    <index>
      table Terms
      name Events_title_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name description
    type Text
    <index>
      table Terms
      name Events_description_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name extra_description
    type Text
    <index>
      table Terms
      name Events_extra_description_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name tags
    type Tags
    <index>
      table Tags
      name Events_tags_index
    </index>
  </mapping>

  <mapping>
    name uri
    type ShortText
    <index>
      table Terms
      name Events_uri_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name source_icon
    type ShortText
  </mapping>

  <mapping>
    name reply_uri
    type ShortText
  </mapping>

  <mapping>
    name actor
    type Actors
    <index>
      table Actors
      name Events_actor_index
    </index>
  </mapping>

  <mapping>
    name timestamp
    type Time
    <index>
      table Timestamps
      name Events_timestamp_index
    </index>
  </mapping>

  <mapping>
    name created_at
    type Time
    <index>
      table Timestamps
      name Events_created_at_index
    </index>
  </mapping>

  <mapping>
    name parent
    type ShortText
    <index>
      table Terms
      name Events_parent_index
      flags WITH_POSITION
    </index>
  </mapping>
</match>
  • buffer_pathに指定するパスをこの設定のための固有のパスにしている点に注意して下さい。
  • 処理の進行状況によっては、人物やタグのレコードが登録される前にイベントのレコードが登録される事があり得ます。 そのような場合も想定して、ここには外部の参照先であるActorsTagsの両テーブルの定義も含めています。

まとめ

ここまでで設定したすべてのプラグインの関係を図にすると、以下のようになります。

(図:登場した全てのプラグインの関係)

以上、Sharetary 0.3.0をSharetary 0.4.0以降にバージョンアップする際の手順と、Sharetary 0.4.0以降向けにGitHubのアクティビティをクロールするためのFluentdの設定の仕方を解説しました。 その中で、Fluentdのストリームから1つの入力を分岐して複数テーブルにレコードを追加するfluent-plugin-groonga向けの設定の仕方も解説しました。

先日のリーダブルコード勉強会ではまだお試し運用でしたが、今月27日のOSS Hack 4 Beginners来月に予定されているOSS Hack Weekendでは、会場内で行われる開発の様子を横断して眺められることでしょう。 これらのイベントに参加を予定されている方は、会場内のサブスクリーンにもご注目ください。

また、クリアコード関係者の公開アクティビティを収集しているSharetary運用サンプルも引き続き運用しております。 クリアコードの普段の開発の様子も是非ご覧になってみて下さい。

つづき: 2016-01-06
2015-06-18

2015年6月27日(土)にOSS開発参加未経験学生向けOSS開発イベントを開催予定

要約:6月27日(土)にOSS開発参加未経験の学生向けに、OSSの開発に参加する方法をワークショップ形式で教えるイベントを開催します。「OSSの開発に参加してみたいけど漠然とした不安があり手を出せていない…」という学生向けに「具体的にこういう方法で始めるといいよ」という方法を伝えます。ワークショップ形式で実際に手を動かしながら学べるうえに、現役の超優秀エンジニアが多数メンターとしてサポートするので、OSS開発への漠然とした不安を払拭できるはずです。OSSの開発に参加してみたいけど手を出せていなかった学生の人はイベントページから応募してください。背中を押してあげたい学生を知っている人は教えてあげてください。締め切りは6月22日(月)です。

2015年のSEゼミの第3弾のイベントです。2015年のSEゼミは「OSS開発」をテーマにしています。第1弾のリーダブルコード勉強会(開催済み)ではコードを書いてOSSの開発に参加するときに必要となる(開発者にとって)読みやすいコードの書き方を学びました。第2弾のGitHub勉強会(6/20なので今週末開催)ではGitHubを使っているOSSの開発に参加する時に必要となるpull requestの仕方を学びます。

それらに続く第3弾のOSS Hack 4 BeginnersではOSSの開発に参加したことのない学生を対象に、OSSの開発への参加方法を伝えます。このイベントの目的は「OSSの開発に参加してみたいけど漠然とした不安があり手を出せていない…」という学生の「不安」を取りのぞくことです。

不安を取りのぞいた後に控える第4弾のイベントOSS Hack Weekend(7月11日(土)・12日(日)開催)ではのびのびと楽しくOSSの開発に参加しましょう!

内容

OSS Hack 4 Beginnersは学生の不安を取りのぞくために、次のことを大事にした内容になっています。

  • 「やり方」を伝える。
  • 「やり方」を自分でやってみる。
  • やるときは「失敗」しても大丈夫な状況でやる。

これは次の点が「不安」の原因になっていると予想したからです。

  • 「やり方」を知らないから「不安」なのではないか。
  • やったことがないから「不安」なのではないか。
  • 「失敗が怖い」から「不安」なのではないか。

これらの不安の原因を次の方法で解消する内容になっています。

  • オススメのOSS開発参加方法を紹介
  • オススメの方法を実践
  • 対象OSSはメンターが開発に関わっているOSS
  • 超優秀現役エンジニアがメンターとしてサポート

それぞれ説明します。

オススメのOSS開発参加方法を紹介

まず、最初にOSS開発への参加の仕方を説明します。参加の仕方は無数にありますが、「好きなようにやっていいんだよ」と言うと不安が増す原因になってしまいます。そこで、トレーナーがオススメの方法を1つだけ説明し、このイベントではその方法を使って「やってみます」。あえて1つに限定することにより迷いポイントが減り、不安を軽減できるのではないかという考えです。

具体的な方法は次の通りです。

  1. まず動かす
  2. 開発用にインストール
  3. テストを実行

「まず動かす」というのは非常に重要な手順です。重要なことなのに言及している情報を見かけません。一方、pull requestの方法などはよく見かけます。当たり前すぎて誰も言及しないだけなのかもしれませんが、OSSの開発に参加する時に最初にやる一番大事なことは「動かす」ことです。

動かすためにはインストール手順を確認する必要があります。ドキュメントがあればそれを読んで、その手順に従ってインストールし、動かします。なかったら…、開発に参加するチャンスです!

OSSの開発に関わっていない人は、「ドキュメントが足りなくてわかんねぇよ!」とグチって終わりになる場面でしょう。しかし、OSSの開発に参加しようと思っている人にはチャンスです。グチっている時間で直せばよいのです。そうすれば、次にインストールしようとした人は「ドキュメントにちゃんと書いてあってスムーズにインストールできた」と思うことでしょう。直接あなたに感謝の言葉が届くことは稀かもしれませんが、「世界を(ちょっとかもしれないけど)変える」(@_ko1さんからのこのイベント参加者へのメッセージより)ことをしたのです。少しかもしれませんが、その少しが積み重なって大きな変化になっていきます。そうやってOSSはよくなっていくのです。

(なお、このイベントではすぐに対応せずにメモに残しておいて後で対応する、という進め方をします。いきなり「動かす」と「直す」を一緒にやると大変なので、まずは「動かす」に集中します。未経験者向けのイベントなので敷居を下げています。)

「OSSの開発に参加」というと、「コードを読んでバリバリコードを書かないと!」というイメージがあるかもしれませんが、そんなことばかりではありません。コードを書く前の段階でも、ユーザーの目線でフィードバックできることはたくさんあるのです。OSSの開発に参加したことがない人はそれを知らずに敷居が高いと感じてしまっていることでしょう。このイベントを通じて、「そんなことはないんだ、開発に参加したいならできることはいろいろあるんだ」ということをわかってもらいたいです。

「動かす」ことができたら「開発用にインストール」します。「動かす」ときはパッケージを使ってインストールするといったように、ユーザーとしてインストールします。一方、「開発用にインストール」するときはソースコードを取得して、そこから使える状態にもっていきます。

一般的に、「開発用にインストール」するときの方が手間がかかります。しかし、ソースコードを変えて手元で変更を確認できる環境が手に入るというメリットがあります。開発に参加するときはこれは非常に大きなメリットです。

なお、「開発用にインストール」するときもドキュメントがあればそれを参考に作業します。なかったら…、自分がやった手順をメモして、まとめたものを開発者に報告しましょう。開発に参加するチャンスですね。

「開発用にインストール」できたら「テスト」を実行します。多くのOSSではテストが用意されていて、開発者が手元で正しい挙動を確認できるようになっています。テストが用意されていたら実行して、自分の手元でも正しい挙動になっているか確認しましょう。

以上がオススメのOSS開発参加方法です。

イベントではこの内容を説明した上で、実際にトレーナーが前でやってみせます。実際にやっている様子を見ることで理解が進むことを期待しています。

オススメの方法を実践

オススメのOSS開発参加方法を知った後は実際にオススメの方法を実践します。

実践する際はグループを作って、グループごとに進めます。1つのグループは多くても4人から6人です。同じグループでは同じOSSを開発対象にします。これは、グループ内で相談できるようにするためです。

OSSの開発では困ったことがあったら相談しながら進めることは当たり前です。多くの場合、時間的・距離的に離れていることが多いので相談するときはインターネット越しになります。ただし、いきなりインターネット越しに相談しながらやってもいいんだよ、といっても敷居が高いでしょうから、今回は目の前の人たちと相談しやすいように同じグループで同じOSSを開発対象にしました。相談することに慣れてきたらインターネット越しでも相談できるようになることを期待しています。

なお、イベントでは現役の超優秀エンジニアが多数メンターとしてサポートします。メンターはグループ内での相談を促したり、困っていることに対してヒントを与えてくれたりします。メンターとも協力してオススメのOSS開発参加方法を実践し、できるという感触を自分のものにしましょう。

対象OSSはメンターが開発に関わっているOSS

今回開発対象にするOSSはメンターが開発に関わっているOSSにします。当初は学生からの希望も考慮して決めようかと検討していたのですが、学生の「不安」を取り除くことを考えた結果、メンターが開発に関わっているOSSにすることにしました。

理由は次の通りです。

  • 「失敗」に対する「不安」を小さくできそう
    • たとえば新しくissueを作るときに、「こんなissueを立てて嫌がられないかな…」と「不安」に思ってしまったとしても、目の前にいるメンターから「それをやっても大丈夫だよ」と言ってもらえるなら行動に移せるのではないかと期待しています。
  • その場で反応をもらえそう
    • はじめてアクションを起こしたときは相手の反応がくるまでドキドキしてしまうものです。メンターはイベントに参加しているのですぐに反応してくれるはずです。反応待ちでドキドキして作業を進められないといったことを短くすることを期待しています。

まとめ

6月27日(土)に開催するOSS開発参加未経験の学生向けに、OSSの開発に参加する方法をワークショップ形式で教えるイベントを紹介しました。「OSSの開発に参加してみたいけど漠然とした不安があり手を出せていない…」という学生の人はイベントページから応募してください。背中を押してあげたい学生を知っている人は教えてあげてください。あるいは、現役の超優秀エンジニアメンターへのサポートを受けさせてあげたい学生を知っている人はこのイベントへの参加を勧めてください。

なお、締め切りは6月22日(月)です。

すでにOSSの開発に参加していて、現役の超優秀エンジニアメンターのやり方を参考にしたいという学生の人は7月11日(土)・12日(日)に開催するOSS Hack Weekendに応募してください。こちらは学生が希望したOSSを開発対象にしますし、メンターからいろいろ学べる時間がたっぷりあるので、充実した週末になるはずです。

2015-06-17

GitHub上のアクティビティを定点観測してみませんか?

先日、fluent-plugin-github-activitiesSharetaryという2つのソフトウェアをリリースしました。 これらを組み合わせると、GitHub上の個々人の活動をウォッチして簡単に共有することができます。

実際にどのように動作するのか、ClearCode Inc. Organizationに所属しているアカウントの公開アクティビティを収集するSharetaryの運用サンプルをご覧下さい。 (ちなみに、この運用サンプルは別のエントリで紹介したデモンストレーション用のDroongaクラスタをデータの格納先として利用しています。)

この仕組みは、SEゼミの2015年度の取り組みを支援するために開発しました(実は、先日のリーダブルコード勉強会でも会場の片隅で試験運用していました)。 今年のSEゼミではOSS Hack Weekendと題して、学生の皆さんに実際のOSSプロジェクトに参加してもらう予定ですが、Sharetaryの画面を会場内のスクリーンに出しておくことで、イベントの中で実際にどのような開発が行われているかを会場内で目に見える形で共有できますし、プルリクエストが採用されればその様子が一目で分かるというわけです。

GitHub上のアクティビティをクローリングするfluent-plugin-github-activities

fluent-plugin-github-activitiesは、GitHub上の公開アクティビティをクローリングすることに特化したFluentdプラグインです。 以下のような<source>を定義しておくと、設定で列挙したユーザの公開アクティビティを一定間隔で取得して、新規に取得したアクティビティの情報をFluentdのストリームに流すようになります。

<source>
  type github-activities
  access_token xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  users ashie,co-me,cosmo0920,hayamiz,hhatto,kenhys,kou,min-shin,monkey-mas,mooz,okkez,piroor,ridec,s-yata,t2y
  clients 4
  interval 1
  base_tag github-activity.
  pos_file /var/log/td-agent/github-activities.json
</source>

この時レコードとして流れるのは、Events APIの個々のEventです。 どんな種類のアクティビティがあってどんな情報を取得できるのかは、GitHubのAPIドキュメントを参照して下さい。

このプラグインの働きとしてはただそれだけの物なので、Sharetary以外にも様々な用途に流用できます。 例えば社内チャットにアクティビティを自動投稿するというのも、1つの使い方でしょう。

使用の際の注意点として、OAuthのアクセストークンを取得して設定ファイルに書いておく必要があるという点が挙げられます。 GitHub APIは手順に則って取得したアクセストークンを使わない場合、クロールの頻度が最大で60リクエスト毎時までに制限されてしまいます(アクセストークンがあれば5000リクエスト毎時までリクエストできます)。 fluent-plugin-github-activitiesはpushに対応するアクティビティを見付けたときはそれに含まれる各commitの詳細も個別に取得するようになっているため、アクセストークン無しの状態だとすぐにリクエスト過多と判断されてしまいます。

収集されたイベントをタイムライン&アーカイブ表示するSharetary

Sharetaryというのは「Share(共有)」と「Secretary(書記、秘書)」の組み合わせによる造語です。 バックエンドとしてはGroongaまたはDroongaのHTTPサーバを利用する想定になっており、所定の形式に則って定義された「イベント情報」のテーブルに対して検索を行って、新たに増えたイベントのレコードをTwitterなどのタイムライン風にリアルタイム表示することができます。

また、タイムライン表示とは別に、アーカイブ表示というモードもあります。 このモードでは、その時点までに取得済みのイベントのレコードをTogetterのように時系列順で表示することができます。 あるイベントに関連付けられたイベント群はスレッド風に表示されるので、タイムライン表示では追いにくい議論やコメントのやり取りなども、このビューであれば俯瞰して見る事ができます。

なお、Sharetaryで表示するための個々のイベントのレコードには「返信先URI」にあたるフィールドを持たせることができ、例えばGitHub上での「コミット」に対応するイベントに対して「コミットに対するコメントの入力フォーム」のURIを返信先として紐付けておけば、SharetaryのUI上で「このイベントに返信する」というような操作を行った際にそのコメント入力フォームへ遷移させることができます。 そうして追加されたコメントはfluent-plugin-github-activitiesによってクロールされ、Sharetaryのタイムライン上に表れることになります。 このように、Sharetaryはそれ自体に固有の情報をなるべく保持せずに、公開の情報だけをソースとして運用できる設計となっています。

fluent-plugin-github-activitiesとSharetaryの連携

Sharetary自体はデータをクロールする仕組みを持っていないため、fluent-plugin-github-activitiesのように何らかのクローラと併用することが前提になります。 冒頭でも紹介した実際の運用例は、以下の図のような構成になっています。

(droonga1の上に、サービスを提供しているSharetaryと、クローリングしているFluentdが存在する)

この図を見ると、Sharetaryとfluent-plugin-github-activities(Fluentd)は、共通のデータベースとしてDroongaクラスタにアクセスしてはいますが、互いに独立して動作している事が見て取れるでしょう。

fluent-plugin-github-activitiesは収集したアクティビティの情報をそのままレコードとして流すだけなので、それだけではSharetaryの情報ソースには使えません。 実際には、それらのアクティビティをSharetary用の適切なイベント形式のレコードに変換し、fluent-plugin-groongaを使ってSharetary用のデータベースにloadさせる必要があります。

Fluentdでのレコードの形式変換に使えるプラグインはいくつかありますが、元レコードの情報を使って全く新しいレコードを組み立てるという用途には、fluent-plugin-mapが適しているようです。 例えば、「Issueを新たに作成した」というアクティビティをfluent-plugin-github-activitiesがクロールしてきた際に、それを使ってSharetaryのイベントとなるレコードを組み立てる(GitHubのアクティビティをSharetaryのイベントに変換する)ルールは以下のように書けます。

<match github-activity.issue-open>
  type map
  tag "\"sharetary.\" + tag"
  time time
  record (require("time") || true) && { "_key" => record["payload"]["issue"]["html_url"], "type" => "issue-open", "source_icon" => "https://github.com/favicon.ico", "class" => "major", "scope" => record["repo"]["name"], "scope_icon" => record["$github-activities-related-organization-logo"], "title" => "Open", "description" => "Opened new issue: " + "#" + record["payload"]["issue"]["number"].to_s + " " + record["payload"]["issue"]["title"] + ": " + (record["payload"]["issue"]["body"] || ""), "actor" => record["actor"]["login"], "actor_uri" => "https://github.com/#{record["actor"]["login"]}/", "actor_icon" => record["$github-activities-related-avatar"], "actor_class" => "major", "uri" => record["payload"]["issue"]["html_url"], "reply_uri" => "#{record["payload"]["issue"]["html_url"].split("#").first}#new_comment_field", "created_at" => Time.now.to_i, "timestamp" => Time.parse(record["created_at"]).to_i, "parent" => nil }
</match>

fluent-plugin-mapでは変換ルールを文字列として定義しますが、Fluentdの設定ファイルでは文字列型の設定値を改行できないため、このように横長の記述になってしまっています。

基本的にはこの要領でSharetaryに表示させたいアクティビティ用の変換ルールを定義していくのですが、typeがタグと同じ内容だったり、各ルールでactor_classsource_iconが同じだったりする場合、この段階でフィールドを定義するよりも、後でまとめて設定した方がスッキリしますし、今後の変更も容易になります。 レコードのタグなどを使って追加のフィールドを定義するfluent-plugin-record-reformarプラグインを使うと、これらの共通フィールドは以下のようにして一気に設定できます。

<match github-activity.issue-open>
  type map
  tag "\"sharetary.\" + tag"
  time time
  # type, actor_class, source_iconはこの段階では埋めていない
  record (require("time") || true) && { "_key" => record["payload"]["issue"]["html_url"], "class" => "major", "scope" => record["repo"]["name"], "scope_icon" => record["$github-activities-related-organization-logo"], "title" => "Open", "description" => "Opened new issue: " + "#" + record["payload"]["issue"]["number"].to_s + " " + record["payload"]["issue"]["title"] + ": " + (record["payload"]["issue"]["body"] || ""), "actor" => record["actor"]["login"], "actor_uri" => "https://github.com/#{record["actor"]["login"]}/", "actor_icon" => record["$github-activities-related-avatar"], "uri" => record["payload"]["issue"]["html_url"], "reply_uri" => "#{record["payload"]["issue"]["html_url"].split("#").first}#new_comment_field", "created_at" => Time.now.to_i, "timestamp" => Time.parse(record["created_at"]).to_i, "parent" => nil }
</match>

<match sharetary.github-activity.**>
  type record_reformer
  enable_ruby false
  tag completed.${tag}
  <record>
    type ${tag_parts[2]}
    actor_class major
    source_icon https://github.com/favicon.ico
  </record>
</match>

こうして組み立てたレコードは、fluent-plugin-groongaを使ってGroongaまたはDroongaに格納します。 以下はfluent-plugin-groonga用の設定例です。

<match completed.sharetary.**>
  type groonga
  store_table Events

  protocol http
  host droonga0

  buffer_type file
  buffer_path /var/spool/td-agent/buffer/groonga
  flush_interval 1s

  <table>
    name Events
    flags TABLE_PAT_KEY
    key_type ShortText
  </table>

  <table>
    name Timestamps
    flags TABLE_PAT_KEY
    key_type Time
  </table>

  <table>
    name Terms
    flags TABLE_PAT_KEY
    key_type ShortText
    default_tokenizer TokenBigram
    normalizer NormalizerAuto
  </table>

  <mapping>
    name type
    type ShortText
  </mapping>

  <mapping>
    name class
    type ShortText
  </mapping>

  <mapping>
    name title
    type ShortText
    <index>
      table Terms
      name Events_title_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name description
    type Text
    <index>
      table Terms
      name Events_description_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name extra_description
    type Text
    <index>
      table Terms
      name Events_extra_description_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name scope
    type ShortText
    <index>
      table Terms
      name Events_scope_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name scope_icon
    type ShortText
  </mapping>

  <mapping>
    name uri
    type ShortText
    <index>
      table Terms
      name Events_uri_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name source_icon
    type ShortText
  </mapping>

  <mapping>
    name reply_uri
    type ShortText
  </mapping>

  <mapping>
    name actor
    type ShortText
    <index>
      table Terms
      name Events_actor_index
      flags WITH_POSITION
    </index>
  </mapping>

  <mapping>
    name actor_uri
    type ShortText
  </mapping>

  <mapping>
    name actor_icon
    type ShortText
  </mapping>

  <mapping>
    name actor_class
    type ShortText
  </mapping>

  <mapping>
    name timestamp
    type Time
    <index>
      table Timestamps
      name Events_timestamp_index
    </index>
  </mapping>

  <mapping>
    name created_at
    type Time
    <index>
      table Timestamps
      name Events_created_at_index
    </index>
  </mapping>

  <mapping>
    name parent
    type ShortText
    <index>
      table Terms
      name Events_parent_index
      flags WITH_POSITION
    </index>
  </mapping>
</match>

この構成においてはdroonga0droonga1のそれぞれでFluentdが動作していますが、Sharetary関係の物はすべてdroonga1に置いておくという方針で、今回はdroonga1にもfluent-plugin-groongaをインストールして、そこからSharetary用のイベントレコードをloadするようにしています。

Issueの作成以外の各種アクティビティも含めた変換ルールの設定例の完全版は、Sharetaryのリポジトリに含まれていますので、参考にしてみて下さい。 ……というか、認証のための情報やfluent-plugin-groongaの接続先ホスト以外の全ての設定は、上記運用例でもこれをそのまま使っています。 皆さんも、試してみる時はこの設定をスタート地点として、ご自分のニーズに合うように微調整していくとよいでしょう。

ところで、上記の説明からも分かる通り、適切なイベント形式のレコードに変換さえできるのなら、Sharetaryの情報ソースはGitHubのアクティビティでなくても構いません。 この設定例は、イベント同士を関連付けたり、イベントの種類に応じてSharetaryの画面上での目立たせ方を変えたりといった事のサンプルにもなっていますので、他の情報ソースを使う場合の参考になるのではないでしょうか。

まとめ

以上、最近開発・公開したfluent-plugin-github-activitiesSharetaryという2つのソフトウェアの概要と使い方について簡単にご紹介しました。

この両者の組み合わせは利用形態の一例に過ぎないとはいえ、現時点では最も代表的な利用形態でもあります。 GitHubを開発の場所として利用するハッカソン形式のイベントを開催される際は、会場内での賑やかしとして、またイベント期間中の出来事を後から振り返る際のログとして、是非利用してみてください。

また、Sharetaryの運用例としてクリアコード関係者の公開アクティビティの定点観測所についてもご案内しました。 クリアコードが日常の業務の中でOSSやフリーソフトウェアを開発・公開したり既存プロジェクトに協力したりしている様子を俯瞰してご覧頂けますので、弊社オフィスに遊びに来られるような感覚でフラッと覗いてみて頂ければ幸いです。

2015-06-10

SEゼミ2015 - リーダブルコード勉強会を開催

2015-06-06にプログラミングが好きな学生のためのリーダブルコード勉強会を開催しました。この勉強会について、内容を作った立場からどうしてこのような内容にしたのかについて紹介します。また、今回の内容の課題と今後の解決案についてもまとめます。

基本的な流れは昨年開催したリーダブルコード勉強会と同じで次の通りです。

  1. 概要説明(「既存のコードからリーダブルなコードを発見する」を体験するよ!)
  2. 課題実装開始(各自リーダブルなコードで書く)
  3. コードチェンジ
  4. チェンジしたコードをベースに継続して課題を実装(リーダブルなコードを探しながらリーダブルなコードで書く)

コードをチェンジ(交換)して実装を継続することで「強制的に他の人の書いたコードを読む機会を作る」ことがポイントです。

詳細な流れに興味のある方はGitHubのclear-code/sezemi-2015の中に資料があるので、自由に活用してください。当日は6種類のスライドを使いましたが、それらへのリンクも含んでいます。資料もスライドもライセンスはCreative Commons BY-SA 4.0で、著作者は「株式会社クリアコード」です。

今年と昨年の違い

今年と昨年で基本的な部分は変わっていませんが、大きく変わったことがあります。それは次のことです。

  • メンターが増えた
  • テーマがOSSの開発になった
メンターが増えた

昨年はメンター5名で学生30名くらいでやっていましたが、今年はメンター20名超で学生50名くらいでやりました。メンターは単に数が増えただけではありません。優秀度合いは去年同様高いままです。メンターのリストを見れば「学生うらやましい!社会人だけど参加したかった!」と思うことでしょう。

メンターが増えたため、メンターそれぞれにどう振る舞って欲しいかを具体的に伝えることができなくなりました。そのため、今年は方針を共有して後はいい感じに振る舞ってもらうことにしました。共有した方針は「学生さんに新しい視点を与えること」です。答えを教えるのではなく、一緒に考えて、新しい視点を与えて欲しい、とお願いしました。

たとえば、ほとんどの学生さんは「他の人のコードからリーダブルなコードを探して自分のコードに取り入れる」ということを初めて経験します。新しいことに拒否反応を示したり、やることはわかってもうまくできないかもしれません。そんなときは、学生さんから今の気持ちを聞いて考えを整理することを手伝ったり、見方をこう変えてみるとなにか見えてこないか示唆を与えたりして欲しいということです。

難しいところもいろいろあったでしょうが、メンターのフォローにはとても助けられました。

たとえば、コードを読む習慣がない人の場合、プログラミングがうまければうまいほど、他の人のコードからうまくリーダブルなコードを見つけることができません。これは、「自分が書いている書き方がよい」という意識が強くなっているからです。こうなっていると、よほど自分よりうまく書かれていない限りリーダブルなコードを見つけられません。リーダブルなコードだと認識できない、と表現したほうが近いかもしれません。

このようなリーダブルなコードを見つけにくい状態になっている場合のフォローは難しいものです。「このコード、自分もやっているリーダブルな書き方じゃない?」とか、「違う見方で考えるとリーダブルだといえない?」というように、「すぐそばにある野生のリーダブルコード」に気づくきっかけを与えてくれたことでしょう。

リーダブルコードは読む人にとってわかりやすいコードなので、「キラキラしたリーダブルコード」を探そうと思うとかえって見つけられません。リーダブルコードはキラキラしていません。地味です。特別に意識せずともわかってしまうようなコードですから。そのようなコードを見つけて自分のプログラミングに取り入れるためには、「すぐそばにある野生のリーダブルコード」に気づける視点が必要なのです。

今回の勉強会では、学生さんには「すぐそばにある野生のリーダブルコード」に気づけるようになって欲しいと期待していました。そのため、メンターのみなさんにも「新しい視点を与える」という振る舞いをお願いしました。

テーマがOSSの開発になった

昨年のSEゼミのテーマは「インターンシップの準備」というテーマ(たぶん)で、リーダブルコード勉強会は「リーダブルコードを押さえておこう」ということで開催しました。

一方、今年のSEゼミのテーマは「OSSの開発に参加しよう!」で、リーダブルコード勉強会は「OSSの開発にリーダブルコードの知識があるとよい」ということで開催しました。

これを受けて、導入での説明も変えました。「他の人のコードを読んでよいところを取り込んで自分のコードに活かす」というのはOSSの開発では当たり前のこと。リーダブルコードを書けるようになるためにも同じやり方が使えるんだから、OSSの開発に参加するならこのやり方でいこう。という方向にしました。

これからOSSの開発に参加する中で、「他の人のコードから学ぶ」というやり方を当たり前の習慣として身につけてくれることを期待して説明を変えました。

今年から始めたこと

今年から「メビュー」を始めました。「レビュー」と似ていますが違います。

「メビュー」は「Mentor's View」の略で「メンターの視点」という気持ちです。

レビューは「問題がないか」という視点でコードを読みますが、メビューは「メンターの視点を共有する」という視点でコードを読みます。

自分だとよいと思っていなかったコードでも、メンターから見ればよいコードだと教えてもらえるかもしれません。リーダブルコードの解説にも次のように書いていますが、最初はヒントなしでよいコードを見つけることは難しいです。熟練したメンターの視点をもらって自分の「よいコードを見つける視点」を養います。それがメビューです。

実際にやるとぶつかること

まず、内容を活かす場所がぜんぜんないって思うだろう。でも、それはあなたの書いたコードがすでにリーダブルだからじゃない。単に「気づかない」からだ。本書を読んでいるときは次々と出てくる小さなコードを読みながら「たしかにここはそう変えた方がいいな。ふむふむ。」なんて読んでいただろう。読みながらあなたが「ふむふむ」言っていられたのは著者がわかりやすく書いてくれていたからだ。でも、実際のコードにはそういうヒントはない。著者のヒントがないんだから、実際のコードを読んでいるときは見逃してしまうだろう。それが自分が書いている真っ最中のコードならなおさらだ。読む側の視点じゃなくて書く側の視点でコードを見ているんだから。

課題と解決案

学生さんにはアンケートを書いてもらい、メンターの何人かには直接感想を聞きました。

アンケートを見る限り多くの学生さんにはよい機会となったようです。この勉強会をきっかけにOSSのコードを読んでいこうという気になった人もたくさんいました。

メンターの人の何人かには「楽しかった」と言ってもらえました。楽しくフォローしていたのなら、学生さんにもいろいろ伝わりやすかったことでしょう。

一方、次の点では課題がありました。

  • 最初の開発環境準備
  • プログラミング言語と課題のミスマッチ
  • 交換対象のプログラムの選び方

昨年もそうでしたが、何人かは開発を始める前のところでつまづきます。不安な人は30分くらい早めに会場に来てもらって、勉強会前に開発を始める準備のところを手伝ってあげるのがよいかもしれません。

この勉強会で実装する課題はファイルの読み込み処理があります。開いたファイルの管理方法、ファイルをどこに配置するか、などで工夫ポイントがでるかもしれないと考えて入れた処理です。しかし、SwiftやWebブラウザー上で動作するJavaScriptなどファイル操作をすることがまれなプログラミング言語では実装の難易度をあげてしまいます。これは意図したことではないのでファイルを使うこと自体やめた方がよさそうです。

途中でコードを交換することはこの勉強会での大事なところですが、交換する相手の実装がほとんど進んでいない場合は読む機会が少なくなってしまいます。その場合はあまり進んでいない人のコードは交換対象とせずに、ある程度進んでいる人のコードを2人以上で使うなどの交換の仕方の方がよさそうです。

他にも、課題から自由度を減らして実装に集中しやすくする、メモのまとめ場所をGitHubのIssueにしてメモをまとめるときの敷居をさげる、といったことも検討しています。

まとめ

昨年に引き続き開催したSEゼミのリーダブルコード勉強会の内容について、昨年の内容と比べながら紹介しました。また、実際に開催したわかった課題とその解決策もまとめました。

今年のSEゼミのイベントはまだ3つあります。引き続き優秀なメンターが多数参加するので、OSSの開発に参加したそうな学生さんに教えてあげてください。

今回の勉強会に参加した学生さん・メンターの感想に興味のある方は次のWebページを見てみてください。

今回の勉強会での成果に興味のある方は次のWebページを見てみてください。

2015-06-08

2015年6月6日(土)に学生向けリーダブルコード勉強会を開催予定

要約:6月6日(土)に学生向けリーダブルコード勉強会を開催する予定なので、興味のある学生の人はイベントページから応募してください。興味のありそうな学生を知っている人は教えてあげてください。締め切りは6月1日(月)です。

去年も開催した学生向けリーダブルコード勉強会を今年も開催します。(去年は参加者多数につき2回開催しました。1回目の開催レポート2回目の開催レポート。)今年も勉強会の講演者・トレーナーをリーダブルコードの解説を書いた須藤が務めます。

リーダブルコードはとても内容が評価されている本です。リーダブルコードが発売されたのは3年前ですが、今でも新しく読む人が増えています。例えば、2014年のジュンク堂書店池袋本店でのコンピュータ書売上冊数ランキングで1位*1になったり、Amazonのクラウドエンジニアが選ぶ技術書35選でも一番最初に紹介されていたりします。学生のうちからこの本に書かれていることを理解し、実践していると、今後、プログラミングをする上で大きな力になるでしょう。

内容

内容は去年と同じくリーダブルコードの解説に書いていることの体験です。解説では、本文に書いているリーダブルコードを書くための方法をどうやって実践するか、その方法を説明しています。つまり、学生がリーダブルコードに書いていることを単なる知識ではなく、実際に自分の技術や考え方として身につけるきっかけとなる内容です。

リーダブルコードを書くには読む人の視点を持っていることが重要です。リーダブルかどうかは読む人の視点での基準だからです。読む人の視点を持たずに書いているうちは、単に「想像して」リーダブルだろうコードを書いているだけです。読む人の視点を持っていれば、「事実に裏付けされた」リーダブルコードを書けます。

しかし、学生で読む人の視点を持っている人は少ないです。仕事では複数の人が同じソフトウェアを開発することが多く、また、リリースしたあとに継続して開発を続けることも多いので、コードを読む機会が増え、読む人の視点が身につきやすいです。一方、学生は自分ひとりで開発し、作って動かして研究成果が得られたらコードに触れることはない、というケースがほとんどです。そうすると、自分が書いたにしろ他の人が書いたにしろ、コードを読む機会がほとんどなく、読む人の視点を身につけることができません。

今回の勉強会ではこの「読む人の視点」の体験を重視します。具体的には次の流れで勉強会を進めることで、「読む人の視点」を体験します。

  1. 参加者全員にこの勉強会で開発するプログラムの仕様を伝える。
    • 参加者全員が同じプログラムを開発する。
    • プログラムの難易度は簡単なものにする*2
  2. 参加者全員が一定時間で各自プログラムを開発する。
    • 一定時間経過後、みんな途中まで開発を進めている状態にする。
  3. 参加者同士で開発しているプログラムを交換し、交換したプログラムを元に開発を継続する。
    • 開発を継続するためには交換したプログラム(= 他の人が書いたコード)を理解する必要がある。
      • 複数人での共同開発や作って終わりではない開発の疑似体験となり、コードを読む機会となる。これで「読む人の視点」を体験する。
      • 自分が書いたコードよりもリーダブルに書いているコードを見つけることを重視してもらう。ダメなところ探しに終始してグチグチ言っていたら勉強会が終わっていた、となったら失敗。
  4. 実際に開発をし、「読む人の視点」を体験してみて感じたことをまとめ、整理する
    • 「面白かった」、「難しかった」だけで終わらせず、「なぜ難しかったか」など理由まで落としこむ。理由まで落としこめれば、今回だけの体験で終わらず自分に身についた今後も活かせる財産になるはず。

「コードを読む」というと「コードレビュー」というイメージが根強いので、どうしても「悪いところはないか?」という視点で読んでしまいがちです。そうではなく、「コードから学ぶ」という視点で読めるようになると、コードの理解も速くなりますし、読んでいる人の成長スピードもあがります。日々新しい技術が生まれ、学ぶことが増えている昨今、コードから学べるようになると重宝されることでしょう。

参加する学生の人には「読む人の視点」、もっと言うと「悪いところはないか?」ではなく「コードから学ぶ」という「読む人の視点」を身につけ、コードを読み、よい書き方を身につけ、それを自分が書くコードにも活かす、という習慣を体験してもらいたいです。

メンター

去年はメンターとして企業で活躍中のトップエンジニアが学生をサポートしました。具体的には、クックパッドのまきもとさん、楽天の川原さん、DeNAのたなべさん、クリアコードの結城さん沖元さんがサポートしました。学生でなくてもサポートして欲しいですよね。

今年は去年よりもたくさんのトップエンジニアが学生をサポートする予定です*3。現在スポンサーになることが決定している企業は次の通りです。調整中の企業もあるのでまだ増える見込みです。

これらの企業からトップエンジニアがメンターとして参加し、学生さんをサポートしてくれます。例えば、クラウドワークスからはCTOの大場さんがメンターとして参加します。他にもメンターが決まったら随時イベントページに載るはずです。

まとめ

6月6日(土)に開催する学生向けリーダブルコード勉強会を紹介しました。リーダブルなコードを書くために大事な「読む人の視点をもつこと」を体験できる勉強会です。興味のある学生の人はイベントページから応募してください。興味のありそうな学生を知っている人はこの勉強会を教えてあげてください。あるいは、成長させたい学生を知っている人はこの勉強会への参加を勧めてください。

なお、締め切りは6月1日(月)です。

*1 2012年、2013年も1位なので、3年連続1位。しかも、2014年はその前の年より売れたみたいです。

*2 難しいプログラムを開発するためのテクニックを学ぶことが目的ではない。

*3 たぶん。

2015-05-13

6月24日開催 アジャイルアカデミー「実践リーダブルコード」募集開始のお知らせ

6/24(水)にアジャイルアカデミー「実践リーダブルコード」を開催することになりました。前回の3月6日に続いて、2回目の開催です。

「コードの読み手の視点」を身につける

「実践リーダブルコード」は、「リーダブルコードを書けるようになりたい人」や「リーダブルコードを書く開発チームにしたい人」が対象です。

ただし、このワークショップではリーダブルコードを書くためのテクニックは学びません。リーダブルコードを書くために必要な「コードの読み手の視点」を身につける事が、この講座の目標です。

リーダブルなコードかどうかはコードを読む側が判断するものですので、リーダブルなコードを書くためには、この「コードの読み手の視点」が不可欠です。この講座では「読む人が 読みやすいなら リーダブル」を合い言葉にして、今までとは違った角度からリーダブルコードへの理解を深めていきます。

「1年目から身につけたい! チーム開発6つの心得」にも関連

クリアコードでは、4/24発売WEB+DB PRESS Vol.86の特集記事「1年目から身につけたい! チーム開発6つの心得」を執筆しました。この記事は、チームによる開発をうまく回していくために不可欠な、「良いコードを書くこと」と「良いコミュニケーション」を実現するためのノウハウを紹介しています。

「実践リーダブルコード」で伝えたいことは、この記事に通じる内容です。「実践リーダブルコード」でも、「チーム開発6つ心得」で紹介するノウハウのいくつかを学ぶことができます。チーム開発6つの心得に取り組んでみようという方も、是非「実践リーダブルコード」の受講をご検討ください。

新人、ベテラン問わず学べる

実践リーダブルコードでは、開発チームでリーダブルコードを実践する方法として、「チームの『よい』を共有して、それを育てる」というやり方を学びます。これは「コーディング規約を作って守らせる」や「コードレビューで問題を指摘する」といったアプローチとは大きく異なりますので、開発経験豊富なベテランの方にとっても新鮮な学びになることでしょう。実際に、前回の受講者からもそのような声を多くいただきました。

まとめ

6/24に開催するアジャイルアカデミー「実践リーダブルコード」の募集を開始しましたので、講座の特徴を紹介しました。前回の内容はGitHubで公開していますので、ご検討の際は参考としてください。

ご興味をお持ちになられた方は、是非アジャイルアカデミーのページからお申し込みください。

2015-04-22

2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
タグ: