はらへり日記

腹に弾丸

近況報告その1

tl;dr

  • 2020/5~2021/4の間、株式会社ELYZAでSoftware Engineerとして働いたのでその話
    • ELYZA社にはとても感謝してるのでELYZA社宣伝が半分、なぜ感謝してるのに退職したのか話が半分
  • 現在は退職して無職ですが、次の職場が決まって落ち着いたので近況の話

近況

特に入社エントリを書かなかったり、大々的に(?)発信してなかったんですが去年の5月に株式会社ELYZに参画してました。

elyza.ai

そこからぴったり1年間、働いて先日の4月末で退職しました。

ELYZAでの1年間

そもそもの入社のきっかけですが、メルカリを退職する前からずっとお話を聞いており興味を持っていました。

メルカリでの退職を決めてからきちんと話を聞き、業務委託期間を経て入社しました。

入社の動機は端的に言うと3つです。

1. 働いてるメンバーが魅力的だった

一番最初のきっかけはメルカリで同僚だった@tarunonさんが声をかけてくれたことでした。

彼は最も尊敬するエンジニアの一人で、理由を書き出すと趣旨が変わるので省略しますが私としては彼が声をかけてくれるということだけでめちゃくちゃ光栄だったし、シンプルに一緒に働いてみたいという気持ちがとても強かったです。

ELYZAはそんな彼が次の職場として選んだ場所だったのでそこで働くメンバーも、チープな言い方ですが優秀なメンバーが多くそれが大きな魅力でした(そしてこれは今も間違いなく変わりありません)。

2. まだ型が決まりきってない小さい組織で働いてみたかった

自身の経験として1社目はアイスタイル、2社目はメルカリで働いてきましたがどちらでも共通して経験したのが「数百人規模から1000人規模まで短期間で急成長する」ことでした。

特にメルカリで働いてた間の企業成長は本当に目まぐるしいもので上場から数え切れないほどの組織改変を経験させてもらいました。

それ自体は基本的に楽しいもので変化し続けることが好きな自分としては大歓迎でしたが、一方で企業が急成長する過程で伴う痛みを社員の立場として感じることも少なくありませんでした。

その痛みに対する基本的な私の考え方としてはその時の立場でできる問題解決はする、立場が問題の大きさに追いつけなくなったら立場を変える or 順応する or 場所を自ら変えるの3択でした。

アイスタイル、メルカリ共に基本的には逃げないようにしつつも最終的には自分が合わなくなったな、という感覚を信じて退職しましたが会社員として働いていく以上、この問題は半永久的に付き合っていく必要があり、一方で変化し続けることを望み続けるのであればどの企業に行っても最終的にはこの選択を迫られ続けることに漠然とした「このままでいいのかな」感を感じました。

それを踏まえて、次の会社では「会社 ⊃ 自分」という構図でなく、「会社 ≒ 自分」として働ける場所で一回チャレンジしてみたい気持ちがありました。

つらつら難しく書いたけど要するに「会社ガー、経営陣ガーと文句垂れる人材やり続けるなら自分が一回そっちの立場になって痛い目見てみよう」と思った、という話です。

3. 技術ドリブンな働き方をしてみたかった

新卒の最初の1年を除いて、今までは特定の技術を掘り下げるというよりはプロダクト作りに必要なものや可能性が広げられそうなものを中心にキャッチアップする動き方をすることが多く、特定技術を掘り下げ続ける同世代や同僚を尊敬しつつ少しコンプレックスを持ってたりしました。

そういう自分にとってML分野の技術を磨きに磨いてそれを活かす、というELYZAのアプローチは新鮮でありチャレンジしてみたい命題でもありました。

実際に働いてみてどうだったか

先述した3つの期待値は入社後もギャップがなく働ける環境でした。

特に2点目は大きいポイントで、自分が入ったタイミングではまだまだサービス開発の型も定まってなかったり、組織レベルで言うとMission/VisionValue言語化されていなかったりしていたので自分の中の今までの貯金をどういう形でアウトプットするかを考える過程は楽しかったです。

また、小さい組織なので大胆なチャレンジもしやすく、1週間業務時間を丸々ML勉強に当てさせてもらったり、Software Engineerでない別のroleにチャレンジさせてもらったりもしました。

なぜやめたのか

一言で言うと自分がモチベーションを感じる志向と会社の向かう方向のミスマッチを感じたからです。

入社したタイミングでは会社として目指したい世界や起業時の想いは当然ありましたが、そこにどのように、どんな方法で向かうのかが言語化されていないフェーズでした。

それが私のいる1年間の間で徐々に解像度が上がっていき、それに伴いミスマッチを徐々に感じるようになった、という感じの流れです。

会社に対する不満等はまぁ0だと言ったら嘘になりますがそもそも今回の私の転職のテーマとして不満は解決する側に回る、というのがあるので生まれてきた不満は全部、自分が解決すべきだったが解決できなかった問題だった、というだけの話だと素直に捉えてます。シンプルに力不足!!!

やめたけどELYZAは本当に面白い会社

自分は目指す方向性が合わなかっただけで、ELYZA自体は面白い会社だと今も思っています。

「目の前にこんなに使える最新技術があって社会に役立てられそうな場面もあるのになぜ実現してないのか」を本気で感じてる日本屈指の頭脳を持つメンバーが集まってる職場です。

実際にはその最新技術と社会実装の間に大きな溝があるわけで、それをいろんな手段で埋められないか、それに本気で取り組んでいる会社です。

個人的にはずっと応援し続けたいですし、合いそうな人がいたら本気でおすすめしたい会社のうちの1つでもあります。

面白そうだな、と思った人はダイレクトに会社に繋げるのでtwitterなりでこっそり教えてください🙌

open.talentio.com

退職後は何をしてるのか

前回の転職活動期間が1回目の緊急事態宣言と被ってゆっくりできなかったり、1年間かけて自分が何にモチベーションを感じるか改めて気づいたのでダラダラしながら自分に合う会社をずっと探してました。

その過程で数社、お話を聞かせてもらいうち2社でお試し入社をして、そのうちの1社に行くことに決めました。

8月前半に親知らず抜き2本目、ワクチン2回目を経て16日から働き始めるのでその際にまた働き始める新しい会社のポストでも書こうかなと思います。

転職活動でお世話になった方々には本当に感謝しきれません。ありがとうございました。

今回いろいろ話を聞いてみて思ったのは、本当に面白そうな会社が増えたなぁということです。

体が5個あったらいいのになぁと何回も思いました(マジで)。

最終的に行く先を決める時も本当に迷いに迷って最後、毎日家でOculs Quest2のコントローラーを振り回しながら発狂してましたがまぁその話は後日!

ここまでつらつら文章書いて思いましたがコロナ下で人間力が下がるに伴い、日本語が本当に下手くそになってきてるのでまた定期的にブログ投稿したいですね。

fabfuel/ecs-deployを使うときに必要なIAM権限

やりたいこと

  • GitHub Actions上でECRにDocker imageをデプロイ後、そのimageを利用するECSに空デプロイしたい

使ったもの

github.com

ちなみに調べると大体は下記を使う方法が出てくる。

github.com

が、今回の私の手元の構成だとtask definitionは別repositoryのterraformでpush, docker imageのもととなるアプリケーションはまた別repositoryという構成だったので使えない。

この構成がそもそもダメという説はある(当方、terraform初心者)。

ハマったこと

以下のエラーが出てデプロイができない

Unknown task definition arn: {task definitionの名前}

流れ、結論を超ざっくりいうと

  • 原因はデプロイに利用していたIAMユーザの権限不足
  • にも関わらずエラーはこれなので「コンソールだと存在するのに見つからないの…?」と混乱してた
  • 実装コードを読みに行くとエラーを雑に丸めてtask not foundとしているだけになっていることを発見
  • aws cliで直接実行して権限不足を確認

足りなかった権限

ecs:DescribeTaskDefinitionが足りなかった

エラーを握りつぶしてる部分

github.com

ここでcatchするClientErrorにはtaskが見つからない以外のエラー、今回なら権限不足のエラーも入ってくるが全部丸めてtaskが見つからないというメッセージが出力されてしまう。

ややこしいのは、この処理の前後では権限不足でもきちんとエラーを出してくれてるので対応できるのだがここにきてこれなので俺が悪いのか、となってしまった。

最終的に必要だった権限

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid":"DeployECS",
      "Effect":"Allow",
      "Action":[
        "ecs:DescribeTasks",
        "ecs:DescribeServices",
        "ecs:DescribeTaskDefinition",
        "ecs:RegisterTaskDefinition",
        "ecs:DeregisterTaskDefinition",
        "ecs:UpdateService",
        "ecs:ListTasks",
        "iam:PassRole"
      ],
      "Resource": "*"
    }
  ]
}

反省

AWS初心者というのもあったが、まだよくわからんものは横着して便利ツール使う前に泥臭く触るべきだったなぁという気持ちになりました。aws cliありがとう。

特に面白い記事でもないんですが同じことで死んでる人のために記事を残す。

該当のコードについては気が向いたらPR出そうと思います。

github.com

近況報告

僕のことを知らない人はどうでもいい記事なので回れ右してください。

目次

  • 退職します
  • 3年間本当にいい経験できたので振り返らせてくれ
  • 今後

退職します

現職のメルカリを退職することにしました。

言語化するのが難しいので理由は詳しく書きません。1つだけ強く書いておくとネガティブな理由ドリブンではないし、この記事では基本的に自慢か会社を褒めるしかしないです。

理由をざっくり言語化するなら、入社前にこの会社でやりたかったことにそれなりに取り組めたこと。

それ以上に自分が想像していなかった体験をさせてもらえたこと。

それらを通じてまた別にやってみたいことができたことです。

sota1235.hatenablog.com

働いている間の経験は本当に濃いもので3年間とは思えないものでした。雑に振り返る。

3年間の経験

USに行った

今でも覚えてる、入社1週間経ってからの流れ

  • メンター「この会社、来週からUS行ってとか普通にあるからパスポート更新しておいてね」
  • 私「あ、はい。(まぁ脅しでしょ)」
  • (2週間後)
  • メンター「再来週からUS一ヶ月ね」
  • 私「ファッ!?!?」

まさか試用期間中の当時はジュニアと言われてもぐうの音も出ない私という人材を貴重なお金を使って出張させたりしないだろうとたかを括っていたので、大急ぎでパスポートを作った記憶があります。

英語もろくに聞き取れず、開発のスピード感についていくのに必死だった。とても、とても楽しかった。

思えばこの経験がきっかけで必死に英語を勉強し始めた気がする。

英語をそこそこ習得した

そこそこ、と称した文脈としては以下の通り。

  • 英語しか喋れない同僚と仕事をすることができるようにはなった
    • しかしながら多少生産性は落ちる
  • 一方で学問的な観点ではほぼ成長していない、と捉えることもできる

「コミュニケーションの手段としての英語」だけを頑張ってきたので例えばTOEICの点数はおそらく入社当時から大きく変わっていないし覚えてる単語数にも大きな変化はない。

しかしながら会社で一緒に働く英語話者と仕事をしたり、海外カンファレンスで登壇者に聞きたいことを聞き出すだけの英語は身についた。つまりはSpeakingとListeningは身についたかなと思う。

これは多少言い訳ではあるのだが、社会人で英語に限らず何かを勉強するって相当難しい。

そもそも週5で労働していて週2日しかないうえに学習したことを反復して毎日活かせるわけでもないので学生の頃よりは圧倒的に勉強したことが身につかない。

私もUS出張直後は参考書を買って勉強しようと試みたが結局挫折してしまった。

それでもここまで英語が喋れるようになったのは、会社の補助でオンライン英会話をほぼ毎日、就業直後のまだ気力があるタイミングでオフィスで受けられたこと。そして実際にリアルの会話をする機会が社内に溢れていたことに限る。

もちろんそれだけだと英語力は頭打ちになるし、実際今はなっていると思う(余談だが、日本に住んでいる外国人の英語はめちゃくちゃ優しいと思う、日本人が英語が苦手なことを無意識に知っている)。

それでも自分がコミュニケーションを取りたい優秀な人に人の力を借りずに話せるのはこれから先もかなりアドバンテージだなと思う。

海外カンファレンス参加

入社当時から今も変わらない文化として、アウトプットしてるものにはインプットの機会を与える、というのがある*1

そのため、というわけではないが自分の力を磨いたり常にベンチマークできるように内外問わずアウトプットをしてきたおかげか、国内外問わず様々なカンファレンスに会社の援助を受けて参加させてもらった。

以下は私が会社に参加させてもらえた海外カンファレンス達。

Firebase Dev Summit 2017の参加権を得て、初めて参加のチャンスが転がってきた時のことが印象的だったのをよく覚えてる。

私は性格上、自己評価も低いし何事も遠慮しがちなので当時のCTO(@sotarok)に顔色を伺うように「私なんかが行ってもいいんですかね…?」っといった感じで相談した。

そしたら「行ってもいいけど、行きたいの?」というような感じで私自身の意思みたいなのを確認されて、若手意識じゃないけど自分は未熟だから許可を取らなきゃいけないとか、100%のリターンが無いとお金のかかる行動を起こしてはいけないみたいな無意識の遠慮がスパンと消えた記憶がある。

それからはチャンスがあったら自分の頭でコストとリターンも考えつつ、一人の開発者としてどうしたいのか、行ったらどうなれる可能性があるのかとかも考えられるようになった。

CTOはもちろん、当時行って来いと背中を押してくれたチームメンバーの人にも心の底から感謝しています。

メルチャン立ち上げ

2017年の7月にメルカリチャンネルというサービスを立ち上げたのですが、初期の開発からその後、約1年ほど運用をしていました(※2019年7月にクローズ)。

sota1235.hatenablog.com

当時の思い出は書き出すと本当に長くなるしまとまりもないので箇条書きで書くと

  • 素早く、最低限の品質で出す開発を経験できた
  • 全く触ったことのなかった技術スタックに触れることができた
    • よかった副産物として、それを外部にアウトプットすることでいいつながりも増えた
  • プロダクトのことを死ぬ気で考え抜いて開発の立場からできることを考える、という入社した際に目標にしていたことを肌で体験できた

私には「これが無いと生きていけない」というなにか(Webサービスじゃなくても何でもいい、なにか)を作るというのが人生の目標のうちの1つなんですが、メルチャンではそれを一定、叶えることができたと実感できたのがよかった。

実際に作ったものに対して利用してるお客様から生の反応がもらえ、ときには対面でフィードバックをもらうこともできました。

クローズまで面倒を見ていたわけではないものの、半永久的にお客様を幸せにし続けることができなかった、という点は未だに悔しさもあるので来世で頑張りたい。

採用へのコミット

一時期ではありますが、採用プロセスに参加させてもらってました。

それに関する自分のアウトプットは振り返ると本当に反省が多いのですが、それまでエンジニアリングやプロダクト作りにしかフォーカスしていなかったので、人にフォーカスしなければいけない採用は非常に刺激的な経験でした。

今振り返るとああできたらよかったのに、というタラレバだらけなので次の場所でもチャンスがあったら頑張りたい。

個人的な目標としては再現性のある採用がしたい…。

Tech LeadというRoleをもらった

2018/4から休職までの間、Tech LeadというRoleを任せられていた。

Tech Leadという言葉を出しちゃうと各位の俺の中のTech Leadが一人歩きし始めてめんどくさいのでTech Lead業に関して語りたい人はぜひ飲みに行きましょう。受けて立つ。

やってみて実際どうだったかなと振り返ると、学びもつらいことも同じくらいあったなと思う。

自分はパーソナリティ的に人に頼るとか任せるということが本当に苦手なので人をうまく使えなかったり、頼ることができずに自分で自分自身に負荷をかけて自滅することがかなり多かった。

そんな感じで自滅してパフォーマンスが落ちたり、いわゆるやらかしをやってしまったことも多々あるが、それでも率直なフィードバックをくれ続けた上長や支えてくれた同じチームのエンジニアたちには本当に感謝しかないです。

それのおかげか、慣れてきてからはうまく負荷分散をしたり少し視点を引き上げて、自分がどうなるかより自チームがどうなるかを考える癖がついたのはよかった。

そうなったらそうなったでまた次の壁があったりするんですがそのあたりはメルカリにいる間に解決できず仕舞いなので次の場での宿題としたい。

WebDB+Press執筆参加した

若干裏話なのかもしれないけど、これはメルカリにいて「あいつ、設計周りうるさいらしい」みたいな感じで誘ってもらえた故に得られた機会でした。

とても楽しかったな、でも執筆は本当に大変でした。

Web Rearchitect

2018年に元のチームを離れてからはずっとWebの作り直しチームにcommitしていた。

speakerdeck.com

今まで取り組んだ仕事の中でぶっちぎりで抽象度が高くて、一番最初のステータスは「今のWebをいい感じのアーキテクチャに作り変えたい」ぐらいの粒度だった。

当然「なるほど?」となるわけで、初期は必死に変数集めをしていた記憶がある。

  • 現状のシステムはどうなってるのか
  • 作りかけの新システムはあるのか、あるならどういう感じなのか
  • 会社のアーキテクチャ方針はどうなのか
  • プロダクトとしての方向性はどうなのか
  • Webチームの未来像はどうなりたいのか
  • ビジネスとバランスを取った時のスピード感はどうなのか

こんなことを必死に考えてた。考えて問題を整理して解きほぐして分解して、最終的に出てきた「n年後のチーム、1年後のチーム、今の状態」の引き算をして、現実的な戦略に落としていった。

この過程が本当に暗中模索だし五里霧中だし、意思決定に迷い続けた記憶がある。迷い続けて周りにいる自分より圧倒的に強いエンジニアたちに相談しまくった。

思えば毎日が意思決定の連続で胃に穴が空きそうなことを決め続けていたと思う。でもそのおかげで技術的な意思決定を一定の品質を担保しながら最速でやること、決めきれない意思決定では未来を見据えて極限までリスクを減らした上で意思決定することができるようになったと思う。

このあたりの能力は抽象度が高い課題でありながら決定権の大部分を会社が移譲してくれたからこそ身につけられたものだと思う。本当に感謝したい。

そして(私が把握してる範囲では)対外的に発表してませんが、メルカリWebのトップ画面は新しいアーキテクチャで動いています。

https://www.mercari.com/jp/

たった1ページに1年半かかってしまった(これは私の実力不足)けど、非常に難易度の高い要求を会社の荒波に揉まれながらも世に出すところまでこぎつけられたのは間違いなくチームメンバが優秀だったおかげです。

私はなぜかチーム運だけは異常にいいと自負しているのですがこの先、自慢できる最高のチームのうちの1つに今のWebチームは間違いなく入ると思います。

僕より強いBackendマンで興味ある人いたら声かけてください。絶対おもしろいと思わせる自信ある。

ISUCON9の運営やった

こればっかりはタイミングが合わないと参加できない活動なので本当に参加できてよかった。

sota1235.hatenablog.com

その他いろいろ

  • Splathonに参加できたのはよかった、もう参加できないと思うとさみしい
  • 収入は引くぐらい上がった、たぶんこの先5年は同じ給料もらえないと思う
    • し、環境も本当によかった。同じ給料、待遇、環境で働ける会社は当分ないだろう、欲しいなら作らないとだめだろうくらいに思ってる
  • 障害対応力が異常についた、障害を起こしてしまっている、という裏返しでもあるが自チームでなくともある程度舵取りできるところまで成長できたのは本当によかった
  • 上場をリアルタイムで経験できたのはエモかった
  • 3年間で露骨に集中力が落ちてきた、年齢に反比例してパフォーマンスが下がらないための工夫を考えないとまずい
  • 仕事じゃないけどRoppongi.js立ち上げたりした。近々復活の噂があるらしいので乞うご期待
  • サービス立ち上げてコード書きまくってそれなりに評価してもらっても俺は弱いという感覚は全然抜けない、もっと強くなりたい…

その他Twitterから見つけた微笑ましい思い出たち

異国の地で心を落ち着かせるためにtweetしてる様子。

当時は言えなかったけど、社内賞をもらって実はかなり喜んでた様子。

有志のファンにより出張の様子がトゥギャられたやつ。

togetter.com

ヘルニアで入院してる図。

会社の開発合宿で温泉宿行ったのにヘルニア手術直後で温泉入れない図。

チームメンバーに飲み会でかけられた温かい言葉。

アイデンティティを確立した様子。

今後

次の職場、という切り口で言うと特に決まってないです。副業しながら適当にのんびり過ごす予定です。

じゃあフリーランスになるのか、というとそういうわけではなくまぁ色々考えてはいるんですが焦って決めずにのんびり考えようという気持ちです。

こういうタイミングをpublicにすると飲みに行きましょう、とか弊社遊びに来ませんか、みたいなのがたくさん来がち(とか言って来なかったら馬鹿にしてくれ)だと思うんですけど、前述の通り当分はのんびりマイペースに過ごしたいので興味がないものとか知らない人とかからのそういうコミュニケーションはしばらく放置しがちだと思います。すいません。

色々決まったらまた雑に報告できると、特にメルカリでお世話になった人にはまたこれ頑張りますと言えたらいいなぁ。

一緒に働いてくれた方は内外問わず、本当に3年間お世話になりました!またいつかどこかで。

*1:今も変わらない文化と言ったが、昔と比べると規模が大きくなってるので能動的に自分のアウトプットをアピールしないと承認は降りづらくなってると思う。とはいえ普通にやってたら認めてもらえると思う。

2019年振り返り

去年

sota1235.hatenablog.com

見返すと何も達成してなくて笑った

サマリ

  • ライブ行きまくった
  • ISUCON9の運営に参加した
  • MP減って休職した
  • 登壇しなかった
  • 副業始めた

ライブ行きまくった

昔から音楽が好きで若い頃の一時期はライブハウスに通う日々を過ごしてたのだけど社会人になってからはあまり行ってなかった。

ただ、時間が経つにつれて名前を知ってるバンドが解散したりメンバーチェンジしたり、亡くなってしまうことが増えてきた。

特に去年はそういうのが多かったので年初に「人生で一度は見たいバンドのライブに全部行く」という目標を立てた。

結果、一回見て満足するどころかもう一回見たくなるし対バンやフェスで見つけた新しい推しのライブにも行き始めてしまって芋づる式にライブ本数が増えた。

scrapbox.io

上に全部書いたが実に47本のライブに参加した。満足。

来年もたぶんいっぱい行く。

ISUCON9運営に参加した

sota1235.hatenablog.com

学びが深かった。

MP減って休職した

9-11月の間、MPが減って休職してました。

邪推されるのもめんどくさいんであんまopenにしてなかったんですが今は元気に復帰してるのでお気づかいなく…!

意外と休んでも人生どうにでもなるんで死にかけてる人はみんな休んでほしいなと思ってます。

登壇しなかった

去年は割とカンファレンスや勉強会で登壇する機会が多かったんですが今年は仙台PHPカンファレンスの一本だけでした。

理由として発表準備に時間をかけるよりインプットに時間を割きたかったのと、単純に飽きもなくはないです。

今年それなりにいろいろ勉強できた気がするので来年はちょいちょい話したいですね。

副業始めた

以前も副業をしてたことはあったのですが、今年は技術アドバイザーなるものを副業として始めました。

開発者としてでなく、アドバイザーとして副業をするのは初めてなので結構不安もあるのですが今の所それなりにうまく行っているんじゃないかなぁと思います(少なくとも頂いてる対価と同等以上の価値を出せてると信じたい)。

アドバイザーとなると基本的には自分が持つ知識やノウハウを価値として提供し続けることになるのですが、提供する一方で私自身も学びになることがとても多く、楽しくやれています。

このあたりも機会があれば言語化したい。

2020年

社会人になって5年経ち少々人生に飽きが出てきた感覚があるのでaikoのライブと同じくらい熱中してやれる何か(仕事でも仕事でなくても)を見つけられたらいいかなぁと漠然と思ってます。

今年もいっぱい迷惑をかけてみなさんに助けられた年でした、来年も引き続き助けてくれたら嬉しいです。

ISUCON9予選でフロントエンド周りの実装を担当した話

ISUCON9に参加した方、本戦までお疲れさまでした!

今年のISUCON9では運営として予選の作問に携わり、フロントエンド周りを担当しました。

その際に得た知見や実装なりの話を雑に書こうと思います。

話で出てくるソースコードは全てここにあります。

github.com

Excuse

  • 個人としての雑記です
  • 本業フロントエンドエンジニアではないので詰めが甘い部分がかなり多いです
    • 本業フロントエンドエンジニアからのマサカリ待ってます
  • ISUCON運営として作るフロントエンド、という視点で書いていきます
    • 願わくば未来のISUCONフロントエンド担当者に届け

あとは講評や解説は公式Blogで出してるのでそのあたりをぜひ読んでください!

isucon.net

あとはBackendやベンチマーカーの実装に関しては@catatsuyさんの記事とGitHubのコードを合わせて読むことをオススメします。

medium.com

私とISUCON

私自身のISUCONとの関わりはこんな感じです。

  • ISUCON5から毎年参加している
  • 毎年予選落ちで地団駄を踏む
  • 去年は覚悟を持って望んだものの予選落ち、ビール禁止を決意する

といった感じでした。

今年ももちろん再挑戦、悲願の予選突破を目指そうと思っていたのですが自分の会社に作問が回ってきたのでこんなチャンスなかなか無いだろうと思い運営として参加しました。

技術スタック

アプリケーション自体、どれくらいフロントエンドを作り込むかの点に関しては後述する理由でSingle Page Application(SPA)を採用しました。

私自身、以前にSPAを作ってからかなり年月が経ってしまっていたのと、今回は多少小回りが効かないとしても動くことが最優先だったのでcreate-react-appを利用しました。

丁寧にテストを書く余裕はないだろうと想像できていたのでなるべくエンバグしないようTypeScriptも導入しています。

create-react-app.dev

create-react-appを最後に触ったのは2年ほど前だったのですが、その時よりもかなり使いやすくなっていて最高でした。

no config(厳密には違うのかな)でhot reload、transpile等々動くのは楽だしソースコードと設定周りが強く依存し合うこともないので個人的に重視している捨てやすさも十二分にあるなという印象です。

これに合わせてState管理周りはRedux、UI周りはMaterial UIを導入しています。Material UIマジで最高。

他に使ってるものは興味あればpackage.jsonを覗いてください。

フロントエンドの方針

以下の点を方針として立てました。

  • Backendのテンプレート移植の負担をなるべく無くす
  • 競技者がアプリケーションを理解しやすいフロントエンドを作る
    • 機能が理解しやすい
    • 壊れたときに原因がわかりやすい
    • フロントエンドでデバッグしたいときにやりやすい

Backendのテンプレート移植の負担をなるべく無くす

ISUCONでは例年、多くの参加者が楽しめるよう複数の言語実装を提供しています。言い換えるとブラウザで開くと全く同じに見えるアプリケーションを複数種類の言語で実装するということです。

その際、コストとして大きいのがHTMLレンダリング周りの移植です。

Backend側での実装移植はよほど言語に依存した実装にしていなければ難しくないですが、テンプレートエンジンは言語やライブラリによって機能の差異が多く結構大変です(※過去の運営をやったわけではないですが、一緒に運営をした人の体験談から想像するのは難くなかった)。

なので基本、Backend側はHTTPのAPIを提供するのみ、HTMLのレンダリングはブラウザ上でする、という方針になりました。ゆえのSPA。

競技者が機能を理解しやすい

今回のアプリケーションは一言で言うならば椅子のフリマアプリでした。

近年は様々なフリマアプリが使われるように身近に感じられるようになりましたが、とはいえ競技者全員が直感的に機能を理解できるとは限りません。

特に今回は配送機能や決済機能周りはAPIのI/Fや仕様書のみから全てを読み解くのは容易ではなかったので、フロントエンドの画面を触ることでそれぞれのAPIの存在意義や機能を理解できるよう心がけました。

このあたりは以下のissueで議論しました。興味があれば覗いてみてください。

github.com

壊れたときに原因がわかりやすい

参加したことがある方はよくわかると思いますが、ISUCONでは初見のアプリケーションを壊さずにドラスティックに書き換えていく必要があります。

その過程で起こる問題として意図しないエンバグやその原因調査です。

フロントエンドではこのあたりにすぐに気付けるようにし、原因調査のヒントとなるようにすべきです。

それにあたり以下のような方針で実装しました。

現実のアプリケーションではAPIのエラーをそのまま表示したり、criticalなエラーを全て500として扱うことは望ましくないのですが今回は競技者にいち早くエラーに気づいてもらう&エラー文をDeveloper Tool等を見ずに知ってもらうためにこのような構成にしています。

BUMP機能のフィードバックUI追加 by sota1235 · Pull Request #411 · isucon/isucon9-qualify · GitHub

フロントエンドエラー周り修正 by sota1235 · Pull Request #299 · isucon/isucon9-qualify · GitHub

ログイン/新規登録時のエラー表示方法を変更 by sota1235 · Pull Request #417 · isucon/isucon9-qualify · GitHub

その他工夫したこと/ハマったこと

React Routerで同ページ間の遷移もActionが発火する

今回はReact RouterをReduxにつなぎこむためにsupasate/connected-react-routerを利用しました。

このconnected-react-routerはReact Routerでページ遷移が発生した際に@@router/LOCATION_CHANGEというActionを発火させます

このActionを見ることで例えば「商品ページから別ページへ遷移したら商品ページのstateを初期状態に戻す」といったことが可能になります。

私のState管理の実装は以下のようになっていました。

  • Page Componentのレンダリング時(constructor)にデータをfetchするためのActionを発火
    • データが来るまではloading componentを表示しておく
  • React Routerのページ遷移アクションが発生したらデータとローディングステータスのstateをリセット

しかし、このActionは同じページ間を遷移する場合にも発火してしまいます。

結果として

  • ユーザからはページ遷移していない(同じページにとどまっているだけ)のにデータリセットのが走る
  • Page Componentはレンダリングされない(既にされている)のでデータfetchは走らない

というバグが発生しました。

対応策として、@@router/LOCATION_CHANGEが発火したら遷移前のpathと遷移後のpathを比較し、違う場合のみカスタムActionを発火するmiddlewareを実装。それをもとにState制御することで解決しました。

import { Dispatch, Middleware, MiddlewareAPI } from 'redux';
import { AppState } from '../index';
import { LOCATION_CHANGE } from 'connected-react-router';
import { pathNameChangeAction } from '../actions/locationChangeAction';
import { ActionTypes } from '../actions/actionTypes';

// react-routerのページ遷移発火時、pathnameが変わった場合は独自のactionを発火する
const checkLocationChange: Middleware = <S extends AppState>(
  store: MiddlewareAPI<Dispatch, S>,
) => (next: Dispatch<ActionTypes>) => (action: ActionTypes): any => {
  const { getState, dispatch } = store;
  if (action.type !== LOCATION_CHANGE) {
    return next(action);
  }

  const { router } = getState();
  const currentPath = router.location.pathname;
  const nextPath = action.payload.location.pathname;

  // 遷移前と遷移後が同一pathなら何もしない
  if (currentPath === nextPath) {
    return next(action);
  }

  dispatch(pathNameChangeAction());
  return next(action);
};

export default checkLocationChange;

github.com

タイムラインのバグ修正 by sota1235 · Pull Request #285 · isucon/isucon9-qualify · GitHub

同一Routeで異なるpathへの遷移時のState管理がうまくいかない

先程の実装で十分かと思いきや、まだカバーできないパターンがあります。

例えば/items/:item_idというRouteがある際に、/items/8から/items/9へ遷移したパターンです。

このパターンの際に起きるバグとして

  • /items/8へアクセス
  • Page Componentがレンダリング、constructor内でfetch Actionが発火
  • ページレンダリング完了
  • /items/9へ遷移
  • ページ遷移のカスタムアクションが発火
  • stateがリセットされる
  • しかしPage Componentは再レンダリングされないため、fetch Actionが発火しない

これを解決するためにReact ComponentのgetDerivedStateFromPropsメソッドを利用しました。

  static getDerivedStateFromProps(nextProps: Props, prevState: State) {
    const nextLoading = nextProps.loading;
    const nextPageUserId = Number(nextProps.match.params.user_id);

    // ページ遷移を確認した場合はデータ取得を行う
    if (nextPageUserId !== prevState.currentPageUserId) {
      nextProps.load(nextPageUserId, nextIsMyPage);

      return {
        ...prevState,
        loading: true,
        currentPageUserId: nextPageUserId,
      };
    }

    return {
      ...prevState,
      loading: nextLoading,
      currentPageUserId: nextPageUserId,
    };
  }

上記のnextProps.match.params.user_idには例えば/users/:user_idというRouteにアクセスした際に:user_idに入る任意の値が入ります。

これにより同じRoute間の遷移、かつ違うpathでも正常に動作するようにできます。

github.com

Material UIのtheme styleがうまく反映されない

Material UIにはBase Colorやfont-size等をglobalで統一して管理したいときに便利なTheme機能があります。

v4-3-3.material-ui.com

今回も色の調整等でこの機能を利用していたのですが、特定のページの一部コンポーネントにスタイルが適用されないバグがありました。

github.com

色々調べていくと同じバグに遭遇してる人は何人か見つけたものの、どの方法を試してもうまく修正できなかったのでバグが再現するコンポーネントにのみnesting themeを同じテーマを使って行うというハックでバグを回避しました。

github.com

f:id:sota1235:20191005121120p:plain
勝利宣言

Special thanks

今回、きちんとSPAを作り込むにあたり主にデータローディング周りの設計に結構悩まされたのですが、私のggり方が悪いのか体系的に解説したいいドキュメントや記事を見つけられませんでした。

自分で車輪を再発明するしかないかと諦めかけましたが、藁にもすがる気持ちで@hiroppyソースコードを読みにいったら抱えてた疑問や迷いに対する答えが全てありました。

github.com

最後に

ISUCON9予選に参加して競技を楽しんでくれた皆様、メルカリの出題チームのメンバー、そして運営の皆様、本当にありがとうございました!

来年は(きっと)参加者として予選参加して本戦に行きたいと思います。

f:id:sota1235:20191006002923j:plain