エンジニア宗教戦争の本質

※書いたあとに思ったんですが、エンジニアというよりプログラマと書いた方が良かった気がします。全部直すの面倒くさいのでココに書きます

内容

ふとエンジニアがエディタとか言語とか、なんならタブスペースとかの「宗教戦争」に思わず熱くなってしまう理由に納得のいく説明ができるのでは、と思ったので書き始めたものです。宗教戦争に合理的な理由があるのでは、という仮説ですね。

独りプロジェクト

ぼくが所属している組織のうちの1つは比較的小さい会社ながら、若干マイクロサービス的な思想もあり、沢山のプロジェクトを開発保守管理しています。なので1人でガッと作って機能追加も1人でやるぜ、みたいなプロジェクトがちょいちょいあったりします。

ぼくも2つぐらい抱えているのですが、自分独りプロジェクト、すごい気持ちがいいんですよね。具体的に言うと機能追加も猛烈な勢いでできて、複数人で開発してるプロジェクトより生産効率めちゃくちゃいいんですよ。

勿論俗人化という致命的な欠点があり、仕事のプロジェクトでそんな状態をずっと放置しておくわけにもいかないので、一時的な生産性低下を覚悟の上で引き継いだり複数人プロジェクトに移行するわけですが。

ただこの生産性向上を複数人で擬似的に発生させる方法があります。宗教です。

宗教

複数人の開発でも独りプロジェクトと同じ効果を発揮させるには、キツいルールで縛り、そのルールの信奉者でグループを作るという方法が1つ思い付きます。つまりこのルールを体系化したものが、このblogで言う「宗教」の定義とします。

ぼくがWebエンジニアなので、大分Webに偏った話として、以下のようなものが思い付きます。

これ、なんか言語やWebFrameworkと密接に結び付いてる気がしませんか。こうやって言語やWebFrameworkの宗教戦争が起こるのだ、ということに説明できる気がしませんか。

宗教戦争のカラク

つまりこういうことです。同じ宗教の人間で開発プロジェクトを固めると大変開発効率が良くなります。しかし、多くの場合宗教には別の宗教に比べて際だって強い点はありません。どの宗教もなんやかんや言って開発できるので一定の勢力を保っていると思われます。(分野の違いによって強さが変わるとかもあると思います)自分の宗派が強くなればそれだけ自分の転職可能性も上がりますし、自分の開発効率が相対的に上がるとも言えます。

特に言語は開発スタイルを伴っていることが多く、宗教戦争の火種になりやすいと言えます。エディタも最近は半ば冗談で語られることが多いですが、エディタを制限する職場もちょいちょいあると聞くので馬鹿にはできないと思います。そもIDEと非IDEで効率的なコードは若干異なるという話もあります。あとC++のようにコミュニティが大きく、言語があまり開発スタイルを強制しないために差がでかいところだと、エディタという宗教の流派みたいなので差が付くという話もあるかもしれません。

宗教という言葉について

エンジニアの宗教戦争に合理的な理由が付与されてしまった以上、宗教という言葉は相応しくないのでは、という言説もあると思うので補足ですが、宗教が合理的でないという発想が間違っているという主張はしたいと思います。宗教は一見非合理性の塊とも言えますが、歴史的な研究からは宗教が果たした重要な役割について、所謂理系人間は軽視し過ぎていると考えています。Civilizationというゲームでは、宗教から多くの場合、都市の幸福度にプラス補正が入ったりと様々な役割を与えられています。(勿論負の側面もありますが)まさにエンジニアのそれを「宗教」と呼ぶのが相応しいと思いませんか?

フリーランスになりました

12/5補足

週2日ほど空いてる状態になっています。仕事募集中です。1社行ってる方が短期的に忙しく、結局フルタイムで働いているような状態ですが、おそらく問題なく空けられます。

11/15補足

近いうちに週2日ほど手が空きそうなのでお仕事募集します。また決まったらここに何か書きます

近況報告

9月から、今正社員として働いている会社の契約を業務委託契約に切り替えて名実共にフリーランスになります。「本骨システム」という屋号でやってます。当面は仕事が埋まってるのですぐに仕事を受けられるような状態ではないですが、自己紹介兼ねてできることとか纏めておきます。

本人が分かりそうな情報

東京麻布在住のエンジニアです。麻布家賃高いし街も嫌いなので引っ越したいですね…

Twitter: ぽんこつ@本骨システム (@ponkotuy) | Twitter

GitHub: ponkotuy (ponkotuy) · GitHub

Qiita: ponkotuy - Qiita

HatenaBlog: ponkotuyのブログ

特に得意なやつ

大体一通りできるやつ

基本サーバサイドのWeb系エンジニアという認識で良くて、フロントは必要最低限という感じです。

経験は弱いけど隣接技術領域なので習得は早そうなやつ

おすすめのパターン

  • ScalaWebサービス1本作りたい。全部任せられる人探している
  • 先人が残したScalaプロダクトが保守できなくなってる。Scala詳しいマン募集
  • サービスがヒットしたけど技術詳しい人がいないからリードテック欲しい。人数雇えないから1人でそこそこできる人が欲しい
  • インフラできる人欲しいけど専業でインフラ雇うほどでもない規模。できればコードも見て欲しい
  • スケーラビリティの問題でRailsからScala/Playframeworkに移植したい

ずっと小規模な企業で活動したり、1人で個人的なプロジェクトの開発保守をしていた関係で、一通りを一人でやることが多かったです。

お勧めしないパターン

  • ガンガンフロントエンド開発して欲しい
  • ガンガンRailsで機能追加していって欲しい
  • ついでにカスタマーサポート・営業もして欲しい
  • 大人数のマネジメントしてほしい

Railsとフロントエンド専業は、多分ぼくより優秀な人間が一杯いるとおもうので向いてないとおもいます(あとScalaの能力値で時給設定してるので)。 カスタマーサポートと営業は新卒と同レベルという認識でいいです。電話こわい。 マネジメントは見れても2-3人が限度じゃね、って感じです。若輩者だしマネジメント特性高いと思ってないので。

ネタ的にやってみたいやつ

  • 鉄道・地理オタクとしての知識が生かせそうな仕事
  • 位置情報ゲーの知識が生かせそうな仕事(IngressとかPokemonGoとか駅メモとか
  • 歴史(特に戦史)のうんちくが生かせそうな仕事(第二次世界大戦は基礎教養
  • 宇宙物理で大学院出た経験が生かせそうな仕事(GPGPUで流体シミュレーションやってましたが、ブランク長過ぎてあんま自信無いです

Wikipedia徘徊とGoogleMapだけで1日過ごせると主張しています。

MyFleetGirls終了のお知らせ

2014年の3月頃からサービスしてきた艦これツールのMyFleetGirlsですが、この度myfleet.moeでのサービスを終了することにしました。myfleet.moeは失効しており、別の事業者が取得している可能性があるので不用意にアクセスされないようにお願いします。

サービスを終了する直接的な原因としては、myfleet.moeのドメインがぼくのミスで失効してしまったというのがあるのですが、元々自分が使っていない、メンテ困難になりつつあったコードで開発するモチベーションを維持するのが難しかったり、SakuraVPSからAWSに移行したら想像以上のコスト増(10倍とか)になってしまい、会社のクラウド補助金を引いても月間8000円程度の支出が生じており、カスタマーサポートもしなければならないなど、維持コストが馬鹿にならなくなってきたことが挙げられます。というより、ここ最近は全体的に放置気味で、サービスを継続する上で必要な事を経費以外殆ど出してなかったという感じです。世間のOSS開発者の偉大さを思い知った格好です。

もし奇特ながら、MyFleetGirlsのこの諸々のコストを引き継いでサービスを継続する、という方がいらっしゃいましたらぼくに相談してください。少しぐらいであればお手伝いできるかもしれません。Scala + Play + MySQLのアプリで最終的にはDocker(ECS)へ移行してました。

ちなみに最終的に18000円ほど月額掛かっていたサーバ代ですが、手間をかければ相当に削減できそうな感触はあり、何故やらなかったかといえば単純に開発のモチベが喪失していたからというところが大きいです。

瑞鳳でMyFleetGirlsが落ちた話

起こったこと

21日の深夜にMyFleetGirlsが接続不能になった。この時点でCPUが100%に張り付き一瞬でT2インスタンスのCPUクレジットを消尽した。

22日の昼に片手間で復旧作業を開始。最初EC2インスタンスの不都合かと思って雑に再起動をかけるが直らず100%に張り付く。このあたりで採掘を疑う(完全に見当違い)。すぐに復旧できなそうなので放置。

仕事中に無限ループの可能性に思い当たり、夜にログを見て調査してみると、起動時のRanking処理で落ちていることが分かる。

→コードを読み返していくとEvolutionBaseという改造元検索アルゴリズムに不都合に思い至る。

→21日の深夜に瑞鳳改二増えてるで

→瑞鳳がわるい!

原因

MyFleetGirlsは艦これとの通信をproxyすることで通信を覗いて、必要なデータをサーバに送る仕組みで、このときMyFleetGirlsの動作に必要なマスターデータも必要に応じてupdateしてもらう。

瑞鳳改二の実装により艦娘のマスターデータが更新されたので、一番最初に接続したユーザから自動でマスターデータを受け取っている。

そのうちの1つに艦娘改造先マスターデータがある。

id aftershipid afterlv name
116 117 25 瑞鳳
117 555 80 瑞鳳改
555 560 80 瑞鳳改二
560 555 80 瑞鳳改二乙

nameは分かりやすくするために付けたがこんな感じである。

で、MyFleetGirlsには改造元を知りたいときが割とあって、艦娘毎の人気とかを知るための集計類は改だろうが同じ娘なので集約してーとかやったりする。なので改造元を計算するEvolutionBaseというclassを置いている。

ここで厄介なのが改二と改二乙で、改造元が循環してしまう問題がある。実際に循環したことがある。なので、進化元を辿るときはidの小さい方を優先するようにしている。(これは今思うと不十分である。後で述べる対応策で解決している)

上の正しいデータが来ていればMyFleetGirlsが落ちてこのblogを書くこともなかったわけだが、一番最初のユーザがマスターに上げてきたデータは以下のようなものである。

id aftershipid afterlv name
116 117 25 瑞鳳
117 0 80 瑞鳳改
555 560 80 瑞鳳改二
560 555 80 瑞鳳改二乙

あとはわかるな?AWSの虎の子インスタンスは永遠に瑞鳳改二と瑞鳳改二乙の間を行き来することだけに全てを費やし始めたのだ。

なんというかありそうなミスではあるが、MyFleetGirlsを殺すには十分な破壊力があった。

教訓

  • CPU使用率が張り付いたら無限ループを疑え
  • 再帰は必ず回数をカウントしろ
  • 自分の管理外のサーバが正しい情報を返してくる前提で事を進めるな

対応策

復旧はデータの修正と再起動だけで片を付けたが、そのあとで再帰カウントを導入した。

宝石の煌き+拡張:都市

はじめに

この記事はボドゲ紹介 Advent Calendar 2017 - Adventarの2日目ですが、大変遅れてしまったので4日目ぐらいに掲載されています。申し訳ない。

宝石の煌き(英題Splendor)と、その拡張、都市について解説します。

勝利条件

宝石の煌きは宝石を集めて発展カードを買っていき、最終的に勝利点15点を集めるゲームです。勝利点を得る方法としては以下の2つがあります。

  • 発展カードを購入する
  • 貴族タイルを獲得する

順番に見ていきましょう。

発展カード

f:id:ponkotuy:20171203231809j:plain

図のように3つの世代に分かれており、後ろの世代になるほど勝利点もコストも大きくなります。左上の数字が勝利点、左下に書いてあるのがコストです。

右上に書いてある宝石は産出する宝石です。購入時に必要な宝石を減らすことができます。つまり、発展カードを買うことによって、高コストなカードを安く買うことができるようになるわけです。

貴族タイル

f:id:ponkotuy:20171203231917j:plain

貴族タイルは左下に書かれた枚数の発展カードを持っていると自動で入手することができます。左上の点数が入ります。勿論早いもの勝ちです。

ターン

このゲームは順番に以下の3つの行動のうち1つを行い、手番を回していくゲームです。

トークンを獲得する

f:id:ponkotuy:20171203231519j:plain

宝石1つ分の価値になるトークンを獲得します。発展カードと違い使い捨てです。

取得する方法は

  • 異なる種類の宝石3つ
  • 同じ種類の宝石2つ(ただしその宝石トークンが4つ以上ある場合のみ可)

の2つです。なお、持てる宝石トークンの数は10までです(重要)

発展カードを購入する

カードを1枚、コスト分の宝石を支払い、手元に公開して置きます。

発展カードを確保する

カードを1枚確保します。コストは必要ありませんが、まだカードの効果も発揮しません。手札として持っておきます。確保することにより、自分のみが対象のカードを購入することができるようになります。もちろん購入する際には宝石を支払う必要があります。

また確保する際に1枚黄金トークンを手に入れることができ、このトークンはオールマイティーな宝石として扱うことができます。便利!

で、どんなゲームなんですか

このように宝石の煌きはおおまかには拡大再生産ゲームですが、購入の場を共有することになるので、自分しか買えないカードが沢山できるようにカードを構築していく必要があります。

あとカジノのチップを流用したコンポーネントがいいです。その分ちょっと値段が張るんですが、それだけの価値はあります。

拡張ルール

拡張は4種類のルール追加があり、好きな拡張ルールを混ぜてプレイすることができます。

都市

f:id:ponkotuy:20171203232333j:plain

拡張パックのタイトルにもなっている拡張です。勝利条件が15点ではなく、最初にランダムで選ばれる都市を購入するルールに変更されます。都市には、勝利点が少ない代わりにカードの枚数が結構必要だったり、逆に勝利点が沢山あればOKみたいな都市もあったりします。

東洋

f:id:ponkotuy:20171203232203j:plain

各世代に2枚ずつ、購入できる特徴的な発展カードを増やす拡張です。既に持っている発展カードのマークをコピーしてその宝石が付いたカードとして扱う発展カード、下の世代のカードをノーコストで入手することができるカード、宝石トークン2枚分として扱うカード、2枚の発展カードを破棄して購入するカードなどがあります。

交易所

f:id:ponkotuy:20171203232251j:plain

5つのパワーが書かれたボードが追加されます。パワーは左に条件(主に発展カード)、右に恩恵が書かれています。条件を満たしたら当該パワーに紋章トークンを置いて示します。

  • 赤3白1 -> 発展カード購入時に宝石トークンを1枚獲得
  • 白2 -> 同じ色の宝石トークン2つ獲得した際に他の色のトークン1つを追加で得る
  • 青3黒1 -> 黄金トークンは1色のトークン2枚分に数える
  • 緑5+貴族タイル -> 5点
  • 黒3 -> 紋章1つにつき1点

城塞

f:id:ponkotuy:20171203232448j:plain

カード購入時に以下の行動のうち、どれか1つを行う必要があります。

  • 公開されている発展カードに城塞を置く
  • 置かれている城塞を移動する
  • 他のプレイヤーの城塞を取り除く

城塞の効果は以下のようなものです。

  • 他のプレイヤーの城塞が置かれているカードは購入できない
  • 他のプレイヤーは城塞を置くことができない
  • 3つの城塞が置かれたカードはターンを消費せずに購入できる(購入時の行動を追加で行う

拡張まとめ

慣れてきて、普通のゲームに飽きたプレイヤーも楽しめるものになってます。宝石の煌き好きな方は是非購入してみては。

個人的なオススメは交易所です。都市も割とどんなゲームに入れても面白いので、デフォルトで突っ込んでしまってもいいかもしれません。

なお、私がプレイしたときは、城塞がやたら不評でした。これ妨害効果強過ぎて割と別ゲーな雰囲気です。東洋とか入れると選択肢が増えて多少はマイルドになるかもしれません。

モデルの共用ダメ説の提唱

はじめに

最近サーバとクライアントを同じ言語にしてみたよ、っていう話を良く聞きます。その理由として「モデルの共用ができる」というメリットが挙がります。これに関して、いやそれ違うだろ、って前から思っていて、もやもやしているので書きます。

本当はQiitaで書きたかったのですが、どう考えてもポエムなのでHatenaBlogにしておきます。

軽い背景解説

「はじめに」が大筋理解できていれば読む必要が無いと思いますが、そうでない人もいると思うので解説します。

所謂Webの世界ではサーバ側とクライアント側で厳密に分けられることが多くなっており、ソシャゲだったら運営側のAPIサーバとスマホアプリ(クライアント)という感じで分けられています。ゲームに必要なデータはAPIサーバから取ってきて、スマホアプリは動きとか絵を管理している感じですね。普通のWebサイトでも単純なAPIサーバからデータを取得してJavaSciprtがレンダリングする仕組みのサイトが増えています。この方がUIの自由度が増すんですね。

で、そのAPIサーバとクライアントの間で必要な通信のデータ構造をモデルとここでは呼んでいます。例えばサーバにPersonという情報があって、Person内にname(文字列)とage(数値)があるとします。Scalaで書けばこんなんですね

case class Person(name: String, age: Int)

クライアントがPersonのデータを欲したら、この形式でJSONのObjectなりMessagePackなりでパックされて送られてくるわけです。

当然この形式になっていることをAPIサーバ側もクライアント側も知っていないといけない訳です。ところが今まではサーバ側とクライアント側で言語の断絶があって、サーバ側はRuby/PHP/Perl/Java、クライアント側はJavaScriptが多かったので、結果的に両方でモデル定義を持つ必要がありました。今までは。

ところが、NodeJSでクライアントの覇者JavaScriptがサーバサイドで書けるようになったり、サーバサイドでも使い勝手良さそうなC#が、Unityとしてクライアントで大々的に使われるようになったところで、モデル定義を1つ書けばいいんじゃないか、という話が出てきたわけです。(Fate/Grand OrderがUnityだからサーバもC#にしているという話がありました)

で、ここでぼくが書きたいのは「モデル定義を本当に共用して大丈夫?」という話です。ようやく追い付きましたね。

本題

モデル定義を共用するのに疑問を持っている理由は「そのモデルが更新されたときの、クライアントとAPIサーバの互換性どうするの」という点に尽きます。

さてサービスが運用に乗ります。機能を追加します。最初に作ったPersonモデルにデータが追加しよう! 良くありますよね?ここでは例でnameとageがあったPersonに、海外展開に備えて言語情報を設定しましょう。めっちゃありそう。langっていう名前の文字列でいいかな。

langを追加するためにはモデルに修正が必要です。共用のモデルなら1箇所にlangを追加すればいいだけですね。さてリリースしましょうか?

このとき、もしクライアントとサーバが同時に変更してリリースが可能であればうまくいきます。それは大変幸運で、そして現代においては稀有なパターンだと思います。1つのAPIサーバに1つのJSクライアントしかなければ、まぁうまくいくかもね?

つまり、APIサーバは運営が自由にアップデート可能なことが多いですが、クライアントはそうではなく、古いモデルでやりとりをすることが多々あります。クライアントがスマホアプリだったら更新サボってる人も多そうですし、APIサーバを公開していて多数のクライアントが付いてる場合だと更に困難ですね。全員に更新が行き渡るのに何年掛かるの?TwitterBasic認証やめるのに何年掛かったっけ?

結論として、APIサーバは最大公約数的なモデル定義をする必要があり、クライアントにその必要はなく、それによる弊害の方が大きいと考えています。APIサーバがクライアントからPersonの投稿を受け付けるときはlangがある場合と無い場合がありえるので、両方のパターンを記述する必要があります。クライアントはサーバが自分の持っているPerson型を受け付けられない可能性を考慮する必要はありません。モデル定義さえ別ならば。langという余計なデータが来ても捨てればいいのです。

モデル定義を共用化するためにクライアントのコードを不必要に複雑にする必要はありません。モデル定義のコストとか多寡が知れてるし、2つぐらい作ったらどうですか、という話です。どうせサービス運用がうまくいって3年とか経てば全くの別物になっているのですから。

MyFleetGirlsの経験について

実際あったかのように語りましたが、実際にあったんです、MyFleetGirlsで、という話です。

MyFleetGirlsという自作の艦これツールがありまして、ScalaサーバとWebクライアントとScalaクライアントに分かれています。艦これの通信をMyFleetGirlsのサーバに横流しするのがScalaクライアントの仕事で、サーバと同じ言語なので、この通信に使われるモデルは共用化されています。安易に共用化しました。

結果として、後から追加されたモデルデータは全てOptional、存在するか分からない型として処理されるか、古いクライアントからのアクセスを拒否する仕様になりました。サーバが古いクライアントと通信する可能性があるからです。Optionalになった場合、クライアントは常に値があることが保証されているにも関わらずOptionalを強要されるため、コードからのスメルは強烈なものになりました。古いクライアントからのアクセスを拒否する仕様のために、強制的にクライアントをアップデートする仕組みを1から作るハメになりました。

蔵王キツネ村探訪2017/9/16

大分前になってしまったがきつね村に行ってきたのでひたすらもふい写真を貼る。

f:id:ponkotuy:20170916101635j:plain

入口。檻が無い…というより檻の中に入れる動物園なので、色々免責事項が多い。

f:id:ponkotuy:20170916102212j:plain

入口の安全地帯(突然キツネに攻撃されない的な意味で)にはうさぎとかヤギとか馬がいる。

f:id:ponkotuy:20170916102354j:plain

f:id:ponkotuy:20170916102407j:plain

f:id:ponkotuy:20170916102550j:plain

f:id:ponkotuy:20170916102422j:plain

f:id:ponkotuy:20170916102510j:plain

f:id:ponkotuy:20170916102633j:plain

f:id:ponkotuy:20170916102716j:plain

f:id:ponkotuy:20170916102834j:plain

こっから先はキツネが生息するだだっぴろい空間。

f:id:ponkotuy:20170916103236j:plain

f:id:ponkotuy:20170916103200j:plain

f:id:ponkotuy:20170916103250j:plain

f:id:ponkotuy:20170916103345j:plain

f:id:ponkotuy:20170916103132j:plain

f:id:ponkotuy:20170916103436j:plain

f:id:ponkotuy:20170916103530j:plain

f:id:ponkotuy:20170916104157j:plain

f:id:ponkotuy:20170916103754j:plain

f:id:ponkotuy:20170916103639j:plain

f:id:ponkotuy:20170916104220j:plain

f:id:ponkotuy:20170916104306j:plain

f:id:ponkotuy:20170916104452j:plain

f:id:ponkotuy:20170916104536j:plain

f:id:ponkotuy:20170916105005j:plain

f:id:ponkotuy:20170916104411j:plain

f:id:ponkotuy:20170916105119j:plain

f:id:ponkotuy:20170916111311j:plain

放し飼いにされていた子山羊

f:id:ponkotuy:20170916111845j:plain

f:id:ponkotuy:20170916111715j:plain

f:id:ponkotuy:20170916112301j:plain

f:id:ponkotuy:20170916112507j:plain

f:id:ponkotuy:20170916111929j:plain

おしまい。入場料1000円でそこそこリーズナブルだけど場所が本当にひどいところにあるので新幹線代金とレンタカーorタクシー代を片手に来訪されることをお勧めする。

なお私は現地でタクシーを駆使して色々行くつもりだったのだが、残念ながらお金を引き出さずに蔵王高原に入ってしまい、泣く泣く歩いてキツネ村から駅まで戻りました。

そこらへんの顛末はTwitterでどうぞ

twilog.org