async.waterfallとneo-async.angelFallの話
今回登場するモジュールはこちらです。
簡単に説明すると、Node.jsにおいて、callback地獄とif (err) return callback(err);
の多用を軽減して読みやすくするためのモジュールです。
async.waterfallについて
async.waterfallは、非同期な処理を逐次行っていく時に、メインに使うメソッドです。
使い方
var async = require('async'); // or 'neo-async' async.waterfall([ function(next) { next(null, 1); }, function(result, next) { // result に1が入る。 next(); } ], function(err) { // 途中でnext(err)すると、ここに飛んでerrが入る。 }
問題点
次のように、第2引数を返したり返さなかったりするメソッドがあったときに、その次の関数の受け方が定まらなくて、よくバグの原因になる。
function anAsyncMethod(foo, callback) { if (foo) { callback(null, 1); // [A] } else { callback(null); // [B] } } async.waterfall([ function(next) { var foo = false; anAsyncMethod(foo, next); }, function(result, next) { // [A] の場合はresultに値(1)が入るが、 // [B] の場合は入らない。(すなわち、resultにnextと呼ぶべき関数が入っている) next(); // TypeError: next is not a function } ], function(err) { });
回避方法はある(後述)ものの、面倒だし、
「値を返していない状態から、返すように変更する」ことがbreaking changeになるのも辛い。
同期関数だったり、非同期コールバックスタイルでも普通の使い方であれば、そうはならないのに。
回避方法
anAsyncMethodがコールバックへ引数渡してきたり渡さなかったりするから困るなー、でもanAsyncMethod自体は書き換えられないな、という時は、次のように回避する。
async.waterfall([ function(next) { var foo = false; anAsyncMethod(foo, function(err, result) { next(err, result); // 1つ渡すことを強制している }); }, function(result, next) { // ...
neo-async.angelFall
waterfallと似ているが、上述の問題が解決されたもの。
常に最後の引数にnext(と呼ぶべき関数)を渡す。
すなわち、関数の仮引数を、単に(next)
と書けば、前の関数が何を渡そうが無視されて、nextに関数が入る。
もし(result, next)
と書けば、仮に前の関数が何も渡してなければresultにundefinedを入れ、nextに関数が入る。
var async = require('neo-async'); async.angelFall([ function(next) { var foo = false; anAsyncMethod(foo, next); }, function(result, next) { // 定義してる引数の個数が見られ、 // 2つなので、1つめに前の関数から渡される結果、2つめにnextが入る。 next(); } ], function(err) { });
前回の記事で紹介したFunction.length
を利用して実現されている。
注意点
async.angelFall([ function(next) { var foo = false; anAsyncMethod(foo, next); }, function(result) { // nextを呼ばないから仮引数としても定義したくないよ、というとき。(実際あり得る) // 仮引数を1つしか定義していないので、ここにnextと呼ぶべき関数が入ってしまい、 // 期待する値(result)が入ってこない。
他の解決案
waterfallのタスク関数はfunction(foo, next)
ではなく、function(next, foo)
の順で取るように仕様変更するという話。
これをすれば、fooがこようがこまいが、nextの位置はずれないので上のような問題は起きない。
実際自分もこれは1年半前くらいに思ったのだけれど、Node.jsの「コールバック関数は最後にとる」という規約に反するので少し気持ち悪い。でも黒魔術しないですむし、async.waterfallに限って言えばこれもありだなぁと思う。
まとめ
Node.jsがcallbackを最後にとるというルールが、思わぬ形で影響を及ぼしてしまっている話でした。
Function#length と、そのlodashにおける使用例
JavaScriptにおいて、関数の中でarguments.length
は良く目にする/使うと思うのですが、関数fn自体のfn.length
を利用することはまれではないでしょうか。
(これ以降fnは全て、定義された関数を表すことにします)
async.angelFall
を社内に布教しようと思ったのですが、そういえば前のエントリで言及しかけていたので、どうせならfn.length
の話題とまとめてブログに書こうと思った次第。
fn.lengthとは
Function.length - JavaScript | MDN
arguments.length
が関数が実際に呼ばれた時の引数の個数なのに対して、fn.length
はその関数fnが定義されたときの仮引数の個数を表します。
var fn = function(a, b, c) {}; fn.length // 3
Function#bindで第2引数以降を渡すといわゆるカリー化ができますが、きちんとこのlengthの値も変わったりします。頭良いですね。
var fn = function(a, b, c) {}; // 引数aに1を部分適用した新しい関数を作る var fn_curried = fn.bind(null, 1); // b, cの2つの引数をとる関数になっている fn_curried.length // 2
使用例
コイツの使用例をいくつか紹介したいと思います。
lodash
まずは大御所lodashにおける使用例です。関連するissueはコチラ。(読まなくても下で全部説明しますが、読んで分かったら多分このエントリにそれ以上のことは無いです。)
https://github.com/lodash/lodash/issues/944
https://github.com/lodash/lodash/issues/997
さて、この問題の背景としていくつか説明する必要があります。
iterateeに渡る引数
まず、lodashのeachやmapなど(多数)のメソッドに第2引数として渡す、関数(lodashではiterateeと呼ばれています)は、次のように引数を3つ取ることが出来ます。
var arr = ['a', 'b', 'c']; _.map(arr, function(value, index, array) { // value: 配列の値。 順に 'a'/'b'/'c' が入る // index: 配列のindex。 順に 0/1/2が入る // array: 配列全体。 ここでは毎度 ['a', 'b', 'c'] が入ってくる });
これはJavaScriptのArray#forEach
などと同じ仕様です。
lodashのメソッドチェーン
次に、lodashにはメソッドチェーンをする書き方があります。例としてはこんな感じ。(少し煮え切らない例ですが)
var arr = [1, 2, 3, 4, 5]; var result = _(arr) // _.chain(arr)でも同じ .map(function(value, index, array) { return value * value; // 2乗 }) .filter(function(value, index, array) { return value % 2 === 1; // 奇数 }) .take(2) // 配列の最初から2つを取得 .value(); // 実際の結果を取得 result // [1, 9]
lodash v2までは、単に順番に _.map
, _.filter
, _.take
を適用していくだけでした。すなわち、以下と基本同じです。
var arr = [1, 2, 3, 4, 5]; var mapped = _.map(arr, function(value, index, array) { return value * value; }); var filtered = _.filter(mapped, function(value, index, array) { return value % 2 === 1; }); var result = _.take(filtered, 2); result // [1, 9]
lodashの遅延評価
しかしlodash v3で遅延評価(lazy evaluation)が登場したことで少し話は変わります。
上記の例でいうと、配列をまずmapして[1, 4, 9, 16, 25]
を得て、その後filterして[1, 9, 25]
を得たうえで、takeで頭2つを取って[1, 9]
を得ています。しかしながら、最初から「頭2つを取る」とわかっていれば、全て計算しなくとも、map・filterを手前からしていって[1, 9]
まで得られた時点で打ち切っても良いことになります。
メソッドチェーンの方法で書けば、文として切れ目がないので、必ずしも[1, 4, 9, 16, 25]
とか[1, 9, 25]
という結果を得る必要はないわけです。そこで、メソッドチェーンの書き方をされた時に、可能であれば遅延評価をするような実装がなされました。
一方、後者の例ではmappedやfilteredという変数に一旦代入しているため、当然結果を計算する必要があります。(残念ながら、JSのエンジンレベルの遅延評価はありませんから・・)。これがlodash v3で実装された遅延評価の優位性です。
ちなみに、遅延評価時の実行の流れは大体こんな感じです。map=>filter=>takeの時点では、内部的に状態を持つだけで、計算はしません。そして、value()が実行されたタイミングで、以下のような手順で計算されます。
arr[0]
である1
をmap(2乗)する。1
。- 結果の
1
にfilter(奇数)の関数をかける。true
。 true
なので、結果の配列にpushする。[1]
。※take 2なのでまだおわらない。arr[1]
である2
をmapする。4
。- 結果の
4
にfilterの関数をかける。false
。 false
なのでなにもしない。arr[2]
である3
をmapする。9
。- 結果の
9
にfilterの関数をかける。true
。 true
なので、結果の配列にpushする。[1, 9]
。 take 2なのでここで終了。[1, 9]
を返す。
これにより、頭3つ分しか計算せずにすんでいます。
普通は1回ずつ配列を生成して次に進むのに対して、
遅延評価が効く場合は、配列の要素1つずつがchainを通り抜けていくイメージですね。
さて、これで背景の説明は終わりです。すでにお気づきでしょうか?
遅延評価で生じた、iteratee第2・第3引数の問題
map=>filter=>takeの真ん中、 filter
に渡したiterateeの第3引数arrayに注目してみます。
本来ならばここには、直前でmapした結果である [1, 4, 9, 16, 25]
が入るのが正しいはずです。
しかし、上の遅延評価の動作を見れば分かるように、mapした全体の結果 [1, 4, 9, 16, 25]
は作っていません。ということは、第3引数のarrayには正しい値が渡ってこないことになります。(作ってないものは渡しようがない!)
普段はこの第3引数などまず使いませんが、_.uniqを遅延評価に対応させるPRを送ろうと実装をしていてたまたまこのことに気付き、issueを立てました。(拙い英語ですが)
その後、もう片方のissueで、第2引数のindexも正しさが保証されないことがわかりました(より重要な問題)。
fn.lengthを用いて修正される
これに対して、jdalton 氏が機転の利いた対応を行いました。
上の例では、遅延評価してしまうと、そもそもiterateeに第2引数のindexや第3引数のarrayを正しく渡すことができないのです。しかし、ほとんどの用途では第1引数のvalueしかとらず、そのときは問題ない。第2引数以降を渡さなければいけないケースだけ、遅延評価をとりやめれば良いのです。すなわち、iterateeの仮引数の数を見れば良い。それがこのコミットです。
iteratee.length > 1
のとき、isLazy
がfalse
になっているのがわかります。
これを見た当時、fn.lengthはこうやって使えるのかと、心の底から感心しました。自分がissueを立てた時点ではこの方法は全く思いつかなかったです。
以上、lodashにおけるfn.lengthの利用例の紹介でした。(長かった。)
fn.lengthを利用する潜在的な問題
次の2つのコードの違いはなんでしょうか。
var fn = function(a) { return a; }
var fn = function() { return arguments[0]; }
答えはもちろん「fn.lengthの値が違う」です。(他にもfn.toString()の結果が違うなどありますが)
fn.lengthはあくまで仮引数の個数なのですが、arguments
を使えば、仮引数として宣言されていなくてもその引数にアクセスすることは可能です。
lodashの例でいけば、仮引数を宣言せずにiteratee内で第2・第3引数にarguments[1]
arguments[2]
などとアクセスした場合には結局、期待された値が入らないことがあります。
この問題はfn.lengthを使うと常に潜在してしまいます。
最後に
普通にプログラミングしてても絶対使わないようなものも、ライブラリ書くときにはたまに出くわすよね、という良い例だと思います。実際のプロジェクトでも、基盤的な機能の整備をするときには色々知っておくと便利そうです。
あと、lodash は結構面白いなと思います。
async.angelFallの話を書こうとしてたはずなんですがね。次で書く。
LT「チーム開発においてNode未経験者の学習コストを下げるための工夫」の補足
先日、東京Node学園18時限目「Node.js 4.0の話」の回で、LTをさせていただきました。
Node学園 18時限目 Node.js v4.0の話 - connpass
資料はこちらです。
たかが10分といえど、120人*10分=20人時間というプレッシャー。勉強会等で発表するのはこれが初めてで、この1週間はひたすらこのLTのことだけを考えて生活していたような。
テーマは初めに思い切って設定したものの、まずはこの1年の開発を振り返り何がポイントだったのかを考えるところから・・・、
そこからプロットを作って話す練習をしてみると、どうも話が繋っていなかったり、技術的な話が少なすぎて、これじゃNodeの会合に来ているお客さんに申し訳ないよなぁ、というような内容だったりと、内容をまとめ上げるのに苦心しました。
そんな苦労もあった準備の甲斐もあり、良い反応も頂き、ひとまずホッとしている次第です。
これはホントに会社に持ち帰りたい話 #tng18
— fnwiya (@fnwiya) October 8, 2015
いい話だった #tng18
— Kohei Asai (@axross_) October 8, 2015
会場にいらした皆様、ご清聴ありがとうございました。
さて、資料には一応載せたけれど、発表時にもスライド上でも十分説明できていない点について、このエントリで少し補足をさせていただきたいと思います。
lodashのバージョンについて
lodashはv2.4.1でしばらくの間リリースが止まっていましたが、約1年前くらいにv3がリリース、そしてそろそろv4がリリースされそうです。
特徴として、v2 => v3, v3 => v4 でのbreakingな変更が割と多いです。運用に入ってしまうとメジャーバージョンを挙げるのは結構大変になってしまうイメージです。
資料にちらっと書きましたが、いまから開発始めるなら、v4が出るまで(出てからしばらくまで)、依存先をedge(github上のmasterの最新)にしておくのが良いんじゃないかなと思っています。
v4の特徴としては、今まで1つの関数にいくつかの引数の渡し方ができていたところを、関数自体を分けるように変更がなされます。
例えば_.uniq
という関数は、コレクションを渡すと重複する値を1つだけにした配列を返しますが、この_.uniq
の引数に関数を与えると、その関数を通した結果に対して、uniqをかける、という仕様でした。
これが、_.uniq
と_.uniqBy
という2つの関数にわかれ、関数を渡したければ後者を使うことになります。
このように、シンプルな方向に倒していくのがlodashの良い所の一つであります。
その他、aliasを大量に削除するようです。underscoreにあったメソッドに対して、lodash v3で別の名前をつけておいて、元の名前にはaliasを貼っていたが、v4でそのaliasを削除するなんてケースも。。そこまでやるか・・??(lodashの作者jdaltonとunderscoreの作者jashkenasは仲悪いです)
なお、バグはそれなりにあります。中規模くらいでガッツリ使っていると出くわすこともあると思います。特にv3の初期は、遅延評価まわりのバグが結構ありました。issueを軽く追っておいたほうが良いです。
async#waterfallとneo-async#angelFallの話
ここらへんは、書き方オタクとしては結構こだわりのあるところで、キチンと書いておきたい話なのですがまた今度の機会にします><
lodashのクエリの書き方
オブジェクトが並ぶ配列arr中から、プロパティxの値が2であるものを見つけるとき。
_.find(arr, function(obj) { return obj.x === 2; })
のがunderscoreからある書き方ですが、これを
_.find(arr, { x: 2 })
という書き方ができます。さらに別の書き方として、
_.find(arr, 'x', 2)
という書き方もあります。面白いですね。
個人的には2つめの_.find(arr, { x: 2 })
が良いかなと思っています。
3つめにしない理由は、例えば「xが2, yが3のものを探す」ときに_.find(arr, {x: 2, y: 3})
という書き方ができますが、3番目の書き方だと応用が効かなくなってしまうからです。
余談ですが、lodashあるあるで、_.includes
を使うべき場面で間違って_.some
を使ってしまう、というのがあります。
前者は単なる厳密な比較で含まれていればtrueを返す、後者は上の_.find
と同じ感じで、見つかったらtrueを返す。
あるオブジェクトそのものがある配列に入っているかを調べたく、つまり_.includes(arr, obj)
としたいところで、
間違えて_.some(arr, obj)
なんて書くと、オブジェクト同士のプロパティを1つずつ(しかもdeepに)比較しはじめてしまう。
大抵の場合で結果が一致してしまうのでテスト書いてもバグに気づきにくい。
結果、思わぬところでパフォーマンスの問題を引き起こしてしまうかもしれませんね。
何度も見かけた事例ですが、lodashオタクならコードレビューで間違いに気付くことが出来ますw
undefinedチェックの話
LTで最も反応が良かった、undefinedチェックの話です。
資料中では以下の4点の方法を挙げています。
x === undefined
x === void 0
typeof x === 'undefined'
_.isUndefined(x)
undefined が変数に入れられたらどうするとか考えたくもないから x === undefined 派閥 #tng18
— Yosuke FURUKAWA (@yosuke_furukawa) October 8, 2015
最近、比較にvoid 0 使ってなんですかこれって言われた件 #tng18
— れい (@rei_m) October 8, 2015
ちなみにLT中で会場の方にどれを使うか挙手でアンケートをとらせていただきましたが、大体挙手の比率は 6:4:10:1 くらいだった印象です。
さて、自分はtypeof x === 'undefined'
が良くないと考えている旨を話しました。その点を整理したいと思います。
typeof x === 'undefined'だとReferenceErrorを握りつぶすことがあるのか… #tng18
— Toshiya SAITOH (@stoshiya) October 8, 2015
まず、typeof x === 'undefined'
は1つのJavaScriptのイディオムです。この書き方をすると、もし仮にxがそもそも変数として定義されていない場合でも、ReferenceErrorを吐くことがなく、上の式の結果はtrueになります。ブラウザ上で、特にグローバルに変数xが宣言されているかどうかをチェックする場合などに有用であることから、よく使われます。ReferenceErrorを吐かないから安全、という言われ方をすることもあります。
しかしながら、Node.jsではグローバル変数をチェックすることはまずありません。そのときに、「ReferenceErrorを吐かない」ことが「安全」なのか?という疑問が生じます。
例えば変数名をtypoしてしまった。そのときはReferenceErrorを吐くのがむしろ正しい動作で、そうならないということは、吐かれるべきエラーを握りつぶしていることに他ならないわけで、それによりミスに気付く機会を逸するかもしれません。"良くない書き方"であると考えるのはその点です。
さて、書き方をどれにするかでいうと、少し抵抗ある方もいるかもしれませんが、今時は x === undefined
で問題ないと言ってよいでしょう。
一応var undefined = 1;
されることもあるよ(だけどそんなことする人居ないから気にしないよ)と話しましたが、
Node 4.xで'use strong;'
するとそれもできなくなるそうです。
それ StrongScript だったら var undefined = 1; 防げるよ!!! #tng18
— Yosuke FURUKAWA (@yosuke_furukawa) October 8, 2015
というわけで、一番シンプルなx === undefined
で良いんじゃないでしょうか。
書き方オタクが複数人いたら?
書き方オタクが複数人いてまとまらない話とか無いのかな #tng18
— のびすけ/S.Ryousuke (@n0bisuke) October 8, 2015
これに対する、自分の解決策は以下です。
- 最終決定をする人(リーダー)は1人にする。
- 「書き方が統一されている」ことが最重要であり、 全ての書き方オタクはそれを肝に銘じている。
1の方は、書き方に限った話ではないでしょう。とにかく決定する人が2人いると面倒この上ありません。成功事例とした開発も、初期にダブルリーダーのような状態になったときは良くなかった。
2の方は、私のLTで極意の3つめとして話しているものです。これは他をさしおいてなによりも重要だと私は考えています。
良い実例があります。
僕がいっしょに仕事した中での代表的な書き方オタクに、neo-asyncという人物(?)がいます。ちょうど僕の前にLTを行った人です。
neo-async
LT 「Neo-Async」
僕と彼はしょっちゅう書き方について議論をします。その結果、50%は最終的に意見が合致しますが、意見が合わずに終わることも50%あります。
例えば、この記事の上のほうでlodashのクエリの話をしていますが、彼は3つめの_.find(arr, 'x', 2)
という書き方が良いと言います。彼は何か話すときは常にベンチマークを取っており、確かこの件ではそれのほうが2倍くらい速く、わざわざ速い書き方があるのでそれを放棄する必要もなかろう、という主張でした。
私はというと、どうせ2つめの_.find(arr, { x: 2 })
の書き方は覚えなければいけないので、(_.find(arr, { x: 2, y: 1 })
のパターンがあるから)、学習コストを減らしたいので2つめで統一したい。
どちらが正しいということはないと思います。
それ以外にも本当下らないことでしょっちゅう言い合っています。_.each
か_.forEach
かなど、自分の会話でなければ即座に「わりとどうでもいい」のAAを貼ってやりたいくらいです。
以前同じチームで仕事したときはたまたま、私がもともといるチームに彼が助っ人的に入ってくれていたこともあり、結果的には最終的には私の意見をほぼ100%通す形になってしまいました。もちろん彼の中では、自分が良いと思う書き方が出来ない不満はあったのではないかと思います。コードレビューでは、"殺してでも うばいとる"ではなく"ゆずってくれ たのむ!"にしたいゆえんですね(LT資料の終わりの方を参照されたし)。
もちろん意見は言ってくれますが、これ以上はその人の考え方あるいは気分だろうな、というところで、それに合わせて修正してくれていました。それはつまるところ、合わせるのが最も大事だと考えているからです。逆に彼のチームに私が入る形だったら、私のほうが合わせることを意識したと思います。(彼はカナダに英語勉強の旅に出てしまうので、少なくとも当分一緒に仕事する機会がないのは残念ですが。)
あとは、あまりロジカルでない主張をむりやり通すと良くなくて(そういうことも実際あった)、個人的にはわりと細かいことでもちゃんと話すのが良いのかなと思っています。ちょっと大変ですが、書き方がバラバラになるというリスクはそれほど大きいということです。
async書き方比較のソースコード
asyncの書き方の例を3つ挙げて比較するスライドを入れましたが、文字の情報にするためにこちらに載せておこうと思います。
準備
var async = require('neo-async'); var getFoo = function(callback) { return callback(null, { x: 1 }); }; var getBar = function(x, callback) { return callback(null, x + 1); }; var doBaz = function(callback) { return callback(null, 'piyo'); }; var doQux = function(foo, bar, callback) { return callback(null, 'baz'); };
plain (callback hell)
var ex_plain = function(callback) { // fooを取得する. getFoo(function(err, foo) { if (err) { return callback(err); } // fooをごにょごにょする. foo.hoge = 1; foo.fuga = 2; // barを取得する. getBar(foo.x, function(err, bar) { if (err) { return callback(err); } doBaz(function(err, result) { if (err) { return callback(err); } foo.piyo = result; doQux(foo, bar, function(err, baz) { if (err) { return callback(err); } // ... callback(null, { foo: foo, bar: bar, baz: baz}); }); }); }); }); };
async.series (弊社で多い)
var ex_series = function(callback) { var foo, bar, baz; async.series([ function(next) { // fooを取得する. getFoo(function(err, _foo) { if (err) { return next(err); } foo = _foo; // fooをごにょごにょする. foo.hoge = 1; foo.fuga = 2; next(); }); }, function(next) { // barを取得する. getBar(foo.x, function(err, _bar) { if (err) { return next(err); } bar = _bar; next(); }); }, function(next) { doBaz(function(err, result) { if (err) { return next(err); } foo.piyo = result; next(); }); }, function(next) { doQux(foo, bar, function(err, _baz) { if (err) { return callback(err); } // ... baz = _baz; next(); }); }, ], function(err) { if (err) { return callback(err); } callback(null, { foo: foo, bar: bar, baz: baz }); }); };
async.waterfall 1 (推奨)
var ex_waterfall = function(callback) { var foo, bar; async.waterfall([ function(next) { // fooを取得する. getFoo(next); }, function(_foo, next) { foo = _foo; // fooをごにょごにょする. foo.hoge = 1; foo.fuga = 2; // barを取得する. getBar(foo.x, next); }, function(_bar, next) { bar = _bar; doBaz(next); }, function(result, next) { foo.piyo = result; doQux(foo, bar, next); }, function(baz, next) { // ... next(null, { foo: foo, bar: bar, baz: baz }); } ], callback); };
async.waterfall 2
var ex_waterfall2 = function(callback) { async.waterfall([ function(next) { // fooを取得する. getFoo(next); }, function(foo, next) { // fooをごにょごにょする. foo.hoge = 1; foo.fuga = 2; // barを取得する. getBar(foo.x, function(err, bar) { next(err, foo, bar); }); }, function(foo, bar, next) { doBaz(function(err, piyo) { next(err, foo, bar, piyo); }); }, function(foo, bar, piyo, next) { foo.piyo = piyo; doQux(foo, bar, function(err, baz) { next(err, foo, bar, baz); }); }, function(foo, bar, baz, next) { // ... next(null, { foo: foo, bar: bar, baz: baz }); } ], callback); };
東京Node学園祭2015 にて発表を行います
2015/11/7(土)に東京Node学園祭2015が行われます。会場は今年も弊社サイバーエージェントのオフィスになります。
さて今回、「Node.jsでのゲームサーバ開発 愛すべきバッドノウハウ3選」というタイトルで登壇させていただくことになりました!
ここでは少し発表内容の背景を説明したいと思います。
弊社のゲーム部門では、サーバサイド開発にJavaとNodeを利用しています。開発者や事例が育ってきたこともあり、最近では新規開発にはほぼNodeを利用しています。
Nodeそれ自体の動作や開発のスピードに関して、困ったことはほとんどありません。(Nodeは普通のサーバ開発に十分適している、と私は考えているゆえんです)
さて、プログラミングには、一般的に良い/悪いとされている設計手法や原則が存在します。しかしながら、新卒で入社後、実際にゲームサーバ開発に従事したとき、必ずしもそれが実践されていないと感じることがありました。
最初は単に疑問に思うだけでしたが、(当然経験の深いエンジニアが多い中、新卒の自分などでは思い至らないことばかりなわけで、)そこから約2年の開発経験を通して自分なりに考えてたり試してきた結果、少なくとも「JavaScript」「Node.js」「ゲームサーバ」というコンテキストにおいて必ずしも"悪"と切り捨てられるものばかりではなく、良いあるいは致し方ない面がある、というふうに納得できるものが多くありました。
(単に直すべきポイントもあり、実際にこの1年ほどの開発で改善してきたものもありますが、それは今回の話では触れません。)
そのようなものを、今回「愛すべきバッドノウハウ」と呼び、そのうちのいくつかの事例に対して極力深い考察を与え、話したいと思っている次第です。中には自分が考案したバッドノウハウも含まれます(笑)
私自身、新卒でサイバーエージェントに入社して以来、仕事としてはNode.jsでの開発しか行ってきませんでしたし、プログラミングに対する一般的な考え方を十分に身につけているとはいえませんから、少し背伸びした発表になってしまうでしょう。
それでも、国内でのNode.jsサーバ開発実績としては最大に近い規模であろう我々の開発現場の工夫を、なんとかして発信していきたいという思いから、今回発表させていただくに至りました。
ところで、今回のタイトルは「・・・愛すべきバッドノウハウ3選」とありますが、現在自分の頭の中にあるものは、大〜小あわせて10個くらいあります。その中でより興味深いものを選んで3つ程度にする予定ですが、個数は少し前後するかもしれません。発表を作る上でさらに深く考えるうちに、もしかしたら単なるバッドノウハウに消化されてしまうものもあるかもなぁ、と思っています。それはそれで自分にとっては有意義なことですが。
自分の経験が、Node + ゲームサーバ開発なので、タイトルではそこに限定していますが、おそらくNodeサーバ開発全般に共通する話が70%くらいになると思います。
また、話の流れ上、タイトル通りのバッドノウハウだけでなく、Node+ゲームサーバにおける通常のノウハウ(グッドノウハウ?)も少し紹介できるかなと思います。
というわけで、興味のある方は聞きに来て頂けたら幸いです。
(なお、東京Node学園祭の参加者募集開始は10/13(火)0時からです。)