NodeSchool Tokyo を開催しました

f:id:yosuke_furukawa:20150412165718j:plain

NodeSchool Tokyo を開催しました。かなり面白かったのでその様子をちょっとでもお伝えできればと思います。

NodeSchoolとは

JavaScript に関する授業を集めた学校です。みんなでコードを書いたり、問題を解いたりすることで勉強できるようになっています。

今回は初回だったので、いくつか失敗した点もあったのですが、Node.jsを初めて触った人でも asyn i/o に触れたり、 babelで ES6 を学んだりと多岐に渡るカリキュラムを実施することができました。

nodejs.connpass.com

https://hexi.pics/resize/w/462/h/462/0f48f67359995f579a3a03da77320fe5.png

どんな感じでワークショップをするのか

npm のモジュールをインストールしてもらって、そのCLIツールを元に実施します。
workshopperというCLIツールを使います。npmが動く環境さえあれば実行できるため、講師が一つ一つ説明しなくても実行できるようになっています。

# learnyounode と呼ばれるカリキュラムを開始する
$ npm install learnyounode -g
# learnyounodeを起動
$ learnyounode

https://camo.githubusercontent.com/17955d1d684a9a0b3fd9d5d863393e6e952cc3b4/68747470733a2f2f7261772e6769746875622e636f6d2f72766167672f6c6561726e796f756e6f64652f6d61737465722f6c6561726e796f756e6f64652e706e67

今回実施したカリキュラム

会場でアンケートした結果、大体 7割くらいの人が learnyounode に取り組んでもらっていて、残りの1割を他の how-to-npm, javascripting, tower-of-babel が分け合う感じでした。みんな node.js に興味があるんだなとしみじみ。

f:id:yosuke_furukawa:20150412150024j:plain

大体早い人でも2時間くらいかかるカリキュラムなので、この時間内で全部終わった人は少なかったんですが、 コツを掴むのがうまい人は2つくらいのカリキュラムに取り組んでいて素晴らしかったです。

tower-of-babelは僕が作ったworkshopperで、割と沢山の人達から実施してもらいました。

英語化への翻訳も進んでいるので、英語化が済んだらまた広く公開していこうと思います。

LT大会

Node.jsで対戦ゲームを作った話 by yuusuke_takeuchi_96

Node.jsで対戦ゲームを作ったという話、socket.ioを使ったら簡単にゲームが作れるという話で、NodeSchoolっぽく初心者にも優しいデモになっていました。

画像変換サーバをNode.jsで作った話 by bojovs

Tomboと呼ばれる画像リサイズサーバーを作った話、koaを使っていたりとだいぶモダンな構成で素晴らしかった!僕らも最近リサイズサーバーをngx_small_lightでやっていたが切り替える可能性はありますね。

LoopBackを使った極初歩的なAPISwiftで作るオシャレアプリ()by isamu_suzuki_54

Swift でろうそくに対して吹き込むと風を送ってゆらぎ効果を出せるアプリを作ってくれました。
このアプリのAPIはLoopBackで作っているらしく、しかも2014年のNode学園祭の時のIssac Rothさんの話を聞いて、使ってみたくなったということで、主催の立場からするとすごく嬉しかった!!

こわくない!ディレクターがexpressではじめる新規開発 by hiro93n

ディレクターがexpressを学び始めてからちゃんと書けるようになるまでの軌跡を示してくれました。
ちなみに、僕と一緒に hexi を作っている人です。hexiについてはまた今度詳しく紹介します。

hexi.pics

まとめと今後

こんな感じでした。すごく面白かったと言ってくれる人が多くて良かったです。メンター側も色々と教える行為を通してレベルを上げられたのではないかと思います。

NodeSchoolはこんな感じで初心者向けですが、ある程度知っている人はメンターとして参加する、workshopperを新しく作るなどで貢献することも可能です :D

ちなみに今回は参加特典として ハチ公の六角形ステッカーとtower-of-babelやってくれた人用に babel ステッカーを用意しました。

hexi.pics
hexi.pics

みんながmacに付けてくれてるのを見て嬉しかった。

次の NodeSchool は 5/23(土) に開催予定です!また東京でやる予定なので是非参加してください。

https://hexi.pics/resize/w/462/h/462/da2efdeea09abcdd601d1d3ab99be13d.png

東京Node学園 15時限目を開催しました


東京Node学園 15時限目を開催しました。

ちょうど前回の学園祭から4ヶ月ぶりで4ヶ月も何もしないとかなり濃厚なネタが揃っていました。

既に素晴らしいレポートがいくつも上がっているのでそちらも参考にしてください。

東京Node学園 15時限目に行った - axross blog

東京Node学園15時限目 アウトラインメモ | Web Scratch

io.js について by @yosuke_furukawa

僕からの発表。個人的には僕がNode.jsユーザーグループ代表になってから一番大きなニュースだったのかなと思っています。
僕の知る限りの内容と実際に使うとどう変わるのかを中心にお伝えしました。

この勉強会から一週間経過して、io.jsのニュースも増えています。僕らの活動の一つとして、翻訳活動があるんですが、既にio.jsの日本のページもできています。

f:id:yosuke_furukawa:20150220143815p:plain

また、io.jsのニュースをタイムリーにお届けするために blog.iojs.jp を作りました。ここで毎週起きているweekly updateやio.jsとnode.js foundation の現状のニュースを伝えたいと思っています。

そして、今まさにそういうio.jsで起きている事について iojs-jp.slack.com で議論をしています。本 Slackに入りたい方はに一声かけていただくか、yosuke.furukawa @ gmail.com にメールを送っていただければ対応します*1


extensible web by @Jxck_

Jxckからの発表。extensible web に関しての話と、今現在Fetch を isomorphic に作っていて、isomorphic な Fetchが完成すればテストを含めた完璧なXHRができるっていうことを教えてくれるスライドだった。

f:id:yosuke_furukawa:20150220143944p:plain

extensible web 自身はローレベルなAPIセットを揃えることでハイレベルな機能を実現しようとする試みで、ローレベルなAPIセットを揃えれば後は僕ら開発者が思う機能(capability)をブラウザに実現していける。もちろん、ローレベルなAPIだけでは誰でも使いやすい機能にはならないけど、コードを書くだけで仕様に貢献できるのは大きい。


Extensible Web を支える低レベル API 群 - Block Rockin’ Codes


WebSocketの圧縮機能とSocket.IO by @nkzawa

permessage-deflateというWebSocketの通信毎に圧縮を行う仕組みを ws モジュールで実装した話。これにより(現時点では chrome 限定だけど、) WebSocketの高速化が見込めるのと、socket.io の v1.4 からはデフォルトで有効になるため、socket.ioのバージョンをアップグレードしたらこの高速化が使えるようになるという話。

f:id:yosuke_furukawa:20150220144146p:plain

こんな感じにsocket.ioのAPIを設定すれば使えるようになる(予定)。


Code On Mobile by @dai_shi

Codeをスキマ時間を見つけてモバイル上で書くことを目標にしたプロダクト。かなり気合が入ってて、モバイル上のエディタに on the fly で直接記述できるようになってた。diffを確認したり、commitしたりできてすごく面白そうだった。

CodeOnMobile

今できる通信高速化にトライしてみた by @shibu_jp

ずっと社内で秘密にしてきた shibu_jp の通信高速化の話。LZ4 + gzip の組み合わせにより、JSONのような繰り返しの多いデータなら圧縮率が上がってデータ量がかなり削減されることを発見。これにより、帯域の節約になり、通信が高速化される。

f:id:yosuke_furukawa:20150220143521p:plain

JavaScriptだとLZ4の解凍に200msはかかってしまうので、速いネットワーク環境下だと遅くなることもあるが、3Gでは速くなる。また、JSじゃなくて、ネイティブで実施する分にはLZ4の解凍はさらにはやいので、ネイティブアプリでは速くなることも予想される。

Socket.ioを使ってライフゲームで遊ぶ by @tako_black_

ライフゲームをsocket.ioで作ったのでみんなで遊びましょうという話。

コードも公開されてて、socket.ioのイントロとしても役立ちそう。

まとめ

次回は4月にまた行います!その時にまた会いましょう。

*1:slackinっていうslackのinvitationをおくる奴を使ってたんですが、slackがこのinviteの仕様をコロコロ変えるのでメールが送られてないことがあります

io.jsについて知っていること

f:id:yosuke_furukawa:20141225101839p:plain

今、Node.jsに起きてることを語る上で、io.jsは避けて通れない話題でしょう。

今回のNode.js アドベントカレンダー 2014の締めを飾るために、このio.jsについて僕が知っている限りの事をまとめて書くことにします。

io.jsを知り、今後"Node"がどうなっていくのかを皆で一緒に考えていきましょう。

またこの一連のio.jsのfork騒動はOSSという特殊なプロジェクトをどう進めていくのがハッピーなのかを知る一つの教材だと思います。
OSSに関わっている皆さん、今回も長いですが、最後まで読んでもらえると幸いです。

io.js とは何か

Node.jsのForkです。次のNode.jsの安定版になる、v0.12をForkしています。「アイ・オー ジェイエス」と読みます。名前の由来は木星にある四番目に大きな衛星の名前から取られました。*1

Nodeを使っている人のことをnodersと呼びますが、io.jsを使っている人のことはionians(アイオニアンズ)と呼ぶそうです。

Node.jsと何が違うの?

一番大きな違いはプロジェクトの取り組み方ですね。io.jsでは、オープンガバナンスモデルを採用しています。

オープンガバナンスモデルというのは、コアチームがどうやってissueやfeatureを進めるのかをオープンにして、周りから見えるようにして実現していくやり方です。一週間に一度程度のペースでミーティングが開催され、その会議の様子はyoutubeで配信されます。もちろん、コアチームは世界各国に散っているのでFace2Faceでの会議ではなく、Google Hangoutを使ったリモート会議です。基本的には合議制ですが、意見が割れた時はVoteして決めます。60%以上の賛成が得られれば採用されます。

さらに、毎回の議事録はio.jsのリポジトリと一緒に文面で保管されます。

これを追っていけば、今io.jsで何が起きててどんなことが議論されているのかが分かるわけです。

io.jsの目的は何なの?

安定したリリーススケジュールを提供することと、オープンガバナンスによる透明性を確保することにより、他の開発者からコントリビュートをしやすくするのが目的です。後述しますが、今のNode.jsの一番の問題点は安定したリリーススケジュールが確保できていないことにあります。これを解決する道を模索した結果が今のio.jsなわけです。

他にも目標としては

  • Semantic Version準拠のバージョニング
  • 企業のコントロールから外れたコントリビュータシップ
  • 週単位でのリリース
  • V8のバージョンを着実にサポートする
  • 予測可能なロードマップを立てる
  • コミュニティからの支持を得た機能拡張

といった目標があります。

Node.jsとの互換性はあるの?

Node.jsとの互換性はありますし、今後も保たれる予定です。というのも、io.js側がNode.jsに取り込まれたbugfixやfeature等を先ほどのミーティングで検討にかけてio.js側にも積極的に取り込んでいくからです。

ただし、Node.js から io.js への互換性は保証すると明言されていますが、io.js から Node.js への互換性は保証される予定はないでしょう。

現時点でのNode.jsとio.jsの機能差は?

細かいバグ修正は置いておくと、現時点の最新のNode.jsとは以下の部分が異なります。

  • gcをフックしてheapの統計が取得できる v8 API が追加されてる
  • v8のバージョンが 3.30 になってる

v8 APIは Node v0.12でギリギリまで議論された末に削除されたtracing APIの機能の一部ですね。

v8は3.30になることで、ES6方面ではgeneratorの機能がデフォルトで使えるようになったり、String.prototype.repeatがデフォルトで使えるようになったりします。

他にもclassとかtoStringとかが、harmonyオプション付きで使えるようになります。

現時点ではよっぽど最新の機能を使わない限りはnode.jsとio.jsの互換を保ったライブラリやアプリを作るのは難しくないでしょう。

また、大津さんにも修正差異をまとめて頂きました。今現在での機能差は以下のリンクに張られている通りです。

2014/12/24時点での iojs/io.js が joyent/node と違っている部分

実行ファイル名はnodeのままなの?

実行ファイル名はiojsになります、ただし、互換性を残すためにnodeのsymlinkが張られる予定です。

これには商標権の問題があげられています。
Joyentが持っている商標はNode.js以外にもNODEとか色々持っています。暫定的にnodeのコマンドも残すけど、Joyent側からnodeのコマンドを商標権侵害なので認めないなどの抗議を受け取った場合にはコマンドはiojsだけ提供される可能性はあります。ただし今のところそういう抗議めいた話はまだ無いです。杞憂に終わってくれることを望みます。

なんでNodeをForkしてio.js作ったの??

当然の疑問ですね。ここはかなり色々あります。

InfoWorldが実際にこのio.jsのモデレータをやっているMikeal Rogersにインタビューした記事があるのでそこから引用しましょう。

InfoWorld: forkしたnodeの責任者はmikealなのかな?


Rogers: ぜんぜん違うよ。Fedor Indutnyがforkとオーガナイザーを始めようとしてたんだよね。でもフォークしたnodeはオープンガバナンスモデルを採用して、technical committee(以下TC)を作る事にしたんだ。今週初めてTCのミートアップがあってそこでは:

  • Indutny (Node.js code team メンバー)
  • Trevor Norris (Node.js core team メンバー)
  • Isaac Schlueter (Node.js core team の前リーダー)
  • Ben Noordhuis (Node.js core team の元メンバー)
  • Bert Belder (Node.js の 元maintainer)

といった人たちを参加者として呼ぶ事にしたんだ。

Rod Vagg (Node.js サポーター) もまたビルドシステムを作成し管理する人として呼んだ。
僕はただTCミーティングをモデレートして議事録をとってたのと、アジェンダを作成する助けをしてただけなんだ。


InfoWorld: じゃあなんでFedorはforkをしようとしたのかな?


Rogers: 実はJoyentには7月位から働きかけ始めていたんだ、コントリビュータとコミュニティがNodeが直面している問題を効果的に解決していくためにはアクティブじゃなくて、新しいコントリビュータがいなくて、リリースが滞っているNodeプロジェクトの現体制を動かそうって試みていたんだよ。


コアチームのFedorがその交渉に待ちきれなくなってforkの準備を始めたんだ。でも彼はそれのプロモーションをしてなかった。だけど、僕らもそれをずっと見てきたし、Fedorの活動に乗っかることにしたんだ。


そうして、Nodeのコア関連のメンバーを全て移動してNode Forwardプロジェクトとして仕切りなおしたんだ。しばらくNode Forwardとして行っていた活動だったけど、"Node.js"っていう商標権はJoyentが持っていて、そこの問題がクリアになるまで新しいNode.jsはリリースできないから、名前を"io.js"に変えることにしたんだ。

という訳です。Node.jsは現時点でリリーススケジュールがちゃんとworkしていません。issueが登録されてもそれがさばかれずに放置されたり、pull-reqが滞ることが多く、この一年くらいそういう感じになってしまっていました。io.jsはこの体制を変えようという活動です。

Node.jsとio.jsの和解の道は?

JoyentはJoyentでAdvisory Boardという形でオープンガバナンスモデルのプロジェクト推進をしようとし始めています。このAdvisory BoardにはIsaacやBertといったio.js側のメンバーも参加しています。彼らが言うには、io.jsのミーティングもAdvisory Boardのミーティングも両方共出るし、敵対する事無く、コンセンサスを取って進めていく予定だと書かれています。

ちょうど、12/4で行われたAdvisory Boardでio.jsに関してどういう形で対応するべきかの議論が載っているので簡単に翻訳しておきます。

io.js Fork (Scott Hammond Joyent CEO)

Scott Hammond (Joyent CEO 以下 SH): io.jsに関して、Node.jsとの関連や哲学、フォークした意図が知りたい。


Isaac Schlueter (元Node.jsリーダー、io.jsにも所属 以下 IS): io.jsはFedorがnode-forward/nodeをprivateリポジトリのままにしておくことにフラストレーションを感じで行ったことだ、商標権の問題なので名前とリポジトリが変わったんだ。コミュニティはそれに集まっただけで、問題の発見と解決というイテレーションを行いたいというnode-forwardとしての意思は変わらない究極的にはjoyent/nodeにも貢献されるはずだ。Advisory Board (以下AB)、ワーキンググループにも何の敵対心も持っていないし、io.jsの成功はそれらの進捗とともに歩んでいかなければありえないと思っている。joyent/nodeをio.js/io.jsにフォークしたのはisaacs/nodeにフォークしているのと何ら変わりない意図だ。


Issac Roth (StrongLoop CEO 以下 IR): オブラートに包んでるよね。node-forwardは利他主義だったけど、io.jsは転覆しようという感じにもとれる。ABにいる人はnode-forwardにいる人を含んでいるけどio.jsへの衝撃はあんまりないよね。


IS: そんなことはないよ。node-forwardは厳密には技術的な発展じゃなくて組織的な発展のつもりだったんだ。


IR: node-forwardはconsensus-seekingモデルを作られているけど、io.jsは一方的にforkしたんじゃないのか(コンセンサスは取ったのか)。


IS: 僕もBertも書いたし、Issac Rothも感じていると思うけど、io.jsのコミュニティはそんな風に敵対的に見ているわけじゃないよ。同じ方向、同じ目標を向いてると思っているよ。そんな風に敵対している体でストーリーを語っているジャーナリストばかりじゃないし、io.jsのメンバーの中で敵対している人もいないよ。8月にJoyentとNode Forward間で対話が始まってから、12月になって展開(forkした事実)があっただけだよ。


SH: 僕がもっとコミュニティから信頼してもらう必要があったのかな。


IS: NodeコミュニティはScott Hammondが来る前のJoyentにも信頼してなかったよ。Joyentの立場はずっと"Nodeを変えない"だった。Scott Hammondのせいではないよ。


SH: 我慢してくれたことは理解してるよ。


IR: もちろん、外から見た認識が大きく変化していない事は知ってる。io.jsで語られてるような事(Node.jsの進捗が悪いこと)は指摘されてるし、io.jsはio.js自身のやり方があるんだろう。


IS: Fedorがforkしてからコミュニティ側が彼に対して助言を与えたんだよ。


Dan Shaw (NodeUp podcaster 以下 DS): ABのメンバーはNodeのビジネスとしての価値を守るためにここにいる、だけどio.jsのリリースはコントリビュートしたい人達の鬱憤が溜まってリリースされるんだ。ABにいるメンバーは目を覚ますべきだよ、io.jsの流れを受け入れてさらに高速化した方がいい。


IS: ABでの約束(リリース高速化)を果たすためにみんなのケツには火がつけられたんだと思っていい。ただ、この時点でio.jsのために変化する必要があるとか思っててはいけないと思うな。Technical Committeeはio.js側のメッセージの意図を理解して、シェアする必要があるし、その上でJoyentのためにより良いフィードバックにする方がいい。io.jsへの意見を何も持たずにただ反対するべきじゃない。こういう分岐は最適な方法ではないだろうけど。


Todd Moore (Joyentアドバイザー TM): ただ分岐してしまったら全員にとって良くないだろう、私達は集中して行くべきだよ。


Chris Saint-Amant (Netflix マネージャ CS): Isaacが描いている内部の意見とは認識が違うな。io.jsサイドからはどんな譲歩も見えない。io.jsからはJoyentの期待を込めて和解へのはっきりとした道を示すべきだよ。真意と野望があるなら和解への道をはっきり見せて欲しい。中間成果としてそれをはっきりさせることはできないのかな。


IS: 和解のための方法がいくつかある: 1) io.jsのコアに code/binaryを移して欲しい。つまりio.jsをnodeにラップさせるんだ。 2) io.jsからのパッチを受け取って欲しい。なんの敵対心も何の競争心もない。io.js側に一時的な方法を言ってコミットしてもらうのはちょっと早計だ。


TM: 私達は良い方向に進んでいるんだ。もう一回一緒にフォークを戻すっていう可能性はあるんじゃないか


CS: 長い間不一致があったって言われてたらABとの和解は難しいんじゃないかな。根本から解決されるべき問題は何なのだろう。もし問題が和解されないなら、フォークするしか解決策がないのか、もっと問題をはっきりとする必要があるんじゃないのか。


IS: わかりやすいメッセージは準備している。いくつか意見もある、だけど誰もNodeの名前を変えよう(Node.jsと取ってかわろう)とは思っていない。ここにいる皆は"Node"って言ってるし、Node以外のサーバーサイドJSをNodeと呼ぶつもりはないし、Node.jsとio.jsが競合した所でお互いに損害があるだけだろう。結論を出すのは早過ぎるけど、共に生きる道を見つけて行ったほうが良さそうだ。もし問題が解決される可能性があるのであれば、io.jsが存在し続ける理由は何もない


CS: よく聞かれるので、Nodeコミュニティへの簡潔で明瞭な説明をしたいところだ。


IS: はっきりさせておきたいんだけど、Fedorがこれを一方的に行ったことには少し困惑している。io.jsが最適な方法ではないという呼びかけを継続して行うのは賛成だ。フォークの発行に伴ってアナウンスするのが理想的だと思う。


DS: Isaac はio.jsのメッセージの多くを語らないけど、ABにとっての反応を返す必要があるよね。


DC: ABからの統一見解は必要だと思う。


DS: ABのメッセージは絶対あったほうがいいだろう、issueの解決に向かって作業を進めているんだっていう事を見せた方がいい。


CS: 統一に向けてABとio.jsは一緒にメッセージを出せるかな?


SH: ABの背景にある理由はこういう問題に正面から取り組むことだ。私達がNodeをどうしたいのかを周りに見えるように並べていく必要があるんだ。コミュニティやエコシステム、それぞれをどうしたいのかを含めてどうしたいのか示す。このグループ内ではっきりさせた上で、明らかな主張を形作っていきたい。解決や進捗が素早いだけでは十分じゃない。これらの問題を解消する事に関心があるのか、フォークするのを妨げるような行為が正しいのか、コミュニティの分割はどうなるのか、私達はある一定のポイントまで信頼を構築していく必要があると感じているし、信頼関係を構築していくには、ここでもっとやりとりする必要があると認識しているio.jsの真のメッセージがあるなら見せて欲しい。


IS: 僕はたまたま両方のグループの中にいるとても独特な立場にいる。io.jsも一緒にメッセージを出していく必要があるし、継続的に発信していく必要がある。ただ共同声明を出すのは良いアイデアとは言えない。io.jsがABと関連が強いように見えてしまうだろう。io.jsにとってのモチベーションの一部はコミュニティ駆動で決めるっていうことであって、Joyentによって支配されているように見えたら意味が無い。もしこの関係が見えてしまったらJoyentを信頼していない人たちのいるコミュニティはさらに混乱してしまう。「Joyentと一緒に働いているコアコントリビュータは問題の解消に努めている」し、「io.js側もJoyentともう一度統一するゴールに向けて作業を始めている」と別々に言うべきだろう。


TM: ABはio.jsと同じ方向を向いてゴールを目指していると約束して、働きかけているというべきだろうね、io.js側のメンバーとももう一度作業したいと思っていると発信していこう。


SH: Fedorに理解してもらうように連絡してみるよ。聞く耳持たれなくても、彼と会話するようにしたい。私が代表だという自覚だけじゃなく、コミュニティと一緒にうまくやるためには一貫したメッセージを持っておく必要がある。彼と連絡することがプロジェクトの方向を正しく、ベストな方向に導く事になるだろう。

現時点ではまだScott Hammondからはio.jsに関する見解は発表されていませんが、なるべく統一に向けて意思を出していくこと、今後issueが放置されたりはしないようにNode.js側も改善に努める事を議論しています。それを受けて、Isaacもio.js側からもメッセージを出すことにするという方向になっています。和解がない訳でありません

現時点ではまだ議論中なのです。

なんでNode.jsのリリーススケジュールは滞ってしまったの?

ここですね。ここまで深堀りしている記事は無いので、ここからは僕の調査が始まった所です。
本当にいくつもの理由があると思われます。僕が調べきれているわけじゃないので、新しく明らかになった事実があれば随時アップデートしていきます。

また、わからない所は想像などで補完しているので事実とは異なる所もあるかと思います。

コアメンテナが抜けたこと

一番大きいのは Ben Noordhuis がメンテナから抜けたことじゃないでしょうか。彼は技術力もさることながら、リーダーシップを取りながらissueを捌くことに長けていました。githubの中の事だけじゃなく、MLへの精力的な解答など非常にコミュニティに尽くしてくれる人物でした。2013年末ですね。

時を同じくして2014年初めにIsaac SchlueterがJoyentから去って、npmの会社を設立します。ここからTJ Fontaineが新しいリーダーになります。

この二名がコアメンテナから抜ける事はNode.jsにとっては非常に大きな痛手でした。TJ Fontaineが悪役っぽくなっている記事を見かけますが、実際TJだけが悪いわけではありません。

2013年から2014年はNode.jsにとって大きな戦力ダウンで誰がリーダーになっても辛かったと思います。

もちろんJoyentも何もしなかった訳じゃなく、CLA(Contributors License Agreement)を書かなくてもコントリビュートできるように調整したりと色々策を講じますが、立て直しきれていません。

TJ Fontaine の役割が多い

TJ Fontaineは何もやっていない訳じゃありません。彼はコアメンテナであり、Node.jsのエヴァンジェリストでもあります。エヴァンジェリストとしての彼の活動は世界中にいるNode.jsの開発者への布教活動です。

Node on the roadと呼ばれるNode.jsを世界中のエンジニアに展開する活動が2014年の2月から行われていますが、そこに彼はひっきりなしに呼ばれています。

つまり世界中を飛び回っているわけです。それでIRCに呼びかけても捕まらないし、移動中であることが多くて活動が滞りがちです。手が止まってしまうことが多いのにはこういう背景もあるのです。

実際僕がnodeconf.euに行った時にTJ Fontaineと会って、ちょっとだけ話をしたんですが、

Q. Node学園祭に来れないのって何か理由あるの??

A. いやとにかく今スケジュールが全然読めないんだ、この前のnodeconfの時もアムステルダムに行って途中からしか参加できなかったんだ。とにかく移動時間ばかりでちゃんとした時間が確保できていないんだ。明らかになったらちゃんと連絡するから。

っていう感じでした。

プロジェクト体制の変化

元々JoyentがNode.jsを作ったわけじゃなくて、Ryan DahlがNode.jsを作り、それをJoyentがメンテナンス母体を作ろうという形で出資したのが始まりです。結果としてRyan DahlやIsaacsを含めた数名がJoyentの社員になり、Node.jsはJoyentの下で開発が進むことになります。

JoyentのビジネスはAWSGoogle Cloud Platform、Azureとかと同様のIaaS、PaaSを提供している所謂クラウドサービスです。別の言い方をすればAmazonGoogleMicrosoftの競合なわけです。

そんなJoyentがビッグカンパニー達と戦っていくのに取った戦略が、Node.jsを自分達の傘下に収めることだったのです。つまり、Node.jsのコンサルをしながらPaaSとしてNode.jsを主体としたサービスを提供することで、言語・OSレベルでサポートできる環境を作るという計画です。

実際に、JoyentはNode.jsのコアメンバーを自社に入れる時、"no.de"と呼ばれるPaaSを計画している事を明かしています

ただ実際はこの"no.de"は2012年末にクローズします。同じ時期に出たNodejitsuの方が人気が出てしまったので、そちらに移行する方向でクローズしました。

こういうプロジェクト体制が変わったことも少なからずチーム体制に影響を与えていると思っています。もしも"no.de"がうまく行っていれば、社内にもう一つNode.jsの運用チームができて、そこでNodeコアチームから知識を継承できる、良い細胞循環できるようなチーム作りができてたのでは、と思っています。

結局Node.jsが滞ってしまっている要因は?

  • コアメンテナの中でも強大な二人が抜けていること
  • TJ Fontaineの役割が多いこと
  • no.deの頓挫とそれに繋がるJoyent内のNode.jsメンテナのリソース不足

の3点位が挙げられるんじゃないかと思います。

結局どうすればいいの?

今回の騒動を受けて、Joyent側もAdvisory Boardを設置し、オープンガバナンスによる透明性確保に努めています。

Node.js側も着実に良い方向に進んでいると思っています。ただし、io.jsの方が今のところ、どうしても速度が早いし、新しい方が好きな人にとっては受け入れられやすい可能性があります。

逆にNode.jsのほうが安定しているのでエンタープライズ向きではあります。Strong LoopというNode.jsコンサルを行っている会社もNode.jsは継続的にサポートをするけど、io.jsの方はtoo early なので今すぐにはサポートしない、という形で静観を決めています。

どちらを選ぶかはその目的によって決めて下さい。安定したプロダクトで使うなら、今はまだNode.jsで良いと思います。そうじゃなくて新しい機能を積極的に使っていくなら、io.jsを使ってみるのも良いのではないでしょうか。

ただ、先ほどのミーティングでもあったように、将来的に和解して、また取り込まれる可能性もあります。個人的にはまだ静観する姿勢でいいかなと思っています。

io.jsのリリースは 1/13 のFedor Indutnyの誕生日に行われる予定です。その時にはまたもう少し動きがあると思います。

もしもio.jsが主流になったら東京Node学園って名前どうするの?

Node.jsもio.jsもどちらも実行ファイルがnodeである以上は東京Node学園のまま行きます!
次回の東京Node学園は2月開催予定です。その時にはもっと状況は変化してそうですね。

まとめ

  • io.jsの概要とio.jsの目的
  • io.jsとNode.jsの違い
  • io.jsに関するJoyentの現時点でのスタンス(和解の道へ)
  • Node.jsのコミットが滞った理由

これら今皆さんが抱いているであろう疑問に回答しました。

2014年のNode.jsは本当激動ですが、2015年もこの激動は変わらないでしょう。ただひとつ言えるのは、ここまで大きくなったnodeがコミュニティが分裂して終わりになるみたいな事は誰一人として望んでいないし、良い方向に進んでいけるといいなと思います。

何か意見があればissueで聞いてみてください。英語が得意じゃなければ僕に意見を下さい。日本からもNode ForwardAdvisory Boardに意見が言えるようにしていきたいと思っています。

*1:木星にある衛星はギリシャ語ではイオですが、英語読みでアイ・オーらしいです。コミュニティのメンバーがアイ・オーと呼んでたのでアイ・オージェイ・エスで間違っていないと思います。

文字認識ライブラリ okrabyte を使ってS3にPUTされた画像をAWS Lambdaで文字認識する

このエントリはAWS Lambda Advent Calendar 2014 - Qiitaの(本当は9日目の)記事です。
(ちょっとAWS Lambdaのアカウント取得に手間取ってしまって遅刻してしまいました。。すいません。。)

AWS Lambda とは

AWS LambdaはAmazonが作った新サービスであり、S3の変更やDynamoDBの変更をフックしてNode.jsの関数を実行するという仕組みになっています。これを使うと、例えばデータが変更された後にPush Notificationを出したり、S3にアップロードされた画像のサムネイル画像を作成したりといったことが可能になります。

今回はこのAWS Lambdaを使ってS3でPUTされた画像から文字認識をしてみようという試みです。文字認識した結果をDynamoDBとかに置いて検索に利用すれば、画像内に書かれた文字で検索できるようになったりしてハッピー。

okrabyte

okrabyteAWS Lambdaで文字認識するために僕が書いたライブラリです。

C++で書かれた文字認識ライブラリをemscriptenによってJS化されたocrad.jsを内部的に利用しています。ocrad.jsがcanvasを内部的に使っていたんですが、そこをcanvasじゃなくても文字認識できるようにアダプターを書いてnode.jsっぽく非同期に呼べるように加工したものです。*1

Getting Started okrabyte

こんな感じにインストールして、

$ npm install okrabyte -S

こういう画像を用意して、

https://github.com/yosuke-furukawa/okrabyte/raw/master/test/fixture/hello_world.png

以下のようにAPIを呼び出せばOK!

var okrabyte = require("okrabyte");
okrabyte.decodeFile("test/fixture/hello_world.png", function(error, data){
  console.log(data); // Hello World!
});

okrabyteの詳細はまた今度書きます。

ではこのokrabyteを使ってAWS Lambdaで画像認識をしてみましょう。

S3でPUTされた画像をAWS Lambda から取得して文字認識する

S3から画像をGETして画像認識させるために以下のようなファイルを書きます。

var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});
var okrabyte = require('okrabyte');

exports.handler = function(event, context) {
   var bucket = event.Records[0].s3.bucket.name;
   var key = event.Records[0].s3.object.key;
   s3.getObject({Bucket:bucket, Key:key}, function(err,data) {
        if (err) {
           console.log('error getting object ' + key + ' from bucket ' + bucket + 
               '. Make sure they exist and your bucket is in the same region as this function.');
           context.done('error','error getting file'+err);
        } else {
           // 今のところ、okrabyteがpng以外をサポートしてないので一旦エラーにする
           if (data.ContentType !== "image/png") context.done('error', 'support png only.');
           // ここで、okrabyteを使って文字認識させる
           okrabyte.decodeBuffer(data.Body, function(err, data){
             if (err) context.done('error', '' + err);
             // 一旦stdoutに出力させる、ここをDynamoDBへのinsertに変えるとかすれば、画像内の文字で画像を検索できるようになって捗りそう。
             console.log(data); 
             context.done(null,'');
           });
        }
      }
   );
};
Execution Role
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:*"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::*"
      ]
    }
  ]
}
Invoke Role
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Resource": [
        "*"
      ],
      "Action": [
        "lambda:InvokeFunction"
      ]
    }
  ]
}

Demo

動かしてみましょう。

画像

f:id:yosuke_furukawa:20141222085912p:plain

この画像をAWS Lambdaに渡して、AWS Lambda amazonという文字列を認識させるようにします。console.logとして、ログに文字列が出力されればゴール。

S3から画像をPUTする。

f:id:yosuke_furukawa:20141222090135p:plain

S3コンソールからUploadボタンを押して、先ほどの画像をUploadします。

CloudWatch Logからログを確認する。

f:id:yosuke_furukawa:20141222090905p:plain

やっふー!AWS Lambda amazonっていう文字列が出力されてますね!
色々試しましたが、独特の表現があるフォントや背景がごちゃごちゃしている画像は認識精度に難があるものの、ある程度はっきり見えているとちゃんと認識されます。

Noderから見るAWS Lambda

AWS LambdaはNode.jsの可能性の一つであり、イベント駆動でサービスを構築できる非常に面白い機能です。これまで、Node.jsでタブーとされてきたイベントループを止めるようなCPUを使った処理が、AWS Lambda経由で実行されることで自分のサービス内のプロセスのイベントループを止める事無く実現できます。つまり、Node.jsが苦手と言われてきたCPUを使ったイベントループを止めるような処理をAWS Lambda側に逃がして非同期で処理させることができるようになります。

AWS Lambdaへの要望

文字認識ライブラリとして代表的なのはtesseractなんですが、こういう任意のコマンドが事前にビルドできると嬉しいなと思いました。
(まぁtesseractがなかったので、okrabyteみたいなpure JSのOCRライブラリができたので、それはそれで副次的な効果を産んで良いとは思いますが。)

まとめ

  • okrabyteっていう文字認識ライブラリを作りました。


  • AWS LambdaでS3にPUTされた画像を使って文字認識をしてみました。

今回使ったコード一式は以下のリポジトリに上げました。




そんなAWS Lambdaですが、今日は初のmeetupが開催されるようです。


AWS Lambda Meetup #0 - connpass

僕は補欠で応募中ですが、既に30人以上の補欠がいるので、ちょっと厳しいかも。またなにか作ったらお知らせします!

*1:ちなみに名前の由来はOCRとオクラからです。Ocraっていう名前にしようかなとも思ったんですけど、既にRubyで同名のGemがあるみたいなんでやめました。

Stream今昔物語

この記事は Node.js Advent Calendar 2014 の 1日目の記事です。

こんにちは、代表です。

Stream大好きなみんなのためにStreamの過去と現在、そして未来についてお話するよ!!
Streamを何故使うのかっていう話と歴史的な話をします!!

Streamとは

データの流れを扱うための抽象化されたモジュールです。もうみんな耳にタコかもしれませんが、Streamを使うとデータの流れを綺麗に書くことができます。ちなみに今に始まった概念ではなくて、C++にもC#にもStreamがありますし、Java8の新機能にもStreamがあります。「データを扱うときの抽象化された流れ」を指す広義のStreamという意味では今日の言語ではだいたい実装されています。

Node.jsでは、以下のリンクが参考になるでしょう。


Node.js の Stream API で「データの流れ」を扱う方法 - Block Rockin’ Codes

Streamを何故使うべきなのか

まずそもそもStreamをなんで使うべきなのかっていう話です。
とりあえずこれを見てください。

ファイルからデータを読み込んでhttpレスポンスを作る

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    // fsのreadFileを行ってdata.txtを読み込む
    fs.readFile(__dirname + '/data.txt', function (err, data) {
        // res.endでdataの中身を送る
        res.end(data);
    });
});
server.listen(8000);

これは一般的なhttpサーバーの例ですが、fs.readFileメソッドdata.txtを読み込み、コールバックでレスポンスとして返しています。一応普通に動きますがちょっとした問題があります。
それは、一旦callbackの第二引数にあるdataの値を生成するためにdata.txtの値をメモリ中に持ってそれからres.endに渡しているっていう事ですね。もしもdata.txtがめっちゃでっかいサイズのデータだったらメモリを食う結果になります。

    // fsのreadFileを行ってdata.txtを読み込む
    fs.readFile(__dirname + '/data.txt', function (err, data) {
        // res.endでdataの中身を送る
        // このdataがすごく大きいとメモリを食う
        res.end(data);
    });

そこでこういう事が無いようにStreamを使いましょうというのがよく言われる話です。
Streamを使って書くとこうなります。


Streamを使ってレスポンスする

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    // ここで Streamを作る
    var stream = fs.createReadStream(__dirname + '/data.txt');
    // resにpipeする
    stream.pipe(res);
});
server.listen(8000);

こうすることで解決されます。こうやってStreamに書き換えることで2ついいことがありますね。一つはメモリ効率。もう一つはcallbackのネストが一つ減りましたね。

逆に失われたのはdataを加工しにくくなりました。前だったらdataを加工する場合はこう書くことができました。


レスポンスデータを加工する

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    // fsのreadFileを行ってdata.txtを読み込む
    fs.readFile(__dirname + '/data.txt', function (err, data) {
        // dataを加工する、最後にhogeって付け足す。
        var newData = data + "hoge";
        res.end(newData);
    });
});
server.listen(8000);


じゃあ逆にStream使って加工したい場合はどうするのがいいんでしょう?
そう、データを加工するためのStreamを作るんです。このStreamをTransformStreamって言います。

coreのモジュールだけでこのTransformStreamを作ってもいいのですが、ここではgulpなどでお馴染みのthrough2を使います。


Streamを使ってレスポンスデータを加工する

var http = require('http');
var fs = require('fs');
var through2 = require('through2');

var server = http.createServer(function (req, res) {
    // 末尾に hoge って出す streamを作る
    var hogeStream = through2(
      function transform(chunk, enc, next){
        // ここでは何もしない、ただcallbackを呼ぶだけ
        // 流れているデータに対して値を加工したい場合はここで処理する
        next(null, chunk);
      },
      function flush(cb){
        // 最後だけ"hoge"っていう文字列を入れる
        this.push("hoge");
        cb();
      }
    );
    var stream = fs.createReadStream(__dirname + '/data.txt');
    // resにpipeする
    stream.pipe(hogeStream).pipe(res);
});
server.listen(8000);

ここまでのStreamの話を抑えておきます。

1. Streamはデータの流れを扱う奴
2. pipeを使うとReadableとWritableのStreamを繋げることができるようになる。
3. データを加工するにはTransformStreamを使う

でかいJSONを扱う時はJSONStreamを使おう

Streamの例で実践的な話としてはnpmのパッケージ一覧だったり、twitterのtimeline APIで取れるような大きなJSONを扱う場合は、絶対JSONStreamを使ったほうが良いです。

Node.jsのJSONをparseしたりstringifyする際に組み込みのJSONオブジェクトを使うとそのタイミングでJavaScriptオブジェクトにしてくれたり、文字列にしてくれたりしますが、これも先程の例と同じで一旦Streamからメモリに貯めてオブジェクトにする必要があります。

Streamをこよなく愛するものはJSONをParseする際にJSONStreamっていうDominic Tarrが作っているやつを使いましょう。これは組み込みのJSONを使わずに自前でParserを実装していて、それをStreamで流すようにしています。なので、Streamの流れを止めること無くJSONをparseしたりstringifyしたりすることができます。

var http = require('http');
var fs = require("fs");
var JSONStream = require("JSONStream");

var server = http.createServer(function (req, res) {
    var stream = fs.createReadStream(__dirname + '/package.json');
    // package.jsonからversionの値だけ引っこ抜いてresponseに返す
    stream.pipe(JSONStream.parse("version")).pipe(res);
});
server.listen(8000);

parseに渡す時にkeyを指定すれば、その値だけ取ることもできますし、keyだけじゃなくてもう全て必要なときは以下のようにする必要があります。

var http = require('http');
var fs = require("fs");
var JSONStream = require("JSONStream");

var server = http.createServer(function (req, res) {
    var stream = fs.createReadStream(__dirname + '/package.json');
    // package.jsonからversionの値だけ引っこ抜いてresponseに返す
    stream.pipe(JSONStream.parse()).on("root", function(root){
        res.end(root.version);
    });
});
server.listen(8000);

ただこの場合は、結局全てを一旦Objectにしているので、JSON.parseと非同期になる以外は変わりません。基本的には大きなファイルを扱う際は全てをオブジェクトにするんじゃなくて、スクレイピングする要領で一部のデータだけを扱って下さい。

Before Stream

こっからはNode.jsにおけるStreamの歴史です。

そもそもNode.jsはlibuv(Event駆動なIO処理) + V8 JavaScriptエンジンというだけでそこにStreamなんてものは最初からは無かった。http, fs, tcpそれぞれ違うイベントを持ってたし、そうなるとそれぞれのデータのやり取りの方法は分かっても統一的なインタフェースが無いから覚えにくかった。

たとえば、httpリクエストは今でこそPOSTの時のbodyを受け取るイベントは"data"イベント(Stream2では"readable"イベント)だけど、前までは"body"みたいなイベントだった。これだとインタフェースが統一されてないから覚えなきゃいけないことが多い。

Stream0

それからStreamが生まれた。これはv0.1の頃。

これはReadableStreamを以下のように書くことにしていた。

// readable stream
// データ読み込み
readable.emit('data', chunk);
// 読み込み終了
readable.emit('end');

// writable stream
// データ書き込み
writable.write(chunk);
// 書き込み終了
writable.end();

んで、Readable Streamと Writable Streamを結ぶメソッドを作られた。
これが、util.pumpっていうメソッドだ。これを使うと、以下のようにStreamを結ぶことができる。

util.pump(readable, writable);

ただこのメソッドv0.1の頃の遺産でもう今は使わない
実際のStreamはdataとend以外のerrorとかcloseみたいな色んなイベントを扱うようになってしまったし。Streamにカスタマイズ性があることを考慮していくとutilpumpっていうメソッド単体で拡張するのは難しいっていう判断があったんだと思う。

かくしてこのStreamに大改修が行われていく。

Stream1

さて、こっからは馴染みやすいStreamになっていく。

readable.on("data", function(data){
  // データ読み込み処理
});
readable.on("end", function(){
  // 読み込み終了処理
});
// 読み込み一時停止
readable.pause();
// 読み込み再開
readable.resume();

// データ書き込み処理、書き込めなかったらfalseが返る
writable.write(data);
// データ書き込み終了処理
writable.end();
writable.on("drain", function(){
  // 再びwriteできるようになった時の処理
});

またStream1になるとpipeメソッドが生まれてreadableとwritableのStreamを以下のように繋げることができるようになる。

readable.pipe(writable);

これによってStreamを改善しつつ、JavaScriptライクな書き方ができるようになった。

// こんな感じで書けるようになった。
a.pipe(b);
b.pipe(c);
c.pipe(d);
// さらにv0.6からはpipeの第一引数のstreamが戻り値になるようになったので、chainメソッドっぽく書けるようになった。
a.pipe(b).pipe(c).pipe(d);

これでめでたしめでたし、と思ったけどこっからが問題。
Stream1にはいくつか欠点があった。

Stream1の欠点

  • dataイベントの受信(readable.on("data", function(){}))の前にデータが送信された場合にデータを取り逃してしまうことがある。
  • これを防ぐために書き出しの前にReadableStreamが受信してるかチェックして、なかったらデータをメモリ中に貯めておくっていう、所謂Bufferingを作る必要がある。でもこれはとても難しい。ユーザーのライブラリで下手に作るとメモリを馬鹿みたいに食うようになっちゃったりするし、メモリリークの元になっちゃう。
  • 他にもIOが不安定な環境でStreamを使ったりするとpause(読み込み一時停止)とresume(読み込み再開)のメソッド呼び出しが多発してしまうので多少CPU負荷がかかる

というわけでシンプルでいいよね、と思ってたStream1だったけど、悪い側面も出てきてしまった。この問題を解決するためにStream2が導入されたんだ。

Stream2

内部にバッファ機構を持ったStream、これでStream1の欠点を解消している。バッファがあるからデータが受信されるようになるまでデータをロストすることなくメモリ中に値を持てるし、IOが不安定な環境でもバッファリングすることでCPU負荷も改善できるようになったんだ。

readable.on("readable", function(){
  // データ読み込み処理がreadableイベントになり、
  // データをもらうときはreadメソッドを明示的に呼び出すようになった。
  // readしない場合はbufferに溜まり続ける
  var data = readable.read();
});
readable.on("end", function(){
  // 読み込み終了処理
});
// 読み込み一時停止
readable.pause();
// 読み込み再開
readable.resume();

// データ書き込み処理、書き込めなかったらfalseが返る
writable.write(data);
// データ書き込み終了処理
writable.end();
writable.on("drain", function(){
  // 再びwriteできるようになった時の処理
}); 

さて、大きな違いは見てもらったとおり、dataイベントじゃなくて、readableイベントになって、bufferから明示的にデータを読み込むようになったことだ。残念だけど、古いNode.jsの本とか記事だとまだdataイベントのままになっていることが多くてどうしてもStream2の恩恵を受けていないことが多い。

ちなみにStream2のもう一つの特徴としては、Stream1のインタフェースは後方互換のために残しているってことだ。dataイベントを受け付けるようにしててもStream1モード(old mode)になって動いてくれる。

いやーめでたしめでたし、Stream1から改善された。と思ったけど、こっからがv0.12以降のFutureの話だ。
Stream2にも欠点があった。

Stream2の欠点

  • Stream1のモードに一回なっちゃうとStream2には戻れなくなる、Stream1とStream2を混ぜて使えない
  • readableイベントで能動的にdataを取りつつもdataイベントでデータを受動的に受けたい場合もある

Stream3

v0.12ではこのStream3が入ることで欠点を解消してくれる。
Stream3 = Stream1 + Stream2 であり、このStream1モードの概念が無くなり、どっちのAPIにも対応されるようになり、シンプルになった。
readableイベントもdataイベントも両方発火されるため、Stream1の受動的なデータ受信もStream2のpull型のデータ取得もできるようになった。使う側が好きな方を選ぶという仕組みに。

また、Writeする際にBufferingの調整をしてから出力したいっていうユースケースに対応するためにwritev(2)を使って必ずStream中のバッファに出力するcorkとそのバッファからflushするuncorkがWritableStreamに追加された。これによってデータを出力させずに強制的にバッファリングさせることも可能になった。

というわけでStreamに完全な互換性が追加され、若干性能面でも安定したStream3が完成。これがv0.12から提供されるStreamだ。もちろんv0.11(開発版)でも既に使える。

安定版(v0.10)でも使いたいあなたに。

実はStream3の参考実装は既に今のnpmに上がっている readable-stream@1.1.x を使えばStream3になります。

$ npm install readable-stream@1.1
var Transform = require('readable-stream/transform');
var fs = require('fs');
var util = require('util');
util.inherits(HogeStream, Transform);

function HogeStream(option) {
  Transform.call(this, option);
}
HogeStream.prototype._transform = function(chunk, enc, cb) {
  cb(null, chunk);
};
HogeStream.prototype._flush = function(cb) {
  this.push("hoge"); 
  cb(); 
};
var stream = fs.createReadStream(__dirname + '/data.txt');
var hogeStream = new HogeStream();
stream.pipe(hogeStream);

hogeStream.on("readable", function(){
  // Readableでpullで取得する
  console.log("read :"+ hogeStream.read());
});
hogeStream.on("data", function(data){ // flowing modeになる
  // dataを受動的に受信する
  console.log("data :"+ data);
});
// paused modeに移行、この行があるとreadでデータを読めるが、この行がないとreadにはデータは残らない
hogeStream.pause();

こうするとv0.10でもStream3があなたの手元に。また、readableとdataを受診するのも可能。もしもthrough2から使いたい場合は以下のようにしてしましょう。

$ npm install through2@1

through2もreadable-streamもバージョン指定しなければインストールされるのは安定版の古いバージョンですが、Stream3を触りたい人達のために既にnpmには新しいものができています。もしもStreamの新しいバージョンのものが今の安定版でも使いたい場合はバージョン指定して使ってみましょう。

東京Node学園祭 2014を開催しました

f:id:yosuke_furukawa:20141115093242j:plain

写真は @summerwind さんからです!ありがとうございました。

僕が最初に参加した学園祭から3年、まさか開催する側に立つとは3年前の僕は思いもしなかった。すごく楽しかった!!

東京Node学園祭 2013にスタッフ兼スピーカーとして参加しました。 - from scratch

東京Node学園祭 2012に行ってきました。 - from scratch

東京Node学園祭に行って来ました。 - from scratch

ちなみに今回は2011年にも来たGuillermo Rauch、2012年にも来たMikeal Rogersに加えて新しくStrong Loopの元CEOであるIssac RothとNodeSchoolの主催者であるMaxwell Ogdenの4人を呼んで開催しました。自分で言うのもなんですが、ハンズオンあり、基調講演ありで非常に有意義なカンファレンスだったと思っています。

楽しかったし、色んな工夫とかTipsがたくさん出たので来年もまたやります!!!

講演

socket.io workshop

socket.ioを使ってFPSを作るというワークショップ。コミッターの@nkzawa@TonyKovanenからsocket.ioの使い方を説明してもらいつつもどういうレベルのものができるのかっていう話でめっちゃ面白かった。完成形はこんな感じ。

f:id:yosuke_furukawa:20141123114714p:plain

残念ながら時間が足りなくて全部は終わらなかったんだけど、一度中身を読んでみたほうがいいと思うくらいよくできたデモだった。正直Redisの使い方とか参考になる所が大きい。
rase-/socket.io-workshop-full · GitHub

nkzawaがsocket.io workshop in Japanを何回かに分けて開いてくれることを期待しています!

nodeschool in japan

@maxogdenからnode.jsの初心者からbrowserifyを使ってみたい人、leveldbを使ってみたい人を対象にしたワークショップでした。
ちなみに世界中でNodeSchoolは開催されててNode.jsのユーザーを増やしてて素晴らしいと思った。
スタッフのみんなが翻訳してくれてて日本語でもワークショップ受けられるようになっているのでまだやってない方は是非やってみてください!!

翻訳へのコントリビュートも期待しています。


ちなみにworkshopはこんな感じでした!

基調講演 「Party」 by @rauchg

Partyっていうファイルアップロードを高速にするためのサービスの話でした。

これまでのファイルアップロードの問題点:
  • HTTPはファイルアップロードに適していない(ダウンロードは圧縮できるがアップロードは圧縮できない)
  • 本来はFTPを使うべきだがFTP複数TCPコネクションが必要だったりバイナリに圧縮するといった当たり前の機能がない
  • 途中でやめて再開するとかそういう機能がない
Solution : (JavaScriptでやる)
  • XHR Level2ならバイナリもサポートされててプログレスも分かる
  • ファイルをチャンクに分けてバラバラにアップロードすればより高速になる
  • WebWorkerを使えばUIスレッドを阻害しないので、(SPAで作っておいて)別ページに遷移してもアップロードが阻害されない。
  • もしも途中で接続が切れてもPartyを使っているアプリケーションは途中で転送を停止したりresumeしたりできる。

というわけでPartyがすごい。かなりファイルアップロードがしやすくなるだろうなと思いました。

Party触ってみたいっていう方はこのサイトに行けば触れます。

f:id:yosuke_furukawa:20141126001409p:plain

Party Demo

すごく大きなファイルをアップロードした時にChunkedにチェックつけるとつけない時と比較して数倍高速になる事がわかると思います。
また、Worker + ChunkedにチェックつけるとUIスレッドを防がずにアップロードが可能になります。

What’s coming in Node, Express & LoopBack by @ijroth

Nodeに何が起きてて、ExpressとLoopBackは今後どうなるのかという話。

  • v0.12 の後ではNodeForwardとかを含めて議論される
  • Nodeの未来ではBetter concurrency, flow control, runtime management, error handlingをやってくだろう
  • Expressはドキュメントが豊富に成り、scaffoldツールとかが含まれていく 5系が出るのはもうすぐ (多分12月頃)
  • LoopBackはExpressをさらに手厚くした感じのフルスタックフレームワーク、LoopBack Studioとか使うとさらに使いやすい統合開発環境が確保される
  • 他にも便利系ツールとして Zonenode-heapdumpの話をしてくれた。どちらもすごく便利なライブラリだった。

node.js for beginners, grunt gulp browserify webpack bower by @ahomu

フロントエンド向けのツールとしてのNode.jsを話してくれる人って言うことでスタッフから一番声が上がったahomuさんに白羽の矢を立てて話してもらいました。包括的にまとまった良い資料だと思います。こういうフロントエンド向けのツールとしては一番現実的な使い方が多いので、定期的に話を入れていきたい。

Node-v0.12のTLSを256倍使いこなす方法 by @jovi0608

  • Node.js v0.12から TLSが速くなった (libuv, openssl間でデータ共有、C++で処理を統一させることでJS呼び出しとC++呼び出しのオーバーヘッドを減らす)
  • Perfect Forward Securityに対応 ECDHE, DHEといった鍵交換方式が可能に
  • TLS Ticketがcluster対応
  • OCSP Stapling用のRequestイベントを受け取れるようになった
  • SPDY, HTTP2を速くなったSSLの上で実行することでより効果的に!!

めっちゃ面白かった。初心者向けの話ばかりだと食傷気味になるからもうガチで玄人向けの話してくださいっていうことを持ってったらしてくれたのがこの話。素晴らしすぎる。

すべてのノードトランスパイラーがひどい!ならば、ノードトランスパイラーをいかに改善できるか。 by @leichtgewicht

  • ノードトランスパイラーっていうタイトルだけど source to source変換をするもの全般をさしてtranpilerと呼んでいる様子
  • 基本的にtranspierは遅くて辛い、非同期前提にしてないから静的サイトジェネレータとかでもトランスパイルの時間が問題になることが多い
  • 設定が大体同じなんだから統一して欲しい。使い方はそろえないと辛い。
  • より良いトランスパイラーを作るためにbetter-compilerっていうコミュニティを作った

Node Past, Present, Future by @mikeal


  • Node.jsの過去から現在、そして未来へっていう話
  • Node.jsでProxy作る場合は昔はすごく長く書かないといけなかった
  • 現在はStreamができて短く書けるようになった
  • 最終的にrequestモジュールみたいにrequestからresponseまでを簡単にStreamで繋げるようになった
  • pipeを使って短く書こう
  • co/yieldとかを使うと非同期も同期っぽく書けるようになる、これが将来的な予想図

っていう話だった。みんな期待していそうなnode-forwardの話をぶっこんで聞いてみたけど、はぐらかされて終わった。
僕らはどういう方向に進むのか、Nodeのコミュニティがどういう風になるのかが聞き出せるとよかった >_<

テスト用ライブラリ power-assert, その開発で学んだ npm モジュール設計の勘所 by @t-wada

  • assertionライブラリが乱立してる。expect, shouldなど、、、
  • expectもshouldも構文を覚える必要があって辛い。APIが33個もあるとか
  • assertだったら構文はシンプルなんだけど、失敗した時に易しくない。
  • そこでpower-assert、power-assertならassertの時に失敗したら周辺の値を全て可視化してくれる
  • power-assertはeasyとsimpleのレイヤを分けて考えるという考え方に則って作られている

power-assertの基礎から設計原則に至るまで発表してもらった。非常に優れたツールで、僕ももうpower-assert以外でテストを書く気があまりしない。この前、ES6でpower-assertが書けるようにしたので、それはまた別な機会に話します。

ギャルでもゎかる node-webkit by @upgrade_ayp


  • node-webkitの基礎、node.jsでデスクトップアプリケーションが作れる
  • node-webkit vs atom-shell
  • Contextってなんだし
  • Context => ブラウザのコンテキストとNode.jsのコンテキストが両方存在してる。
  • いつのまにか境界をまたぐと訳がわからなくなる。
  • テストはpower-assertが神
  • socket.io使うとリアルタイムなやりとりもできるしマジもう神②

ちょっとサマリだと意味不明かもしれませんが、発表資料見ると面白いので是非上がったら見てみてください笑


この後でmaxogdenとascii faceについて話すぁゃぴさん。

Node.jsで操るKurentoメディアサーバー by @massie_g

  • メディアサーバーであるKurentoの話
  • WebRTCを使う場合、メディアサーバーが存在しないと多人数同士で同時にやりとりするのに制限がある (負荷次第だが5人くらいまでしか使えない)
  • メディアサーバーを使えば負荷がサーバーにも分散されるためある程度よいサーバーであればさらに沢山の人数とやりとりできる
  • Kurentoはこのメディアサーバーを構築するためのツール群。中身はCとC++、それをラップしたNode.jsがある様子。
  • Kurentoはスペイン語でStreamっていう意味。

https://cldup.com/pr2VXBQb9w-3000x3000.jpeg

個人的にはすごく興味があるWebRTCの話で面白そうだった。
KurentoOSSなのであとで試す。

LT

  • 新しい並行計算ライブラリjs-cspをご紹介 by @niryuu

https://cldup.com/zPAbaS2RPR-3000x3000.jpeg

世と宇宙の山の話 (js-cspっていう新しいcallback管理ライブラリの話)
新しい並行計算ライブラリ js-csp のご紹介

  • ド初心者が5000QPSの広告配信APIをNode.jsで構築したおはな死 by @zuqqhi2, @Jimisky

https://cldup.com/n0D2bk6Tr7-3000x3000.jpeg

アドテクの裏側をNode.jsで作るというすさまじい話
nodegrindっていうCPUプロファイラが便利そうだった

ド初心者が5000QPSの広告配信APIをNode.jsで構築したおはな死

  • node.js + socket.io + mongoDB で本格風リアルタイムWEBサイトを作ってみた by @ozatty96

https://cldup.com/QaQgPIbuoP-3000x3000.jpeg


チャット | NETROOM - ネットルーム - リアルタイムチャット掲示板

https://cldup.com/LCs8yQxwCe-3000x3000.jpeg


ビッグデータプリプロセスとしてnode.jsを使うというこれもまたすごい話。
NodeはシンプルプロセスなのでCPU一つだけの処理だとCPUを使い切れない
node.jsで細かくchild_processを使って並列に処理させる

  • A Profiling and Monitoring Method for Nodejs Applications by @setogit

https://cldup.com/6B2lvtLQbf-3000x3000.jpeg


Concurixっていうモニタリングサービスを使うとどういうモニタリングができるのかという話。

Concurix/cx-helloworld-readme-ja · GitHub

謝辞

本当にスタッフの力とスピーカーの皆のコンテンツがなかったらここまで面白いカンファレンス開催できなかった。
来てくれた皆さん含めてありがとうございました!来年も頑張ります!!!

nodeconf.eu 3日目 (mad science, leveldb, strongloop)

「英語すごいできるんですね。」みたいなコメントが有ったんですが、できないです。
相手が伝えている情報のうち、分かる単語から情報を拾って、確認するっていう、3 way handshake みたいな確認を逐一しながら補完しているだけです。要は気合です気合。

向こうの人は優しくて、こっちがネイティブじゃないんだってわかるとゆっくり喋ってくれたり簡単な言葉に直してくれるので楽ですね。

さてさて最終日も濃かったnodeconf.euの話をしていきます。

mad science act track

このtrackでは、今までの「node.jsの運用」とか「microservicesで今後のnode.jsがどうこう」というよりもnode.jsのcutting edgeを走るsubstack, feross, dominic tarr, mikolaの話を聞いて彼らが普段何を考えてるのか、どんなものを作っているのか聞くトラック。彼らはNode.jsで普通のサーバーを作るような人たちじゃなくて、誤解を恐れずに言うと、変態的な事(普通はやらない事)をNode.jsで実現しちゃう人たち。マッドサイエンスってことでみんな白衣着ながら発表してた。

mad scientist act by @substack

substackが語る、streamによってレゴブロックみたいに組み合わせて全体のアプリケーションを作ることができるという話。実例ということでライブコーディングしてくれてて、calendarを出すコマンドラインツールを作ってそれをansi-color-streamを使って色付けしてくれたり、簡単なhello worldのwebアプリをecstaticlevelroutesを使ってRESTっぽいアプリに仕上げたりしていた。

最終的には、それらを組み合わせてjenkinsみたいなリモートバッチ処理サーバーをbatchdb-shellbatchdb-web-apibatchdb-web-uiを使って実現していた。オンラインデモでここまでやってくれてすごかった。

WebRTC Mad Science by @feross

これもかなりすごかった。WebRTCを使って変態的な事をする話。話は2つあって、

1. WebRTCでP2Ptorrentでファイル共有を実現しようとしている話 (WebTorrent)
2. WebRTCでP2Pの仕組みを利用してn対nでホワイトボード共有する話

BitTorrentのネットワークとWebTorrentのネットワークを変換するトランスレータがあればBitTorrentとWebTorrentを結びつけることができる。instant.ioを使ってそういうP2Pをウェブでも実現しようとしているという話だった。

動画でホワイトボード共有しているところは撮ったので見てみてください。


Realtime multiplayer games on the Web by @MikolaLysenko

リアルタイムゲームを作る時に気をつけなきゃいけないことの話。作っているリアルタイムゲームの質が違うんだと思うけど、かなり高度な事を語っていて、知らない話が多かった。

IoT track

node.jsの醍醐味の一つであるハードウェアトラック。
ロボットを作る話とかが多くて、やっぱりデモ映えしててよかった。

みんなlittleBitsに夢中で僕もlittleBits触らせてもらったけどすごく面白かった。

一番ウケたのはnodecopterをダンス・ダンス・レボリューションのマットでGangnum styleにあわせて操作するっていうやつ。ちょっとこれは感動するくらいすごかった。デモ動画を貼っておくので見てみてください。


loopback workshop

StrongLoopが作ってるサーバーサイド、クライアントサイドの統合フレームワークであるloopbackのworkshop。




leveldb workshop

一昔前はmongodbがnode.jsのデータストアとして使われやすかったんですけど、最近はleveldbでやることが多くなっているようで、workshop開催してた。

所謂nodeschool形式でワークショップ。levelmeupっていうツール使ってみんなチュートリアルを実施してた。

$ npm install levelmeup -g
$ levelmeup

(loopbackの方にばかり注力してて、こっち聞き忘れてはじめの方までしかできなかった。。)

懇親会にて

TJ Fontaine へのインタビュー ver.2 (node.js gate keeper)

Q. Node.js のロードマップを知りたい。 v1.0が出た後どうするの?
A. まずいくつか整理しないといけなくて、v1.0が出るまでは全てを安定させる必要があるなと思ってる。あと、v2.0に向けてはもっとbreaking changesもやろうと思う。特に標準化への対応をしたいと思ってて、EcmaScript6の標準とかを取り入れていきたいと思ってるよ。

Q. おお、たとえばCommonJSスタイルからES6のモジュールロードスタイルに変更しようと思ってるの?
A. それは当然の疑問だろうけど、難しい問題が多そうだと思ってる。既にあるライブラリがCommonJSで書かれてるから完全に移行は難しいだろうね。ちょっと整理が必要だと思ってるよ。

Q. あと、どこかでdomainsをdeprecatedにしたいって言ってたよね?
A. そう。domainとclusterはどこかでdeprecatedにしたいと思ってる。

Q. え!clusterも?
A. clusterの場合はAPIが良くないと思ってるから、APIをdeprecatedにして、書き換えるだけで、完全に消すことはしないけどね。

Q. domainsはどう思うの?
A. domainは消せると思ってる。解決してる問題が少ないし、本番で使っている人を知らないから消せるなら消したい。

Q. 重ねていうけど、日本に来れたら調整するから来てよ。11/15ね。
A. ああ、行きたいんだけどスケジュールがつかめないんだよね。ホントごめん。

Q. まぁでも会えて良かったよ。11月じゃなくても開くから来れる時があったら教えて!
A. sure, sure!


bajtosへのインタビュー(loopbackのコミッター)

Q. Loopbackめっちゃ良かった。これは日本でも沢山の人に教えたい。
A. Thanks!

Q. StrongLoopってこの前expressのメンテナーになったよね。
A. そうそう。

Q. expressを今後どうしていこうと思ってる?
A. 基本的にはあまり変える気はないけど、APIに整理が必要だと思ってるのと、もっとドキュメントを書きたいと思ってるから当面は内部のリファクタリングとドキュメントの整理かな。

Q. koaとかはどう思ってるの?
A. koaは良いプロダクトだと思ってるから、expressに学べる所があれば持ってこようと思ってるよ。

Q. ところでloopbackは今後どうするの?これってexpress, yeoman, angularをすごく効率的に使えるようにした良いプロダクトだと思ってるんだけど、これ以上に足す所ある?
A. もっとクライアントを増やしたくて、angularだけじゃなくて他のものも使えるようにしたいと思ってるし、あとIsomorphicなデザインにしたいと思ってるよ。

Q. それはおもしろい!僕はIsomorphicにすごく注目してて、airbnbが作ってるRendr以外の新しいやつが出てきたら多分使うよ。
A. COOL! 日本でもloopback使ってくれる人増えるの期待してるよ。


mikolaへのインタビュー

Q. リアルタイムゲーム作ってるんだね。実は僕も作ってるんだよ。僕たちは2つのアプローチで今はリアルタイムゲームを作ってて、一つはSocket.IOを使ってるんだ。Socket.IOでwsが繋がらなかったらlong pollingにするようにしてるんだけど、mikolaの奴はpure websocketで作ってたよね?Socket.IOとかを使わなかったのは何故?
A. 僕のゲームはシューティングゲームみたいなアクションが多くてリアルタイム性が強いやつだからlong pollingだと向かないんだよね。Socket.IOとかはchatみたいなやつなら向くと思うんだけど本格的なリアルタイムゲームを作ろうと思ったらlong pollingへのfallbackは不要だよね。

Q. なるほど。もう一個アプローチがあって、サーバーでロジックを全部計算して、クライアントは表示するだけでほとんどロジックを持たない方法を使ってて、Socket.IOがポケモンでやってたようなやり方でゲームを作ってるんだけどどう思うかな?
A. うーん、レイテンシーが問題かなぁ。もしもレイテンシーがそんなにないなら良さそうだね。

Q. ちなみにWebRTCとかはどう思う?
A. 新しいプロトコルとして注目してるよ。今はWebGL & WebRTCに注目しててHTML5でゲーム作るなら今後必要になるだろうなと思ってる。

Q. これはビジネス的な話なんだけど、HTML5ゲームってmikolaの国では流行ってる?日本ってあまり流行ってなくて、みんなネイティブのiOSアプリとかでゲームやってるんだけど。
A. 実はあんまり需要があるのかどうかはよく知らないんだよね。僕はまだ学生(Ph.D)で、アカデミックな研究として作っているだけだから。

Q. (mikola) ちなみに流行ってないのはどういう理由なの?
A. (yosuke_furukawa)うーん、これは僕もはっきりした理由はわかってないんだけど、やっぱりちょっと遅いからじゃないかなぁ。ブラウザではそこまでゲームやらないって人も多いし。
A. (mikola) そしたらWebGLがやっぱり重要なキーになりそうだね。WebGLではグラフィック操作は速くなるし。

ちなみになんでナプキン載せてるかって言うと、Dominic Tarrが何故かナプキンを頭に巻き始めてから急にみんなに流行ったのです。

Dominic Tarrへのインタビュー (stream master)

Q. dominic、この前リポジトリ見たら、変なバージョニング提案してたよね?確かSentimental Versioningだっけ?あれ面白いね。
A. it's just joke! hahaha!

Q. バージョニングの問題どう思う?
A. まぁSemantic Versioningは考えなおすフェーズに来てるし、実際Isaacsも考え直し始めてるんじゃないかな。

(あんまりしゃべれなかった。。。)

ちなみにdominic tarrはマジでイケメンだった。Guilleも相当イケメンだけど、方向性が違うイケメンってかんじだ。

Paul (V8, IRHydra committer)

Q. (DartのロゴがMacについてたから) おおDartだ!Dartやってるの?
A. ああやってるよ!IRHydraやってるよ。

Q. おお!IRHydraすごく熱いプロダクトだと思っているんだけど、実はよく知らないんだよね。そもそもIRHydraってどういう事ができるの?
A. IRHydraはv8がやってる最適化を可視化して、どこで問題が起きてるのか特定してくれるんだ。@mraleph (v8の内部構造に詳しすぎる神) が作ってるんだよ。

Q. IRHydraってなんでDartで書かれてるの?
A. Dartって元々V8を作ってたチームが作ってるから、内部構造詳しい人(@mralephなど)が多いんだよね。

Q. IRHydraすごそう!もっと知りたいんだけど、デモサイトだけだとどう見たらいいのか分からなかったんだよね。。
A. (paul) 僕がまとめた資料あるんだけど、あんまり詳しくないけどいる?
A. (yosuke_furukawa) もちろん!!いるいる!!日本のエンジニアのおみやげにする!
A. (paul) ちなみにmralephは日本のアニメ好きだから呼んだら来るんじゃないかな (@mralephはConstさんっぽい人らしい)

この後、ちょっとだけIRHydraのデモしてくれたんだけど、IRHydraの何が良いのか知るためにはv8の内部構造に多少詳しくないと無理ゲーっぽい感じだった。

詳しい資料をもらえたので貼っておきます。

Understanding V8 and JIT compilation basics - Google スライド


かいつまんで理解した内容を話すと

  • v8の最適化はものすごく高度な処理
  • 関数呼び出しを実行している間に型が決定されるとその型に合わせて最適なメモリ処理がなされる
  • 例えば整数型と文字列型だと最適なメモリ配置が違う
  • 関数が整数も文字列も受け付けるような関数だと最適化がうまく働かない
  • 一番悪いのはv8が整数で関数呼び出しを最適化してしまった後で文字列が来たりするとその時点で最適化やり直しになってしまい、コストがかかる
  • IRHydraはこういう最適化が働かない箇所を可視化して教えてくれる。すごい!

Cian (nodeconf.eu organizer)

Q. cian、最後の話すごく良かった。ほんとうに来てよかったよ。貴方を尊敬するし、自分のカンファレンスにも参考にしたいと思う。
A. ありがとう、本当来てくれて嬉しいよ。もしも何か困ったことあったら教えてくれ、いつでもアドバイスするよ。

Q. ちなみに今までやってて一番辛かったのってどんなこと?
A. 一番大変だったのは一番最初のnodeconf.euかなー。当時はnode dublinっていう名前だったんだけど、node.js自身がまだ無名だったし、そんなにヨーロッパですら有名じゃなくてスポンサーとかも集めるの大変だった。後はスピーカーも誰呼んでいいかわからなかったし。いまはだいぶ楽になったよ。

Q. 僕が日本語でまとめているから、日本のデベロッパーがすごくnodeconf.euに興味持ってるよ。来年はもっとたくさん来るかも。
A. (ハイタッチしながら)やったー!!日本人にはもっと来てほしい!!

Q. 来年もまた絶対来るよ!
A. ☆-(ノ゚Д゚)八(゚Д゚ )ノイエーイ (再びハイタッチ)


最終日なので総括

mikealがnodeconfを始めたのは2010年。そこから今まで4年間、node.jsはサーバーサイドもフロントサイドツールもすごく発展してきていて、この発展にはコミュニティを盛り上げてるmikealやcianの活躍が根本にあることを忘れちゃいけないと思った。

コミュニティが活性化することでそれを採用する企業が増えて、さらに開発者を生み、イノベーションが生まれて、発表者・参加者が増えるっていう好循環を生んでいると思う。実際にCianがnode dublinを始めたのは2011年頃ということだけど、その頃のアイルランドでは誰も知らなかったNode.jsが、そこからCianのおかげでアイルランドイングランド、イタリア、ドイツ、スウェーデンとかの周辺国でもnode.jsが盛んになってきている。

こういう好循環はmesoさんとかJxckとかその他スタッフがずっとやっていたおかげで、日本でも少しずつ起きている。一時期ほどは落ち着きをみせてるとは言っても、二ヶ月に一度Node.jsでトークしてくれる人たちは増えてきているし、Qiitaとかブログの記事でも色んな情報がNode.jsの話を通して増えてきていると思う。

ただこういう好循環がまだ足りていないことは自覚していて、もっと好循環は起こしていかなきゃいけないし、そのためには僕がこういう世界のNode.jsのコミュニティに積極的に顔を売っていって情報を仕入れて日本と世界の情報格差を減らしていけると良いと思う。

あと今回の出張で得たカンファレンスの運用方法は次の東京Node学園祭でも活かそうと思っているので、期待してください。

さいごに、今回のnodeconf.euの出張報告会はどこかでやりたいと思っていますが、弊社の会場、9月はもう勉強会するスケジュール的余裕が無いので、どこか貸しても良いという場所があったら教えてください!

たくさん友達が作れてよかった。みんなも世界のカンファレンスには参加すると良いと思います!