2021年の振り返り

生活

去年の12月に東京から福岡に引っ越したので、今年は福岡での生活に慣れる年になった。

引っ越す前までもほとんどリモートで仕事していたので、仕事に関してはそうでもなかったけど、子ども関連が大変だった。新しい保育園のあれこれを準備したり、送迎フローを確立したり、休日の遊び場所を探したり。今はもうだいぶ慣れたので子ども関連は引っ越す前と同じぐらいの労力になった気がする。

それとこれまではオフィスで仕事したり家で仕事したりで環境が変わるのが嫌でMacBook一台で開発することにこだわっていたけど、基本家でしか仕事しなくなるということで外付けのディスプレイとかキーボードを買って仕事環境を整えた。ウルトラワイドのディスプレイが便利すぎてもう元の生活には戻れなくなったね。

ただ、引っ越してから緊急事態宣言やらなんやらでほとんど外に食べに行くことができなくて期待していた福岡のおいしいご飯はまだあまり体験できていない。そのへんのスーパーで売ってるお刺身が普通においしかったりするのは嬉しいけど、モツ鍋とか水炊きとか屋台とかもっといきたかったね。とはいえ10月くらいからは徐々に行けるようになったのでこれまでの分を取り返しにいっている。

f:id:hokaccha:20211230201638j:plain

これは記憶の中では一番おいしかった中洲の屋台の焼きラーメンだけどけっこう飲んでる状態で食べたのでお店の名前もわからないけどもう一回食べたい。

総じて福岡はいいところなので引っ越して良かったと思っている。何よりも家が家賃半分以下で倍以上広くなったのがいい。

仕事

今年の前半はクックパッドレシピサイトのフロントエンドのNext.js移行を引き続きやっていたり、春インターンでNext.jsの講座をやってYouTuberデビューしたり、夏インターンで講師業をしたりしていた。

その後なんやかんやあって9月にUbieに転職したのが一番大きい出来事だった。

Ubieでは主にユビーAI問診とかユビーAI受診相談のプロダクト開発をやっていた。技術的にはGCPとかKotlin、Spring Bootが初体験で新しいことを学ぶのは楽しいと同時に、学びながら開発するのはスピードがでなくてつらいなという気持ちも味わった。

プロダクト開発の傍ら、開発環境のセットアップとか日常の開発環境のメンテナンスみたいなものに課題を感じて開発体験を最大化するチームに入ってそのあたりの改善でM1 Macで開発できるようにしたりしていた。本当はもう少しこのあたりをやりたいんだけど、来年は紆余曲折あってレガシーRailsの発破解体をやることにした。前職でも同じことをやるチームをリードしていたので、クソッ何回転生してもレガシーRailsをやることになっちまう、という感じだが好きでやっているのでやっていくぞという気持ち。

それとアクセシビリティのチームに入ってeslint-plugin-jsx-a11yを導入したりUDフォント導入の実装をしたりもした。

入社して4ヶ月しか立ってないけど振り返ると意外と色々やっとるな、という感じがする。Ubieはみんなやる気に溢れていて仕事していて楽しいし、医療ドメインも開発も面白いので来年も色々やっていくぞ。興味ある人は遠慮なく声かけてね。

登壇とかイベントとか個人開発とか

Kaigi on Rails というイベントに登壇したり、JSConf JP に Ubie でスポンサーとして参加してブースに立ったりした。どちらのイベントも SpatialChat とか reBako を使ってブースや懇親会がおこなわれていて、以前オフラインでカンファレンスに参加していたころの体験に近いものを得られてとてもよかった。

個人開発はほとんどやっていないけど、クックパッド時代の同僚が僕が作っているBdashというSQLクライアントのサーバー実装であるBdash Serverというのを作ったのでそれの機能開発を少しやったりしていた。

あとはAdventarにサイボウズさんから協賛いただいて初めてAdventarがお金を生んでうれしかった。

まとめ

今年は福岡での生活に慣れるのと転職のあれこれであっという間に過ぎ去ってしまった。個人開発とかアウトプットはほとんどできなかったけど仕事にも生活にもだいぶ慣れたので来年はブーストかけてやっていきたいね。

RailsエンジニアのためのNext.js入門

というタイトルで先日 Kaigi on Rails 2021 で話してきました。

プレゼンで話せなかった内容なども含めてブログ記事にも書いておきます。

Intro

Railsのことはけっこう知ってるけどNext.jsについて何も知らないという人をターゲットにしてNext.jsとは一体何なのか、いつどこで使えばいいのか、具体的にNext.jsのどういうところがいいのか、どういう機能があるのかという話をします。

最終的には普段Railsを書いているエンジニアが、Next.jsよさそうなんで使ってみようかな?と思ってもらえるといいかなと思っています。

Next.jsとは何か

Next.jsのトップページを見てみましょう。

f:id:hokaccha:20211022225231p:plain

The React Framework for Production

と書いてあります。これは読んで字のごとくですが、Next.jsというのはReactをベースにしたフレームワークです。

では具体的にNext.jsの何がいいのか、というのはその下に書いてあります。

Next.js gives you the best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more. No config needed.

後半のほうは具体的な機能の説明が書かれていますが、個人的には"best developer experience"の部分、最高の開発体験を提供すると書いてあるところがけっこう重要で、Next.jsはめちゃくちゃ開発体験がよいのがいいところの一つだと思っています。

これはRailsと共通するところがあると思っていて、僕がRailsを好きな理由の一つは開発体験の良さなので、RailsエンジニアもきっとNext.jsの開発体験は好きになれるんんじゃないかと思います。

Reactをベースにしたフレームワークということで、Next.jsはフロントエンド方面の開発体験がよさそうなWebアプリケーションフレームワークぽい、というのはなんとなくわかったと思いますけど、じゃあどういうときに使うのがいいんでしょうか。Next.jsはRailsを置き換えて次世代のRailsになるようなフレームワークなんでしょうか?答えは明確にNoです。

RailsとNext.jsを比較するために、まずはRailsの構成要素を見てみましょう。

f:id:hokaccha:20211022225346j:plain

DBがあってActive RecordがあってAction PackがあってAction Viewがあって、必要に応じて使うActive JobとかAction Mailerみたいなものある、と。まあこれはRailsエンジニアに対しては特に説明しなくてもいいと思います。

それからフロントエンドに各種もろもろのやつがありますね。今回はNext.jsとの比較ということでフロントエンドのコンポーネントについては解像度高く細かく書いていますが、まあ色々ありますね。ではNext.jsはRailsでいうどの領域をサポートするものでしょうか。

こんな感じです。

f:id:hokaccha:20211022225405j:plain

Next.jsがカバーする領域はAction Packより下のレイヤーです。フロントエンドのフレームワークなので、Viewより下を見ることについては特に疑問はないと思いますけど、Action Packのレイヤーもカバーしているところに注目してください。

Action Packというのはリクエストを受け取って、ルーティングしてコントローラーにリクエスト渡してレスポンスをクライアントに返すような責務の層です。つまりNext.jsはHTTPのサーバーを起動してリクエストをハンドリングするという機能も持っているということです。当然ですけどサーバーサイドのプロセスはNode.jsで動きます。

それから、Active Recordのレイヤーがないというのも重要なポイントですね。Next.js自信はDBの操作レイヤーを持ちません。もちろんサーバーサイドでNode.jsのプロセスが動くのでDBに接続してデータを読み書きすることは可能ですが、そこは自前で書く必要があります。

なんでNext.jsを使うのか、Railsではダメなのか

Next.jsがRailsでいうとどのあたりの機能を提供するフレームワークかはわかったと思いますけど、Railsの一部のレイヤーしかサポートしないのであれば全部入りのRailsのほうがよくない?という話になりそうなので、そのあたりについて説明していきます。

具体的な理由は、Railsのフロントエンド開発がつらい、というところですね。これらは僕自身RailsでViewをずっと書いてきていてペインに感じるところです。

1つ目は独自路線な技術スタックです。一昔前のsprockets、Webpackerとか最近だとHotwireやImportmapなどですね。それらがダメ、React最高というつもりはイチミリもないですし、流行ってるからReactがいいというつもりも全くないんですが、既存のエコシステムに乗っかれるというメリットはやっぱり大きいですね。採用とか開発のモチベーションにも関わってくるので独自路線の技術選定だとそのあたりが厳しくなるのかなと思ってます。JavaScriptのbundler界隈が盛り上がるまえにsprocketsを発明したDHHはすごいと思っているし、僕自身sprocketsは好きだったんですけど、最近はメインストリームとの乖離がやはり気になりますね。

それと、やはり単純にReact、TypeScriptの体験がよすぎるのでそこを最大化する技術スタックでやるのが筋がいいんじゃないかというのもあります。

それから、JavaScriptRailsのViewが混ざるとどこにViewが書いてあるかわからなくなる問題、あると思います。ただこのあたりはRails7でだいぶ改善するのかなと思っているので期待したいところではあります。

独自路線が嫌でReact使いたいなら、がわだけRailsでbodyの中だけReactにすればいいのでは、という話もあったりするんだけど、それはそれでPre Renderingできないという問題があってつらかったりします。airbnbhypernovaという別プロセスでNode.js実行してサーバーサイドでJSをレンダリングするという手法もあったりしますが、これはこれで運用がきついです。Pre Renderingできないことによる問題については後半で詳しく説明します。

これらのペインをNext.jsだと解決できるのでしょうか。答えはYesです。もはやデファクトスタンダードになったと言ってもよいReactを中心に据えて開発できて、言語はJavaScript、もしくはTypeScriptに統一できます。最近だとよほど特別な理由がない限りはTypeScriptを使うべきだと思います。

こういった理由でNext.jsを使うといい感じになるよ、という話ですが、逆にNext.jsだけ使えばRailsいらなないの?という話はどうでしょう?これはNoです。

Next.jsだけだと、やはりActive Recordのレイヤーがないのがきついですね。これを独自にやろうとするとかなり厳しいことになると思います。Node.jsのORMであるPrismaはわりとよさそうですし、フルスタックスレームワークのBlitz.jsなども出てきましたが、個人的にはRailsの生産性には及ばないかなという印象です。

RailsとNext.jsを使ったアーキテクチャ

ですので、現状だとRailsなどのバックエンドの得意なフレームワークと組合わてNext.jsを使うのがベターな選択肢だと考えています。具体的にどういう感じのアーキテクチャになるかを見ていきましょう。

f:id:hokaccha:20211022230158p:plain

まずはこんな感じのシンプルな3層アーキテクチャです。プレゼンテーション層にNext.js、アプリケーション層にAPIモードのRailsをおきます。もちろんこれが唯一の最適解ということではなくて、アプリケーション層とプレゼンテーション層のプロセスを分けるのは実装や運用面で複雑化するデメリットはあるのでチームメンバーのスキルセットやプロダクトの特性によって最適解を選ぶ必要があります。

例えば僕は両方それなりに得意なので、たぶん今自分が一人で何かプロダクトを作り始めるなら、0->1のスピード優先フェーズであってもこの構成にしますね。それぐらいNext.jsの開発体験がよくてトータルだと速度がでると思います。逆にチームメンバーがReact何もわからなくてRailsでViewを書くことに抵抗のないケースではRailsだけで済ませるのがいいですね。まあこれは当たり前の話ですね。

3層アーキテクチャにしておくメリットは、クライアントがWeb以外にもネイティブアプリなどがあるパターンに対応しやすいというのもありますね。

f:id:hokaccha:20211022230206p:plain

いつ増えるかわからないクライアントに対応するために無理にこの構成にするのはやめたほうがいいけど、最初からWebの他にもクライアントがあることがわかっていればWebアプリケーションのためのプレゼンテーション層としてNext.jsを挟むのは自然なアーキテクチャと言えると思います。

それからシステムが大きくなってきて、マイクロサービス的にバックエンドのアプリケーションを分けていくパターンがあると思います。

f:id:hokaccha:20211022225542p:plain

ほとんどのサービスにマイクロサービスなんていらんのやという論調もありますが、まあそれは一旦置いておいて、ここではそういうシステムがあるとします。そういう場合にNext.jsをWebフロントエンドのためのプレゼンテーション層として立てて各マイクロサービスのアグリゲーションもここでやってしまうという方法もあります。

ただこういうケースではアグリゲーションレイヤーを一枚噛ませるのがベターだと思います。

f:id:hokaccha:20211022225550p:plain

現状だとGraphQLがこのアグリゲーション層の候補にあがりそうなのでここではGraphQLとしていますが、別にGraphQLである必要はありません。

このアーキテクチャは前職のクックパッドで採用したアーキテクチャなんですけど、偶然にも現職のUbieでも同一のアーキテクチャになっています。GraphQLのサーバーはクックパッドだとNode.js、UbieではKotlinだったりしますが、アーキテクチャとしては同じです。それとUbieでは裏のマイクロサービス群はほとんどがKotlinですね。

GraphQLの話をしだすと脱線してしまうのでまた別の機会にするとして、システムが大きくなってきたときに取り得るアーキテクチャとしては現状ではそこそこ現実的な解の一つではないかと思っています。

Next.jsの機能

次にもう少し具体的にここがすごいよNext.js、というのを見ていきましょう。

Zero Config

Next.jsの開発環境をつくるのは、まずNext.jsやReactを含むいくつかのモジュールをインストールして、Reactコンポーネントのファイルを一個おいて、next devコマンドを実行する、以上です。

$ yarn add next react react-dom
$ yarn add --dev typescript @types/react
$ mkdir pages
$ cat > pages/index.tsx <<EOL
const TopPage = () => {
  return <h1>Hello Next!</h1>;
};
export default TopPage;
EOL
$ yarn run next dev

WebpackとかTypeScriptなどのめんどくさい設定は一個もありません。正確にはtsxコンポーネントを作ったときにtsconfig.jsonが自動生成されますが、それぐらいです。

create-next-appみたいな雛形を一発で生成してくれるrails newみたいなものもありますが、個人的には雛形生成系は何が必要で何が必要じゃないかが全然わからないので、必要なものだけを足していきたいので、実際にアプリケーションを書くときもこのような手順でセットアップしています。

とはいえ同じ雛形生成系のrails newはそう思わないのはなんででしょうかね。全部必要だからかな。

File-system Routing

  • GET /
  • GET /posts
  • GET /posts/:id

このようなルーティングをつくるのにRailsだとroutes.rbにこんな感じで書きますよね。

Rails.application.routes.draw do
  root "root#index"
  resources :posts, only: [:index, :show]
end

まあこれは特に説明の必要はないですね。Next.jsだと、設定を書く必要がなくて、ディレクトリ構成を以下のようにします。

pages/
├── index.tsx
└── posts/
    ├── [id].tsx
    └── index.tsx

ファイル名、ディレクトリ名から自動的にルーティングを生成します。pagesというディレクトリ名だけが特殊で、Next.jsはpagesディレクトリ以下のファイルを特別に扱います。また、postsの下に[id].tsxというのがありますが、これで任意のパラメータをURLから受け取ることができます。この大かっこの記法は初見だとだいぶ気持ち悪いしシェルの入力とかとも相性悪いんですけど、まあこれは慣れてください。

pages以下のファイルの中身はこんな感じになっています。

Reactコンポーネントをexport defaultする、というのが規約です。さっきの[id]のidを受け取るにはnext/routerを使って受け取ることができます。ここで受け取ったidをAPIに投げてデータを取得する、などが一般的なフローですね。

import { useRouter } from 'next/router'

const PostPage = () => {
  const router = useRouter();
  const id = router.query.id;
  return <h1>Post ID: {id}</h1>;
};

export default PostPage;

Performance Optimization

次はパフォーマンス最適化の話ですね。パフォーマンスといっても色々なパフォーマンスがありますけど、ここでいうパフォーマンスはCore Web Vitals的なパフォーマンスです。LCP, CLS, FID という指標があります。

f:id:hokaccha:20211022225617p:plain

Next.jsのパフォーマンス最適化は色々あって、めちゃくちゃパフォーマンスの最適化に力を入れているフレームワークといえます。Webパフォーマンスの最適化は色々なプラクティスがあるのでこの時間で説明はできませんが、Next.jsデフォルトで様々な最適化をおこなってくれるので、何も考えずに使うだけである程度は速くなる、と雑に思ってもらえればいいと思います。もちろん遅いコードを書けば遅くなるので過信は禁物ですが。

next/imageというコンポーネントを使うと画像の最適化をしてくれたり、next/scriptで外部スクリプト読み込みの最適化を行ってくれます。Web fontの最適化などもありますね。

それから、next/linkを使って画面遷移をするとクライアントサイドで画面遷移します。Railsエンジニアの人にわかりやすいように説明するとturbolinksということです。いい感じのturbolinksです。ようするに画面遷移するときにHTMLを丸ごと読み直すのではなく、遷移するページのコンテンツをJSで取得してきてURLをJSで書き換えることでページ遷移したように見せる、というやつですね。これによって高速な画面遷移を実現できます。turbolinksはRailsを普段使いしている人の間でも悪名高い存在ですけど、next/linkはよくできているので安心安全なturbolinksと思ってもらうといいですね。

Pre Rendering

Pre Renderingの説明の前にPre Renderingじゃないケースの説明をします。Client Side Rendering(以下CSR)です。

f:id:hokaccha:20211022225637p:plain

CSRはその名の通りクライアントサイドのJavaScriptでViewを描画するというやつです。CSRでは、ブラウザはまず特定のページのURLにHTTPリクエストしてHTMLを受け取ります。このときHTMLはCSSとJSを参照するタグだけ書いてあって中身は空です。

ブラウザはJSの参照先にリクエストしてJSファイルを受け取ったらJSを実行します。JSにはAPI呼び出しのコードが書いてあるのでAPIにリクエストしてデータを受け取ったら、そのデータを元にViewを構築します。これでやっとユーザーにはコンテンツが表示されるわけですね。

CSRにはいくつか問題があります。

  • 初期描画のパフォーマンスがでない
  • JavaScriptを実行しないクライアントに弱い(OGP, SEO

初期描画のパフォーマンスがでないということですね。上記の図のようにユーザーにコンテンツが表示されるまでブラウザは何度もリクエストを発行する必要があります。

もう一つがJavaScriptを実行できないクライアントに対してコンテンツを提供できないということです。SEOについては最近はGoogleのボットがJavaScriptを実行してくれるので問題になることは少なくなってきましたが、OGPなどは依然として問題になります。例えば商品詳細ページのURLをSNSに貼ったときに全部JavaScriptで実行されるページだとその商品の情報がSNSに表示されなくなるわけですね。いい加減OGP問題どうにかならんかと思ってますけどどうにかならないですかね。

画面の一部をCSRするような場合でも問題になるようなケースがあります。例えばメインのコンテンツはサーバーサイドでレンダリングするけど、特定のユーザーだけにバナーを出す、みたいなのをクライアント側でやる場合ですね。これは初期描画やOGPなどは問題になりませんが、CLSを悪化させてしまう可能性があります。このようにCSRはいくつかの問題点を抱えています。

ではどうするかというと、クライアントサイドでレンダリングするのではなく、あらかじめサーバーサイドでHTMLを作って返せばいいということになります。Railsエンジニアにとっては、え、何いってんの、普通じゃない?と思うかもしれないですけど、まあそうです。ただJavaScriptでViewを作る場合、基本的にはクライアントサイドでレンダリングするのが普通なので、それをいかにしてサーバーサイドでレンダリングするか、という発想になるわけですね。

方法は2つあって、1つ目がリクエストを受けたときにサーバーでHTMLを返します。Server Side Rendering(以下SSR)と呼ばれるやつですが、Railsが普段やっているのと同じと思ってもらって大丈夫です。

もう一つがStatic Site Generation(以下SSG)で、予め静的にHTMLを作っておいてそれを返すという手法です。これはJekyllとかHugoみたいな静的サイトジェネレーターを想像してもらうとわかりやすとい思います。

CSRSSR、SSGがそれぞれがどういうケースで有効なのかについても説明しておきます。

まずCSRですが、これはさっきダメな点をいくつかあげたんですけど、いいところもあって、実装や運用がシンプルになるということで。実装はクライアントサイドのことだけ考えればいいし、運用は基本的には静的アセットを配信するだけで済みます。場合によってはnginxとかでルーティングだけ必要になる可能性はありますが、その程度です。なので先程あげたCSRの欠点が受け入れられるのであればCSRを選択するのは良い選択といえます。例えばGmailみたいなアプリケーション系とか、管理画面とかそういうところですね。

次にSSGですが、これはビルド時にコンテンツが決まるようなサービスで選択します。基本的には静的なHTMLを配信することになるのでパフォーマンスは最強なので静的にビルドできる場合はSSGを選ぶべきですね。ブログとかメディア系のサイトが典型的なユースケースです。

最後に静的にコンテンツは決まらないけどパフォーマンスもOGPも妥協したくないというパターンでは泣く泣くSSRを選択します。ただ社会というのは得てして厳しく、要件的にSSRじゃないと無理、というケースはそれなりにあります。例えばクックパッドのレシピページでは300万超えのレシピを静的に生成はできないし、OGPもパフォーマンスも重要な要件でした。

それからNext.js独自の機能してIncremental Static RegenerationというSSRとSSGの間の子のような機能もあります。これは基本的にはSSGで静的コンテンツを作りつつ、コンテンツがない場合はオンデマンドでファイルを生成したり、生成したファイルの生存期間を超えるとサーバー側で再生成してくれるというものです。SSR+ファイルキャッシュのようなものと捉えるとわかりやすいかもしれません。

Next.jsにおいてはCSR,SSR,SSGのどれか一つを選択しないといけないというわけではなく、一つのアプリケーションの中で併用することができます。

例えば動的にデータが変わる商品ページはSSR、事前にデータがわかるヘルプページはSSG、パフォーマンスやOGPを気しない管理画面はCSR、のような感じですね。

最後にNext.jsの具体的なコードを出しておきます。Next.jsでSSRにするには、pages以下においたコンポーネントgetSeverSidePropsという関数を定義して exportします。この名前はNext.js の規約です。

const PostPage = ({ post }) => {
  return (
    <div>
      {post ? <Post post={post} /> : <Loading />}
    </div>
  );
};

export async function getServerSideProps({ params }) {
  const post = await fetchPost(params.id);
  return { props: { post } };
}

この関数はリクエストを受け取ったときに実行されて、データをコンポーネントに渡します。コンポーネントは受け取ったデータを元にHTMLを作ってクライアントに返します。これでクライアントはコンテンツがすでに入っている状態のHTMLを受け取ることができます。

SSGも基本的には同じで、getStaticPathsgetStaticPropsという関数をexportします。

const PostPage = ({ post }) => {
  //...
};

export async function getStaticPaths() {
  const posts = await fetchPosts();
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }));
  return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
  const post = await fetchPost(params.id);
  return { props: { post } };
}

SSRと違ってビルド時に対象のコンテンツがわかっている必要があるので、まずはビルド対象のコンテンツ、ここでは post id のリストを getStaticPaths で生成して、それをもとにgetStaticPropsがidの数だけ呼ばれてHTMLが事前に生成されます。

まとめ

最初にNext.jsの概要についてRailsと比較しつつ説明しました。とにかくNext.jsは開発体験がよくてRailsエンジニアであれば好きになると思います。たぶん。好みというのは人それぞれなので予防線は貼っておきます。

もしこの発表を聞いてちょっとNext.js使ってみようかな、と思ってくれたら嬉しいです。

最後に

私が今所属しているUbie Discoveryでは、Next.jsもRailsも使っているしサーバーサイドはKotlinがメインだったりします。技術的でも事業的でも興味あると思った方は、ぜひカジュアルにご連絡ください。

クックパッドを退職して Ubie Discovery に転職しました

昨日がクックパッド最終出社日で今日からヘルステックスタートアップの Ubie Discovery*1 で仕事します。

クックパッドは2015年4月に入社したので6年半弱在籍しました。過去最長記録です。思えば色々なことをやりました。新規サービスの立ち上げをやったり機械学習を使ったサービスを作ったり、人事と兼任してエンジニア採用とか新卒採用の夏インターンとか TechConf の運営をやったり、技術基盤のグループに移ってマイクロサービス化をやったりフロントエンドの Next.js 化をやったり。

クックパッドのサービスは好きだし、やりたいと思ったことに挑戦させてくれるし、同僚は優秀だしで特に大きい不満はなく、辞めるつもりはなかったんですけど、クックパッドで同僚だった人が何人か Ubie Discovery に転職してめちゃくちゃ楽しそうに仕事してるのを横目で見たのをきっかけに興味を持ち、話を聞いてみたら事業内容とか組織の体制とかに惹かれて転職を決めるに至りました。

これまでも他社の話を聞きに行ったり、体験入社に行ってみたりもしていたけど、選考に乗ってみようと思ったのは Ubie Discovery が初めてでした。

一番大きい理由は医療というビジネス領域です。僕は元々自分とか自分のまわりの人の生活が便利になるようなものを作りたいと思っていて、それがクックパッドに長年いた理由のひとつでもありますが、子どもが生まれて病院にお世話になる機会が増え、さらにはコロナ禍によって自分や家族の健康について以前よりも考えることが増えてきました。そういった中で Ubie Discovery が解決したい課題やビジネス戦略などの話を聞き、この課題が解決されれば自分や家族にとってより良い社会になるだろうということが想像でき、自分でもその課題を解決したいと強く思うようになりました。

それと人事評価をなくしたり、ホラクラシーという組織運営の方法など、これまで在籍したことがある会社とは一風変わった制度の話も色々聞いて興味を惹かれました。単に人事評価をなくしたりフラットな組織にしました、という話ではなくそれを継続するための仕組みづくりや組織づくりに興味を惹かれ、この会社で働いてみたいと思うようになりました。このあたりの話についてはクックパッド時代にも同僚だった @h13i32maru が詳しく書いているので興味がある人はこちらを見てください。

最後にもうひとつがストックオプションです。人生一回ぐらいはスタートアップで一発当ててドーンというのを体験してみたいなあとぼんやり思ってはいましたが、幸いなことに選考も通り、夢を見るには十分なストックオプションと現職と同程度の年俸を提示していただきました。

ただ、考えてみるとお金はもちろんほしいけど僕が真にほしいのは夢を追いかける過程で熱くなれる気持ちなんじゃないかと思っています。それも自分ひとりだけではなく、同じように夢を実現するために熱くなる仲間と一緒に熱くなりたい。そう、青春なんですよ。僕がほしかったのは青春。とりもどせ青春。

Ubie Discovery では同じような思いや熱意を持った人がたくさんいて、事業のフェーズ的にもちょうど伸びてきているところで、ここでなら青春を謳歌できるに違いないと思い入社を決意するに至りました。

積極採用中

Ubie Discovery 最近めっちゃエンジニア入ってない?と思う人も思わない人もいるかもしれませんが、実はソフトウェアエンジニアという職種においてはまだ20人いないぐらいなので人が全然足りてません。そういうわけで Ubie Discovery に興味を持っていただいた方はお気軽に @hokaccha まで DM でご連絡ください!

*1:Ubie 株式会社は 0 → 10の開発をおこなう Ubie Discovery と 10 → 100 のグロース&スケールをおこなう Ubie Customer Science に組織が別れており、自分を含め現状ではエンジニアは全員 Ubie Discovery に所属しています。

lodash 互換の debounce と throttle だけを提供するライブラリを作った

最近は ECMAScript 自身の機能も豊富になってきて lodash のユーティリティ関数の出番はだいぶ少なってきたけど、debouncethrottle だけは未だに使う機会がまあまあある。

しかし、lodash は何も考えずに使うとバンドルサイズが肥大化するのでいい感じに Tree Shaking するために lodash-es を使う必用があったり、自分が使っている機能とは全然関係ないアップデートが Dependabot から大量に降ってきたりしてしんどかったりする。

lodash は lodash.debounce のように個別のモジュールも提供されているけど、Last Published が 5 years ago になっていたりして、あまり継続的にメンテされてない様子が伺える。これに関する Issue は探すといっぱいあって、対応したいけど手が回ってないみたいな様子らしい。

debounce とか throttle は lodash 固有のものではないので探せばいっぱい実装は見つかるんだけど、微妙に API が違って使いにくかったりして lodash 互換でついでにビルトインで TypeScript の型も提供してほしいし、ということで debounce と throttle だけを切り出したモジュールを作った。

処理を間引くので mabiki。実装はほとんど lodash から持ってきて TypeScript 化してテストを通るようにしたぐらい。ESM と CJS 両方対応していて TypeScript の型も提供しているのでたぶん便利。

TypeScript でジェネリクスの部分的な型推論ができない

const obj: any = { a: 1, b: "x" };

function foo<T, U>(x: U): [T, U] {
  return [obj[x], x];
}

こういうコードがあったとする。コードの良し悪しは置いといて、U は引数から推論して、T は呼び出す側から指定したいというケース。

// こう書きたいけどエラー
foo<number>("a"); // Expected 2 type arguments, but got 1

// これは通る
foo<number, string>("a");

これは今現在(TypeScript 4.0)ではできないみたいで、Proposal があがっていた。

https://github.com/microsoft/TypeScript/issues/26242

ワークアラウンドな方法を見つけたけどこれを使うぐらいなら素直に型引数書いたほうがよさそう。

https://medium.com/@nandiinbao/partial-type-argument-inference-in-typescript-and-workarounds-for-it-d7c772788b2e

今後のAdventar

メリークリスマス!Adventarを支える技術 Advent Calendar 2019 の25日目、最終日です。

最終日は今後の Adventar をどうしていきたいかについて技術編と機能編に分けて書こうと思います。

技術編

細かく直したいところはいっぱいありますが、大きめトピックだけいくつか書きます。

SSR のキャッシュを入れたい

SSR のキャッシュについては19日目の記事に詳しく書きました。今は毎回 Lambda を呼び出してレンダリングしているので、キャッシュすることでパフォーマンスをあげたいと思っていますが、コスト(お金)がかかる問題をどうにかしないと...。

gRPC-Web の Node.js 対応

5日目の記事に書きましたが、今 Node.js では gRPC-Web が使えないので SSR の際に仕方なく JSON API を使っていますが、完全に無駄なので gRPC-Web を Node.js でも使えるようにして同一コードでサーバー/クライアント両方動くようにしたいですね。

そのためには gRPC-Web の ptoroc プラグインに手をいれないといけなくて、C++ を書く必要がありそうです。できるのかな。

gRPC サーバーを Lambda で動かしてみる

gRPC サーバーを Lambda と API Gateway で動かすのは、たぶんやればできるんじゃないかなと思ってますが、確証はないです。これができると ECS などのサーバーが不要になり、真のサーバーレスにできます。真のサーバーレスになると圧倒的にコスト削減できるはずなので、取り組んで見る価値はあると思います。

GitHub Actions で CI/CD したい

今はテストは手元で実行するだけになっているし、デプロイも手元からやっているという、プロとして恥ずべき状態です。CircleCI とかでもいいんですが、GitHub Actions ちゃんと使ったことないのでやってみたい。

まあこのぐらいですね。どれも実際にやるかどうかはわかりません。

機能編

サービスをシンプルに保ちたいので、そんなにたくさん機能を追加するつもりはないのですが、現状でいくつかやりたいことはあります。

探しやすくする

今カレンダーを探すのはテキスト検索ぐらいしかないので、もう少しカレンダーを探しやすくするような機能をいれたいです。具体的にはカテゴライズ、スターを導入して人気順、募集中のものだけ検索、などを考えています。

ちなみにカテゴライズについては過去のカレンダーを手動でカテゴライズしてみたのですが、ジャンルが幅広すぎて、あってもなくてもあんまり変わらないのでは、という気になったので一回置きました。機械学習でいい感じにカテゴライズできたりするかな。

アイコン更新できない問題

地味な問題ではあるんですが、今年一番お問い合わせが多かった問題です。詳しくは8日目の記事に書きましたが、Firebase Authentication の仕様の問題で、Twitter などのアイコンを更新した際に Adventar 側のアイコンが更新されるタイミングが難しい感じになっています。

地味なんですけど、アイコン大事なのでどうにかしたいですね。

iOS/Android のアプリ

どちらかというと技術的な興味によるものです。Web よりもいい体験が提供できるんだろうか。ちょっとやってみないとわかりません。

ちなみに技術的には、Swift や Kotlin を学ぶために、それらの言語で作ってもいいし、React Native で作って、Web を React Native for Web で作り変えてみるなんていうのも技術的には面白そうではあります。

以上です。もし機能要望などがあれば TwitterGitHub にお気軽に書いてください。GitHub の Issue は日本語でも大丈夫です。

まとめ

書ききりました。がんばった。全部の記事を読んでくれた方、一部の記事を読んでくれた方、Adventar を使ってくれた方、ありがとうございました。

今年はもう疲れたので何もしませんが、来年以降またがんばろうと思いますので、今後も Adventar をよろしくお願いします!

それでは良いお年を!

Adventar の技術変革の歴史

Adventarを支える技術 Advent Calendar 2019 の24日目です。

今日はこれまで Adventar が利用してきた技術がどのように変わってきたのかを書こうと思います。

2012年

リリースした年です。最初は RubyRails を勉強したいと思い、何かいいサービスの題材はないかなあと思っていて、当時主に ATND で行われていた Advent Calendar が使い勝手が悪すぎて、Advent Calendar 専用のサービスを作ったら使いやすいんじゃないか、というので作りはじめました。(これは ATND が悪いわけではなくて、そもそも ATND はそういう目的のサービスではなかったというだけです)

なので初期実装は Rails でした。サーバーは、たしか YAPC Asia か何かで、当時ペパボに勤めていた刺身さんから、Sqale というペパボのホスティングサービス(Heroku みたいなやつ)のクーポンをもらったのがきっかけで Sqale を使ってホスティングしていました。

値段も安かったし簡単だったんですごくよかったんですけど、今はもう終了しちゃってます。

ちなみに Internet Archive で見つけてきた当時の様子はこんな感じでした。やる気がなさすぎるw

f:id:hokaccha:20191204230504p:plain

2013年

ほぼ一人でやっていたところに、june29ayumikoという強力な仲間を得て、飛躍的に完成度が高まった年です。

june29 さんには僕の初心者 Rails コードをバシバシレビューしてもらい、ayumiko さんにはクソダサかった見た目を見違えるようなデザインにしてもらいました。

また、この年からサーバーは Heroku に移りました。明確な理由はよく覚えてないけど、june29 さんが Heroku に慣れていた、Redis とか memcached などのミドルウェアが Sqale で利用できなかった(ような気がする)あたりが理由だった気がします。

それとフロントエンドは Backbone.js を利用するようになったようです。当時流行ってたんですよ。

当時のデザインはこんな感じだったみたいです。

f:id:hokaccha:20191204230524p:plain

たしか12月になったらカレンダー一覧でなく記事一覧をトップに出したりしてました。背景の画像がなんかかっこいいですね。

2014年

この年は軽微な変更で、特に大きい変更や技術的チャレンジはないみたいでした。あんまり覚えてない。

2015年

Qiita にエントリを書いてました。

https://qiita.com/hokaccha/items/c5cd96c2ec002e27ff4b

サーバーは相変わらず Heroku だけど、フロントエンドを React で書き換えて、react-rails を使った Server Side Rendering を導入しました。

当時 React がちょうど流行りはじめぐらいで、試してみたいなーと思っていたので投入してみた。当時まだ hypernova とかもなくて、Server Side Rendering の知見はほぼない状態だったので色々と苦戦した覚えがあります。

react-rails は Turbolinks とまあまあ相性がよくて、Turbolinks の遷移時のイベントで React Component を Mount/Unmount できるし、初期描画のときだけ SSR、Turbolinks 遷移のときは CSR というふうにできて、ルーティングをクライアント側でやらずに Rails に乗っかれるし、アーキテクチャとしては今でも悪くない気がしてます。流行りはしないと思うけど。詳しくは前に発表したときの資料がありました。

https://speakerdeck.com/hokaccha/react-rails-1

2016年

軽微な変更のみで大きな変更はなかったみたいです。

2017年

この年は ECS にしてみました。

当時仕事でも ECS を使い始めていて、基盤チームが色々整備してくれていたのだけど、自分では何もわからなかったので一回 ECS でインフラを作ってみたかったというのが動機です。

とにかく難しくて大変だった記憶があります。まあもちろん今年のほうが大変だけど。

2018年

ECS にして毎月1万円ぐらいかかることが判明して、ECS だいたい理解したし、できるだけ安価に運用したいということで雑な VPS に移して月1000円ぐらいで運用できるようにしました。

itamaeで構成管理したり、 Mackerel を使い始めたりしました。あとなぜかタイムスリップして古きよき Capistrano によるデプロイになりました。

Heroku に戻らなかった理由は覚えてないけど、なんでだっけな...。単純に VPS のほうが安かったからかな。

2019年

とにかくモダンな構成にしたいと思ってがんばって作り直しました。

来年には Heroku に戻ってるかも知れないです。

まとめ

Adventar ができてからの歴史を振り返ってみました。2013年に基本的なかたちができてか大きい機能や見た目の変更はなく、個人サービスなので技術的なチャレンジを色々とやってみる実験台みたいになってます。

今後も大幅な機能変更とかの予定はないけど、できるだけ使いやすくはしたいし、長くサービスを継続できるようにがんばりたいし、技術的チャレンジももっとやっていきたいと思います。明日はいよいよ最終日です。今後のAdventarの技術的チャレンジや方向性について書こうと思います。