退職しました

2019/10/31 を持って8年間勤めてきたドワンゴを退職しました。

ドワンゴ退職エントリの旬は過ぎているよう気もしますし、こんな何年も放置していたブログで今更何をと思わなくもないですが、なんとなく自分の気持ちの整理もかねて適当に綴ってみようと思います。

何をやってきたか

各種のゲームデバイス、PS Vita, Wii U, 3DS, Nintendo Switch 上でのニコニコプレイヤーの実装をずっとやってきていました。
それぞれのデバイスでのシステム部分というか、ゲームデバイス上での非ゲームアプリケーションフレームワーク、そんなものを作り続けてきた感じです。

これらのニコニコ動画クライアントは、私の手を完全に離れてしまうことになります。
もっとできることはたくさんあるし、改善すべき点もたくさんある。愛用してくれているユーザーに対して自分が出来るはずのすべてを提供することができなかったことは申し訳なく思いますし、心残りがないというと嘘になります。

 

私が入社した当時は前回ドワンゴの退職エントリが流行る少し前。ドワンゴの良くないところが目立つ様になり、技術者が働きやすいドワンゴを整えようという動きが生まれ始めていた頃です。

ゲームデバイスの開発チームは、そもそもが PS Vita 版のニコニコを作るために外部から集められた人たちで構成されており、一時期キテラスという別会社にいたこともあったりして、ドワンゴ内でも少し独自の文化を持っていたと思います。

自分はその一員として、できる限りユーザー目線でものを考え、ユーザーにとって良いものを、できる限り効率よく開発する、そういう開発の文化を意図的に育ててきたつもりです。自分は開発しか出来ない人間ですから、働きやすい環境を作ってくれている人に対しては、良い開発の文化と製品を作る、そういう形で答えるしかないと考えていました。

ドワンゴという会社

ドワンゴという会社はほんとうの意味での裁量労働が実現されている非常に珍しい会社です。しっかり成果を上げていればいつ出社してもいい。開発に関係のないことを一切無視して開発を行うことができる、本来技術者というのはこうあるべきで、そういう仕組みを作って来てくれた人には頭が上がりません。

コミュニケーションは slack が非常に活発に利用されており、業務上の問題もオープンに相談できるだけではなく、社員同士の趣味や嗜好の上でのつながりもあり(合わない人にはとことん合わないかもしれませんけど)自分にとっては非常に心地が良いものでした。会社を離れることでいくつかの趣味チャンネルを見ることができなくなる、それは寂しいものがあります。

ドワンゴはエンジニアにとって非常に働きやすい環境だったと思います。

ですが、その働きやすい環境を作ってきた人たちはすでにドワンゴを離れてしまっています。これからどうなるかはまったくわかりません。

退職を考えた理由

第1に(これを言ったらオシマイだと思って今までは言わないようにしてきていたのですが)ニコニコが自分にとってつまらないものになってしまったこと。

第2に、ニコニコを面白いものにするために、ドワンゴ社内から私ができることは存在しないのだということを強く実感してしまったこと。

ぶっちゃけていってしまえば、ニコニコがもうダメだからです。

なんでもうダメ?

ニコニコを大好きな人はドワンゴ内外にたくさんいて、それぞれがニコニコを盛り上げようとしていることは知っています。そういう人たちには本当に申し訳ないのですが、私はニコニコに将来はないと判断せざるを得ませんでした。

自分自身も結構なニコ厨です。

ニコニコ動画に携われるということは自分にとって非常に幸せなことで、自分にとってそれが趣味なのか仕事なのかわからない、そんな状態で仕事をしていました。そのことについては感謝してもしきれない。その恩返しはしなければならないと考えていました。

ですので、会社の業績が悪いという話を聞いたときにも、多少給料が下がろうが、多少つまらない仕事をすることになろうが、できる限りドワンゴの中でニコニコ動画をより良くすることを考えようと、あるいはサービスの最期を看取るのもいいかもしれない、とか、そんなふうに考えていました。

そんなふうに考えている私ですら退職を考えるだけのことがあったわけです。

業績が上がっていないからコスト削減をしなければならない。だから直接お金を稼ぐことができていないゲームデバイス開発を縮小する。それはわかります。

ですが、手元でできる何を提案してもそれを却下し、何もするなという指示をしておいて、なんの業績も上げていないという評価を下して辞めるのを待つというやり方は果たしてコスト削減なのか。

私が「そんなことをしていたらエンジニアが離れていきますよ。」という話をしたことに対して、「そんなこと」をそのまま返してきたうえに、コミュニケーション上の問題があるとして低い評価を下すのは、要するに出ていけということなのか。

この面談、そちらにこそコミュニケーション上の問題があるのではないか。

私に問題があったのかどうか、その評価が正当であるか、たまたまそのマネージャーが問題だっただけではないか、そういうことの前に、どんな理由であれ部下に対してそのようなコミュニケーションをとるマネージャが存在するということが大きな失望となりました。異動先を決める中で様々な部署の方の話を聞いたのですが、私のような例は珍しくないのだということを知りました。

たまたま私がいたゲームデバイス開発のセクションが守られていただけで、この会社はもともとそういう組織だったのかもしれません。

 ニコニコが大好きなユーザーには本当に申し訳ない。
けれども、私はこんな組織で働くことは出来ないし、そんな組織でこれから良いものが作れるとは、到底思えません。

望みはあるのかな

ドワンゴにはニコニコ動画が好きな人が集まっています。そこにはニコニコのクリエイターも多数含まれます。私が知る限りでも、それなりに名がしれたボカロPがいますし、ゲーム実況者がいますし、RTA走者もいます。私が知らないだけで、歌い手も踊り手も、技術部と言われる人達もいるでしょう。私はその人達の情熱や力を信じていました。

そういう本当にニコニコが好きな社員たちの意見を取り入れて小さなところから確実に良くしてけば、それぞれのユーザーの文化は守ることができる。それを続けていけばどこかでシナジーが生まれ再び盛り上がることもあるだろう。そう考えていました。

自分はコメントアートについては少しは貢献できたんじゃないかなと思っています。他の分野でも私のような人がいるのではないか、と思うのです。

 

ドワンゴの slack では、将来的なニコニコの姿や、今どうすればニコニコがより良いものになるかが日々真剣に語られています。これは非常に素晴らしいことだと思います。

ですが『わかった、それやろう!』と言うべき立場にいる人はそれを見ていながら一切反応しないのです。

こういった改善の声を無視しながら、プレミアム会員を増やしたいと言ってアイディアを社員から公募しておいて、即効性がないという理由で全てをボツにするということが最近行われました。いまだに「なんだかスゴイこと」をぶちあげることで一発逆転しようとしている。これは日々ニコニコのあり方について真剣に考えている社員と、アイディアを出してくれた社員の両方をバカにした行為だと思います。

この辺の考えからを改めない限りどうにもなりませんが、逆に言えばこの辺を改めて、小さな改善を確実に行うことを評価するようになれば、もしかしたら望みはあるのかもしれません。ニコニコが大好きな人はたくさんいるのですから。

何と戦っているのだろう

私から見ると、ニコニコの運営はまともな価値判断が行われているとはいいがたい状況です。

特にコストやリスクに対する感覚が完全にぶっ壊れてしまっています。一つ一つの判断が、なぜそうなるのかが全く私には理解できないので「このようにおかしい」と示すことすらできなくなってしまいました。

全く意味がわからない。

その場その場で自分の思いつきか、自分の仕事を守るようにという理由で判断を下し、その表向きの理由としてコストやリスクをあとからでっち上げる、そういうことがまかり通ってしまっています。思想上ゆずれない一線と、引っ込みのつかなくなった感情の区別がついていません。

普通に考えたらまず起こり得ないような、しかも起こったとしても現実的なコストでカバー可能なようなことを、それが起こったらどうするんだと延々と議論している。

現実に存在しないであろう特殊な状況のユーザーを勝手に想像して、そのユーザーが混乱すると言って新しい取り組みをボツにする。

開発コスト削減だといって機能を削るが、そのコストがいくらなのかを誰も知らない。そして、開発にやることがなくなって完全に遊んでしまう人員が出てくる。(つまりトータルの人的コストは変わっていない。)

この人達は一体何をしているのでしょうか。

なんでそうなってしまったのだろう

細かい問題はいくらでもありますが、一つにまとめてしまうと、

専門知識を持っていない中途半端に偉い人が自分に口出しできる部分に口出しをすることで仕事をした気になって本来するべき意思決定をどこまでも先送りする。部下はそれに振り回され、そういう状態に対抗する手段が存在しない。

そういう組織になってしまったことが問題なんじゃないかと思います。これは組織を作ることを専門にしている人から見ると、典型的な例だったりするのでしょうか?

 

転職について

ゲームデバイス上で非ゲームを作るというかなり特殊なことを8年間もやってきてしまったせいで、わかりやすく売りとなる技術を私は持っていませんでした。ゲームをバリバリに作っていたわけでもなく、マネージャーをしていたわけでもありません。

自分にしかない知識といえば、ニコニコ動画のコメント描画に関しては確実に世界で3本の指には入る知識を持っていますが、これはニコニコを離れてしまうと全く無価値となる知識です。

ですので、職探しは厳しいんじゃないかなと予想していました。

幸い、そういった特殊な知識について誰よりも詳しくなるということを、とある会社にメタスキルとして高く評価していただけたため、それなりに良い条件で雇っていただくことができました。仕事内容は自分にとって少し挑戦となるものではありますが、面白い仕事ができそうです。

当のドワンゴでコメントの知識が一切評価されなかったことは残念でなりませんが、もう終わったことと考えます。

 

lix:CharSequence

XML パーサを作りにあたって、細切れのstringオブジェクトを作るのを避けたかったので、
元々のXMLドキュメントはそのままにしておいてその中の文字位置と文字列長で文字列を占めるという戦略をとりたく、
そのために作ったクラスです。

https://code.google.com/p/lix-xml/source/browse/trunk/lix/char_sequence.h

特に面白いことは何もしていない、文字列の先頭ポインタと文字列長を持つクラスです。
辞書順比較の演算子を作ったので、これが地味にいろんなところで使えたりします。

例えば、string -> string のテーブルなども、内容が変化しないのであれば std::map ではなく、std::map にしてリテラルのままいけるようになったりします。

    // これは一旦入れたら変化しないものとして、
    map<string,string> table;
    table["&gt;"] = ">"; // これは無駄に string オブジェクトができてる。
    table["&lt;"] = "<";
    
    // これで(記述はめんどくさくなるけど) string 作らずにいける
    map<CharSequence,CharSequence> table;
    table[CharSequence("&gt;")] = CharSequence(">");
    table[CharSequence("&lt;")] = CharSequence("<");

xml パーサを書いたのでそれについて

ちょっと特殊な環境下で xml をパースする必要があって、いろいろと xml パースライブラリを探したのですが、どれもちょっとづつ求めるものと違うところがありうまく行かず、自前で xml パーサを書く必要がありました。
その時のことを書いてみようかと思います。

開発言語

開発言語は C++ です。ですが、以下の様な制約がありました。

  • 言語はほぼ C++03。
  • 例外が使用できない。
  • RTTI が使用できない。
  • boost は使用できない。(標準ライブラリはOK.)

boostはヘッダのみで使用できるものであれば導入することは可能ではあるのですが、プロジェクトのメンバーを説得することができませんでした。

実行環境の特性

実行環境はちょっとした組み込み機器です。
メモリは潤沢にあるのですが、その割り当て(malloc)が無視できないレベルで遅いため、細切れの new(malloc) をできるだけ避ける必要がありました。malloc 自体にも手を入れることは可能ではあるのですが、スレッド同期コスト自体がバカにならないため、そもそもの new(malloc) の回数を減らす必要がありました。

プロジェクト的な背景

汎用的なものを作成する体力はなかったので、プロジェクトで不要なxml的機能を諦めて仕様をシンプルにしようと考えました。
プロジェクト的に xml の読み込みには以下のような背景がありました。

  • 読み込む xml はそれほど大きなものではない。大きくて 数百KB。
  • 読み込む xml の種類が多い。また、頻繁に仕様の変更が予想される。
  • xml を作ることはない。編集することもない。
  • パースの失敗は本来はありえない。失敗は『失敗した』ということだけがわかればよく、詳細は必要ない。
  • 汎用的な木構造を持つデータの伝達のみに使用する。xml namespace などの機能は一切必要ない。
  • 文字コードutf-8 決め打ちで良い。

おおまかな設計

まず、sax系はナシです。スキーマの変更への追従のコストが高すぎます。
dom ツリーを作って、ルートからたどっていってお目当てのデータにたどり着くコードをプログラマがちゃちゃっと書ける環境を作らなくてはいけません。

で、dom 系のほうがいいのですが、一般に dom系パーサは大きなメモリを喰います。このパーサーを作る前は tinyxml(http://www.grinninglizard.com/tinyxml/) を使用してコードを書いていたのですが、実際メモリ消費量と確保の回数的に許容できるものではありませんでした。
なので、なんとか考えなくてはいけないのですが、tinyxml が行う new はそのほとんどが string のインスタンスのためだったりします。
これは tinyxml では dom の操作が可能なためです。dom を操作するからには各ノードのオブジェクトが自分のタグ名やアトリビュートの key-value を個別に文字列として持っておく必要があります。
まず、これを諦めます。

プロジェクト的に dom の編集はありえず、元となるドキュメントの文字列も変更されることはありません。なので、各ノードが持つ文字列は元のドキュメント内の位置を覚えておけば良いことになります。
xml のパースに最適化をするならば、全体として文字列のポインタ一つ、各ノードはその中のインデックスと長さのみを持てば良いのでしょうが、ある程度プロジェクト全体で使用できる道具を作りたいという要求もあったため、『別途寿命管理された文字列の部分文字列を示す、内部には const char* で開始位置、int で文字列長を持つクラス』を作ることとします。

次にノードですが、ノードは木構造を作らないといけないためどうしても動的に確保を行う必要があります。ですが、これを new するのは確保の回数的に許容できません。
幸いメモリは潤沢にあるため、多少確保しすぎることはそれほど大きな問題ではありません。なので、メモリチャンクを作ることにします。これによって同期コストも減らすことができます。xmlのパースに最適化したいわけではないので、ある程度はほかでも使用できる固定長メモリチャンクを作ることとします。実装は Loki の SmallObject を参考にすることとしましょう。


続くかも。

SAFE_DELETE って安全でも何でもないよね。

誰が一番最初に作って広めたのかわからないんだけど、

#define SAFE_DELETE( p )  if(p) { delete (p); (p) = NULL; }

っていうマクロ。

そもそも NULL を delete してもなんの問題もないし、このマクロを通したからといって何が安全になるわけでもない。

全ての delete をこれを通してやるべし、って言うなら、素で delete を書くよりは多少は安全になるかもしれないけど、これって安全 delete って言うようなものなの?

template<class T>
void deleteAndNullify( T*& p ){
	delete p;
	p = 0;
}

これじゃダメなん?

Named コンストラクタについて考える。

ごくアタリマエのことだったりするのかもしれないのですが、
コンストラクタの引数が多くなるとわかりづらくなるので、それを避けるためにコンストラクタとは別に static でインスタンスを生成して返す関数を作るっていうのがよくありますけど、

class Human {
public:
	// コンストラクタ、男性が true というのが
	// (ここではまだわかるけど引数たくさんあったりすると)
	// 暗号的でわかりづらい
	Human( bool is_male ){
		
	}
	
	// これで作る
	static Human CreateMale(){
		return Hoge( true );
	}
	static Human CreateFemale(){
		return Hoge( true );
	}
};

Human m = Human::CreateMale();

これって、コンストラクタじゃないので、生成先(ヒープ、スタック、あるいはプレースメント new)が選べなかったり、場合によって無駄にコピーコンストラクタが走ったりでなんかいや感じでした。
殆どの場合は大丈夫なんですけど、『基本ポインタアクセスして欲しくてコピーコンストラクタ殺してるけど、別にわかった上でスタックに作るのは構わないよ』とかいう型を作りたいとき、かなり困ります。(そういうときは shared_ptr 返せよ、という意見も分かるんですけど)

なので、第一引数にマーカーオブジェクトを受けるべきなんじゃないかな、って思いました。

struct MaleCreateMark {};
static MaleCreateMark create_male;

struct FemaleCreateMark {};
static FemaleCreateMark create_female;

class Human {
public:
	// コンストラクタ、男性が true というのが
	// (ここではまだわかるけど引数たくさんあったりすると)
	// 暗号的でわかりづらい・・・ので、廃止する。
	// Human( bool is_male ){
	//	
	// }
	
	Human( const MaleCreateMark& ){
	}
	
	Human( const FemaleCreateMark& ){
	}
};

Human* m = new Human(create_male);

こうしておくと、あくまでコンストラクタだから無駄にコピーする必要はないし、初期化リストで効率的に初期化できるし、第一引数ならば、その後のオーバーロードで後に自由に引数追加できるっていう static の生成関数のメリットも失われないんで、いいんじゃないかな?と思った次第。

設定ファイルからの読み込みをもうちょっと C++ っぽくしてみる。

前のエントリ設定ファイルから true か false を読み出す。 - meg_nakagamiの日記ですけど、これは C++ 的にはまだまだ甘いので、みんながある程度 C++ を詳しく知っている、もしくは向上心があって知らないことを学習の機会とみなしてくれる人であるという恵まれたプロジェクトであったらどんなものを作るか考えてみます。

いろんな前提が考えられるかと思うのですが、

  • 複合的なデータ構造は格納しない、 key = valueスカラー値のみを扱う。
  • keyは殆どの場合予め定められたものを使用する、プログラムでキーを生成して格納するという用途でも使用できるべきだが、そのような用法は優先しない。
  • 保存は SetProperty、取得は GetProperty で文字列を扱う仕組みがすでにできている。
  • SetProperty、GetProperty はあらゆる文字列を扱うことができるようになっている。(エスケープなどはここでは考えない)
  • 同期については考えない。

ぐらいの前提があるものとします。

続きを読む

設定ファイルから true か false を読み出す。

例えば何らかの設定ファイルに true か false という値が書いてあって、それを読み出して拡張機能の有無を変更しなければいけないとします。いろいろ状況は違うだろうけど、とりあえず std::wstring GetProperty( const char* pch ) という関数で "EnableExtention" という Property を取ってくるものとします。
こういうのを何も考えないプログラマがこういうのを作るとこんなかんじでしょうかね。

    if( GetProperty( "EnableExtention" ) == "true" ){
        // ここで拡張機能をなんとかする。
    }

とりあえずこれで問題はないかもしれません。
ですが、これだとおそらく半年ぐらいたってから、『EnableExtentionの設定が効かない』といって問題になります。
調べてみると、設定ファイルには TRUE と書かれていたりします。"TRUE" は "true" ではないから false 扱いです。
これを『そんな想定外の値を書くほうが悪い』とか、わけがわからないことをプログラマが本当に言い出すからたちが悪いです。誰が悪い、とかじゃなくて、現に問題が発生しているのだから、それはプログラマが責をおうべきです。さらには私に言わせりゃ『そんなことすら想定しないオマエがわるい。』
ちょっと考えてこうするとします。(Text という文字列関係の関数を集めた static クラスがあるものとします。)

    bool to_enable = Text::EqualsIgnoreCase( Text::Trim( GetProperty( "EnableExtention" )), "TRUE" );
    if( to_enable ){
        ...
    }

これで True TRUE true 問題はなくなります、が、どう考えてもソースコードの可読性は落ちました。一見して何をやっているかがわからない。(これでも、Text というクラスがある想定だったり、一度変数に入れているからまだいいほうです。平気で stricmp をとって == 0 とかやったりします。)
こういう人がプロジェクト内に一定以上いたり、ごく一部でもその人がよくわからない政治的な力を持っていたりすると『細やかな考慮を行うとソースコードの可読性が落ちる』というイメージが浸透してしまいます。何も考えていないわけですから、それを表立って言うことはないのですけど、漠然としたイメージって大きいです。

で、ライブラリ作成者としては、世の中の大半のプログラマがこういうコードを書いてしまうことや、それによって問題が発生してしまうことは現に予測可能なわけで、誰が悪い、とかじゃなくて、それはライブラリ作成者が責をおうべきで、そんなことすら想定しないライブラリ作成者がわるい。わけですよ。
bool 設定を読み取る、ということは予測可能なので、(GetProperty は弄れないとして)一旦クラスに*1入れて、そういう関数を作りましょう。

    class Property{
    public:
        static string GetTrimed( const char* name ){
            return Text::Trim( GetProperty( name ) );
        }
    
        static bool GetBool( const char* name ){
            string s = GetTrimed( name );
            return Text::EqualsIgnoreCase( s, "true" );
        }
    };

で、これで true TRUE True 問題はなくなり、使う側も

    if( Property::GetBool( "EnableExtention" ) ){
        ...
    }

比較的わかりやすいです。

"EnableExtention" 設定の読み込み、としてはこのくらいでほぼ問題はないでしょう、ですがライブラリとして、『設定ファイルから bool を読みとるもの』としては考慮が甘いです。設定ファイルの値が、例えば FASLE(FALSEの間違い)だったらどうしましょう?真じゃなかったら偽、で問題ないでしょうか?偽じゃなかったら真としたい設定もあるはずです。
すると、こうなります。

    class Property{
    public:
        static string GetTrimed( const char* name ){ 省略 }
    
        static bool GetBool( const char* name, bool def = false ){
            string s = GetTrimed( name );
            if( Text::EqualsIgnoreCase( s, "true" ) ){
                return true;
            }
            if( Text::EqualsIgnoreCase( s, "false" ) ){
                return false;
            }
            LOG_WARN( "%s が予期しない値なためデフォルト値 %s を使用します。", name, def ? "true", "false" );
            return def;
        }
    };

で、ここで『デフォルト値』という概念が出てきたしまったので、考慮すべきことがちょっと増えます。
プログラマが値を取ってくるたびにデフォルト値を意識しないといけないようにすると、それを間違える可能性があります。

    if( Property::GetBool( "EnableExtention", true ) ){
        ...
    }
    
    別のところで、
    
    if( Property::GetBool( "EnableExtention" ) ){   // ←第二引数を忘れてる。
        ...
    }

これを回避するためには、『デフォルト値のマップ』みたいなものをもたせて、正規の方法で取れなかったらそっちから取ってくるようにして、プログラムの初期化時にデフォルト値のマップに一通り値を入れておく、とか。プロパティーのキーの方を何らかのオブジェクトにしてしまって、それ自体がデフォルト値を知っているようにする、とか。

あるいは厳密に設定させたいのなら起動時に一通り設定をナメて、想定外のものだったら例外吐いて落とすような機構を作ってしまってもいいかもしれません。けど、その場合その『想定』をなんらかの言語で表現しなくてはいけない煩雑さが出てきますし、予め用意したキーしか使えないことになります。それは『厳密にする』という意思決定から来るトレードオフなわけですけど、それが良い場合もあるし、悪い場合もある。一時期の『何でもXMLで設定すりゃいい』って言うような風潮は、このトレードオフをちゃんと意識して決定しているとは言いがたいものでしたね。
人間って極端に走りがちで、こういう問題を発見すると『○○を導入してその問題をすべて潰そう』とか、やっちゃいがちで、一時期のJavaコミュニティのXML偏重とかはその最たるものだったと思うんですけど、変な話なんですが、こういうのってどんな機構を導入しても『考えなくていい』ということにはならないんです。何が最適か、ということはそのプロジェクトやモジュールの性質によって違う*2。こういうトレードオフを考えないで『○○を使ってるから○○は全部OK』とかやるのは問題を先送りするだけです。(それでも何も対策しないよりは100倍マシなんですけど)
考えなくてはいけないことがあるならばそれは考えなくてはいけない。
けれども、上手に考えることで、特定の領域においてはそのことを忘れていいようにプログラムを持っていくことは可能で、ライブラリを作る人はそれを目指していくべきなんじゃないかなって思います。

*1:こういうのを static 関数にすべきか、インスタンス関数にすべきか、インスタンス関数にしつつシングルトンにすべきか、どれも一長一短ですが、ここでは static 関数にします。

*2:たとえば、当たり前のように LOG_WARN()とか書いちゃいましたけど、ロギング機構自体の設定でこれをやっちゃいけませんし