読者です 読者をやめる 読者になる 読者になる

チンプイ

お題「オススメプライムビデオ」

www.amazon.co.jp

出てくる人間、だいたい自分のことしか考えてなくてよかった。クズとまでは行かないけど聖人はいないといった感じ。あとパンツも見える。

プライムビデオにあって同じ藤子・F・不二雄作品である21エモンは大体いい人しかいない。いい人しかいないから途中で出てくる資源を違法採掘してるおっさんの狂いっぷりが輝いてた。

www.amazon.co.jp

カマルについて

こんにちは、はてなでアプリケーションエンジニアをしている id:t_kyt です。

この記事ははてなスタッフアドベントカレンダー2015 - はてなスタッフアドベントカレンダーの22日めの記事です。 昨日は id:rapico によるrapico.hatenablog.comでした。

今日は家庭を持たない独身男性の話です。

カマルが好き

f:id:t_kyt:20151222125346j:plain

そもそもカマルとは京都市は中京区菱屋町にあるカレー屋さんです。

www.kamal.asia

僕はバターチキンカレーしか食べませんが同僚達はビーフやなすを食べてることが多い気がします。あとポテトサラダが美味しいです。

僕は平均すると週2〜3くらいで行っています。そこまで高頻度ではないにしろ、京都オフィスの社員であれば一度くらいは行ったことがあるはず、というくらいにははてな社員にお馴染みの店です。

カマルと僕

入社以来結構な頻度で訪れた結果

  • 日曜も通っていたら「日曜も仕事なんですか」と声をかけられる
  • 店員さんが「バターチキンカレーのお客様」的な確認なしに、僕の前にバターチキンカレーを置いてくれる

等、かなり常連感が出てきました。

f:id:t_kyt:20151222103057p:plain

これはgoogleフォトという、カレーと打ち込むとカマルの写真が出てくる便利なサービスです。結構な頻度で行っていることが伺えます。

何故カマルなのか

美味しい

こちらが食べログです。好評なようです。

tabelog.com

味についての感想は人それぞれだと思うので、ああだこうだ言いたくないですが、強いていうなら僕くらいハマる可能性はあなたにもあるということです。とりあえずカマル行くぞ!!

量が選べる

カマルは量が選べます。僕は普段MLサイズですが。食欲が無い日はMサイズのように調節ができます。

近い

はてな京都オフィスから4分でいけるらしい!!近い!

カマルフレンズ

カマルフレンズとは定時後カマルに行くフレンズです。メンバーは不定ですが大体独身男性です。悲しいですね。

はてなでは部活動的なことをやってる人たちも多いですが、カマルフレンズは特にそういうのではなくただの行き場のない人たちです。

はてなの部活動については今年のアドベントカレンダーにも幾つか記事があるので読んでみてください。

airreader.hatenablog.com

elcine.hatenablog.com

yulily100.hatenablog.jp

ちなみに喫煙室コミュニケーション的な世界観でもないです。大抵は幸せについて議論しています。

振り返り

カマルは最高。

おわり

明日のアドベントカレンダーid:hitode909id:amaria のお二人です!お楽しみに!!!

あれから一年、あの TypeScript プロジェクトは今

はてなでアプリケーションエンジニアをしている id:t_kyt です。この記事は はてなデベロッパーアドベントカレンダーを始めます - Hatena Developer Blog の18日目です。

昨日は id:wtatsuru による「はてなで新しくWebサービスを作るときのインフラの作り方 - Hatena Developer Blog」でした。

今回は「TypeScript で実現する MVP アーキテクチャパターン」のその後、1年近く TypeScriptプロジェクトを開発運営してきた話をしたいと思います。

developer.hatenastaff.com

「TypeScript で実現する MVP アーキテクチャパターン」の要約とこの記事の概要

この記事を読む前に前述した記事を読んでいただきたいのですが、ここで簡単に要約します。

  • TypeScriptを用いて既存のフレームワークを使用せずMVPアーキテクチャパターンで開発を進めた
    • AngularJS は学習コスト、React は当時の情報量の少なさから見送った
  • もともとは JavaScript のプロジェクトで途中から Typescript 化したが、移行は容易であった
  • MVPアーキテクチャパターンを採用
    • 「Presenter は View のインターフェイスを知っている」「Presenter は自身の変更を View に反映させる」「View への入力は View 自身が Presenter に伝える」のアーキテクチャ(詳しくは元記事で)
    • View(DOM)とロジックが切り離されているのでロジックのテストが容易

という感じです。

これを踏まえて約一年でプロジェクトはどうなったのか、どうしたいのか、あるいは新規プロジェクトはどうしたのかを

という視点から見ていきたいと思います。

JavaScript からの移行

JavaScript からの移行が簡単だったとはいえ、ただ単に拡張子.ts に書き換えただけでは TypeScript の恩恵をフルにうけることはできません。「動くコード」から「TypeScriptのコード」にするためにはそれなりに書き換えなくてはいけないので、実際僕がやったことあるいはやっていることを紹介したいと思います。

クラス構文で書き換える

素の JavaScript には Class が存在しないので Class っぽいことをしたかったり継承っぽいことをしたかったりする場合、独自に定義する必要があると思います。僕らのプロジェクトでも継承を行う関数を独自に定義し継承を行っていました。TypeScript の導入により classextends は言語機能として提供されるようになったためそちらを利用しない理由はありません。

クラス構文への書き換え、独自に定義していた inherit() なる関数から extends キーワードへの書き換えは思ったよりスムーズに行えました。これはもともとの Javascript が大変お行儀よく書かれていたというのが大きかったので大変助かりました。

型をつける

TypeScript というくらいなので型をつけてこその TypeScript です。とはいえ JavaScript からの移行だと数値や文字列のリテラル以外は型がついていない状態なので適切に型を指定していく必要があります。

ところで TypeScript のコンパイルオプションには --noImplicitAny というのがあり、これをつけると暗黙的な any は使えなくなります。

// 怒られる
var a;

// これはOK
var a: any;

新規プロジェクトでは --noImplicitAny オプションを絶対につけるべきですが、 JavaScript から移行してきたばかりのコードに --noImplicitAny をつけても大量のコンパイルエラーが出てしまいます。そこでとりあえず片っ端から型をつけていき、ぱっと見判断つかない型は any にして、とにかく --noImplicitAny オプションを付けられることをまずは目標にしました。

--noImplicitAny さえつけられれば新規に書き足されたコードでうっかり暗黙的に any が使われることが防げるので、とりあえず新規に暗黙的な any は防止しつつ重要なところからちゃんと型を指定していけば良いという方針です。こうやって JavaScript からの移行が漸進的に進められることも TypeScript のいいところだと思います。

TypeScript の新機能とリファクタリング

TypeScript へ移行した当時からこの一年でかなりの機能が追加され、より便利に記述できるようになりました。実際にプロジェクトで書き換えた部分を紹介します。

Abstract Class

TypeScript1.6 から Abstract Class (抽象クラス)がサポートされました。これによりインスタンスを生成したくない、継承元になるだけのクラスを言語の機能的に明示することができます。

たとえば

class View implements IView {
    presenter: Presenter;

    createPresenter(): Presenter {
        throw new Error('You must implement createPresenter');
    }

    // ユーザーにエラー状態を通知する。
    updateError(): void {}
}

のようにしていた すべてのViewの基底クラスとなる View

abstract class View implements IView {
    presenter: Presenter;

    abstract createPresenter(): Presenter;

    // ユーザーにエラー状態を通知する。
    updateError(): void {}
}

のようにかけます。 class そのものだけではなくメソッドにも abstract もつけられるので、継承していなければ Error を投げるみたいな素朴な実装も書換えることが出来ます。

union types、type guard

例えば引数の型にパターンがあり、型によって処理を変えたいみたいな場合、以下みたいに書けるようになりました

function hoge(pattern: string | RegExp) {
    var regexpPattern = (typeof pattern === "string") ? new RegExp('^' + pattern.replace(/\W/g, '\\$&') + '$') : pattern;
    ...
}

この例はプロジェクトでルーティング処理に使われているコードを少し改変したものです。

少し解説を加えると、union types は typescript1.5.3 からの機能で複数の型のうちどれかみたいな指定が出来ます。またtype guard は複数の型の候補がある場合にif文などで型を絞ることができます。

var a: string | number; // union types: a は string か number

if (typeof a === "string") { 
    // type guard: このブロックでは a はstring型として扱われる
}

type guard は Typescript1.6 からユーザーが定義できるようになってさらに便利になりました。

function isCat(a: any): a is Cat {
  return a.name === 'kitty';
}

var x: Cat | Dog;
if(isCat(x)) {
  x.meow(); // OK, x is Cat in this block
}

さらに 2.0 では switch 文でも有効になる予定です。

TypeScriptに追加された機能は以下でみることができるのでチェックしてみるといいと思います。

What's new in TypeScript · Microsoft/TypeScript Wiki · GitHub

ES6 modules

現在のプロジェクトでは namespace を使って名前空間を分割、生成された js を concat でつなぎ合わせるビルド環境です。

小規模だったらそれで良いのですが、やはりある程度規模が出てくると厳しい面が出てきます。具体的には

  • 依存関係を自分で考えて concat しなくてはならない
  • 外部モジュールの管理ができない
  • 名前さえ知っていればどこからでもアクセスできる

などがあります。例えば

namespace Foo {
    function foo() {...}
}

と書かれたファイルがあった場合、 Foo.foo() はどこからでも呼ぶことができます。またファイル同士に依存関係があった場合自力でreferenceを書かなくては正しく concat できません。

これに関しては新規プロジェクトでは

  • import export を使い依存関係は browserify で解決する
  • 外部モジュールは npm で管理する
  • 型定義ファイルは dts で管理する

という方法をとっています。

これは単なる名前空間であった namespace と違って、正真正銘 module システムなので export していない関数は外から見えませんし import しなければ使えません。

外部のモジュールの管理方法については最近は npm 一択っぽさが出てきたので npm で行っています。

今のプロジェクトもこの方式になるように書き換えてる途中です。

MVP アーキテクチャ

MVP アーキテクチャを採用し、開発運営してきた感想を書きたいと思います。良かった点に関しては冒頭の記事書かれていることその通りのことが実現出来ているので特に補足はないです。

MVPの辛み

MVP採用の経緯にもありましたとおり Presenter つまりロジックのテストが容易です。 しかしながら Presenter の状態が正しく View に反映されているかのテストは簡単にはかけません。実際、僕らのプロジェクトでは View はモック化しているので View に関するテストは行っていません。

結局 Presenter のロジックが完璧でも View への反映を間違えてしまうと元も子もありませんが、View の生成は jQuery で温かみのある作業といった感じなのでミスしている恐れも十分にあります。 View のモックをやめ、 html 片から生成することも考えられますが、DOMのテストはしたくないというのが本音です。これはDOMのテストのめんどくささもそうですが、View 生成用の html 片が本番環境と同じものであると担保していくのかが難しいという面もあります。

冒頭の記事から引用しておきます。

「『DOM に触らずにロジックをテストしたい』という前提が崩れている」とは? (2015-02-17 追記)

MVP アーキテクチャパターンを採用する動機のひとつとして、DOM に触らず (特定のデータに基づく HTML を出力することなく) プレゼンテーションロジックをテストしたいというものがあった。実際、現在は DOM に触る (View に関する) テストは行っていない。

しかしながら、近年はヘッドレスブラウザを用いたテスト実行環境も広まっており、やはりそうした環境で View も含めてテストしたほうがよいのではないかとも考える。その場合、MVP アーキテクチャパターン採用の動機づけが弱くなるのではないかということである。

解決策候補としての React

React は View を生成するためのライブラリです。Reactでは state が決まれば常に同じ View が生成されます。一方で MVP 自体はロジックと View の生成を分離したアーキテクチャなので View の生成部分を React に任せることが可能です。

そこで View への反映は React で行うことによって、本当に Presenter のみをテストすれば良い状況になるのではないかと考えています。

また TypeScript と React との相性についても、TypeScript は JSX (TSX) をサポートしています。TSX 構文内でも IDE の恩恵を受けられ、かなり捗ります。詳しくは id:nobuoka による「UWP アプリ開発に TypeScript + React を導入することの検討 (Node.MSBuild.Npm の紹介)」をお読みください。

UWP アプリ開発に TypeScript + React を導入することの検討 (Node.MSBuild.Npm の紹介) - ひだまりソケットは壊れない

React 導入の不安点

MVP の辛みを解決してくれてなんか良さそうに思える React ですが導入にあたって不安な点もあります。その中でも一番大きいのは「デザイナーさんへの負担」です。React コンポーネントは JSX で記述するため、例えばクラスを付与したいとなった場合は JSX を触る事になります。はてなではテンプレートに関してはデザイナーさんも触る文化ですが、流石に JSX までいくと厳しいのではないか?と感じています。

まとめ

一年前の発表から、JavaScript からの移行、TypeScript の新機能、MVP アーキテクチャと React という視点でプロジェクトがどう変化したかや現状抱える問題意識をまとめました。React の導入による解決はまだ個人のアイデアレベルなので、また半年後か一年後どうなったかを伝えられればいいなと考えています。

おわり

はてなでは、先人の書いた最高のコードに敬意を持って日々リファクタリングを行い、半年後一年後も最高の状態を維持したいと考えるエンジニアを募集しています。

hatenacorp.jp

明日のアドベントカレンダーid:aereal です。お楽しみに!!!!

ISUCON出場した

ISUCONとは

isucon.net

背景

社内のSlackでメンバーを募集していたので良い機会だと思い出場した。

この時点で使用する言語はScalaということになっていた。自分は普段Perl書いててScalaは書けないので不安だったが、まぁどうしようもないクエリ直す位ならできるだろうし、何もできなくて作業見ているだけになったとしても得るものあるだろうという期待が上回ったので参加した。

当日

Scala実装はバグがあるとわかり、ISUCON経験のあるメンバーの得意言語であるPHPへフォールバックした。PHPも触ったことなかったけど、意外と読めるしどうにかなった。PHP最高、一番好きなHPです。

開始1時間くらいはどんなアプリケーションかを眺めてた。その後nginxのログを解析しようとkataribe(https://github.com/matsuu/kataribe)導入を試みるもなんだかうまく行かず、結局生のアクセスログgrepして頑張った。

あとは遅いエンドポイントを特定して、その部分のコードと他のメンバーがだしてくれたスロークエリログを見つつ、コードを改善していった。具体的にはrelationsテーブルはoneだけ引くようにしたり、commentsのN+1問題を解決したりした。

効果はそこそこあって一時は3位までにこぎつけたけど、その後はぱっとせず最終的には予選落ちという結果になった。

良かったこと

何事も一回やらなきゃ実感わかないと思うので、そういう意味では参加しただけでもよかった。正直今までアクセスログみてパフォーマンス悪い部分を特定するみたいなことをしたことなかったのだが、一度やったことある作業とない作業ではそこに関わる技術的な話を聞いた時でも吸収率や興味が違うと思うし、今後きっと有意義さを実感できるはず。

良くなかったこと

途中3位になって、調子乗って同僚チームを煽ったあとカレー食べにいったこと。

その他

チームに誘ってくれた他のメンバーとISUCON運営関係者に感謝です。来年は東京で会いましょう。

今月のロック画面とホーム画面

デザイナーではないです。

ホーム画面

f:id:t_kyt:20150430135628p:plain

かつて少女革命ウテナっていう最高のアニメがあったんですけど、一つ欠点があって高解像度の公式画像が殆ど無いんですね。そういうわけでピンドラの画像なわけです。

時計(というか日付)見難いんですけど、これアラームへのショートカットとして使ってるんで問題無いです。

インストールしたアプリ

BLAZBLUEっていう病気みたいな格ゲーあるんですけどそれのスピンオフ音ゲーです。雑すぎ糞ワロタって印象のアプリですけど、携帯で音ゲーやりたくないしこんなもんだと思います。BBで好きなBGMはSpirit of Fireなんですけど入ってません。

ロック画面

ない。

雑感

とにかくGoogleの敷いたレール最高って感じなのでサードパーティ製のアプリもほとんど無いですし、面白みがない感じに仕上がりました。

あとNexus5のスクショの画質がいつの間にか落ちてるなって気づきが得られました。

この春に始めたいこと・始めたこと

今週のお題特別編「この春に始めたいこと・始めたこと」
〈春のブログキャンペーン 第2週〉

Perl始めた

かつて何かLL言語始めたいなと思った時、真っ先に候補から外した言語だけど、ついに始まってしまった。その時はPython選んだ。

今現在の印象としては、チームに一人倫理観0太郎が交じるとプロダクトごと崩壊しそうというイメージだけど、昔抱いてたほど嫌な印象もない。どちらかと言えば()を省略できるのが良い。ついでに;も省略させてくれ、頼む!って感じ。

筋トレ始めたい

世の中は意外に筋肉あれば解決することが多いと気づいたので筋肉ほしい。だから本当の願望は筋トレ始めたいじゃなくて筋肉ほしい。筋トレなんて少しも始めたくないが、現状筋肉はオンプレでどうにかするしかないし、テクノロジーの敗北といった感じする。

春を感じるとき

今週のお題特別編「春を感じるとき」
〈春のブログキャンペーン 第1週〉

f:id:t_kyt:20150406014606j:plain

猫の毛が生え変わり始めた時。

ちなみにこの時期が一番ふわふわしてるし、擦り寄ってくる(毛が痒いからなすりつけてくる)ので心地いい季節ではある。