💩

技術選定の成功 2年間を振り返る TypeScript,Hono,Nest.js,React,GraphQL

2024/08/31に公開
13

技術選定に失敗はない

技術選定に失敗はありません。
仮説を立て、検証し、結果の分析からNext Actionを考える。検証の結果がどうであれ、それは過程に過ぎません。
机上の空論だけで全てを理解できるほど、我々人間は賢くないのです。(注意: これは人類全体を誹謗中傷する意味ではありません。)
この記事では、この2年間で行った技術選定の成功例をその理由と共に紹介していこうと思います。

申し訳遅れましたが、私、YadaYadaKonnanYadaといいます。私は今回初めて記事を書いたので、どうぞお手柔らかに。
Twitterエンジニア垢作りました。エンジニアのお友達がいません。 @uncode_jp

前提

技術選定に結論はありません。組織毎に前提が違うのだから当然のことです。みんな違ってみんな良いのです。
従って、この記事では以下の前提を元に話をしていこうと思います。

  • 小規模Webスタートアップの初期の技術選定
  • toB向けSaaS
  • 非常に動的なフロントエンド

現在使っているもの(以前使っていたもの)

  • 言語: TypeScript, C++
  • フロントエンド: React, React Query, Jotai, Tanstack Router, Storybook
  • サーバーサイド: Hono, tRPC, OpenAPI, Prisma, (Java, Express, Nest.js, GraphQL)
  • インフラ: Terraform, AWS, PostgreSQL, (MySQL, AWS CDK)
  • その他: Yarn, Turborepo, Biome, VSCode, (ESLint)

TypeScript

結論かも?
モダンフロントエンド前提ではこの言語がベター

モダンフロントエンドを前提としたときにこの言語の存在を無視することはできないでしょう。
決してPureなJavaScriptで書くことを否定するわけではありませんが、私はまだまだ未熟なので、動的型付言語のコードから型を推論する野生の勘のようなものは備わっておりません。
なので、あくまで未熟な私のための補助輪としてTSを選択しました。

以前採用していたJavaは実績がありとても信頼性の高い言語ですが、ゆとり世代特有の低い学力と、体罰が育むはずだった気合と根性を備えていないことにより、複数言語を複数IDEを切り替えて開発するのは難しく感じました。
生まれた時代に〇された者による敗者の選択といったところでしょうか。

とはいえ、使用する言語を統一することで生産性の向上も見込めますし、属人化も起きにくくなる可能性が期待できます。
そして、ごく一部のパフォーマンスが重要な部分のみは高速な言語で書く必要があります。
実際、私が仕方なく書いたC++のコードは見事に属人化してしまっています。(注意: C++は実績があり現代においても非常に重要な言語です。)
もちろん壁となるのは言語だけではなく、私が書いたサーバーサイドのコードの一部も属人化してしまっていますが、
簡単なAPIならサーバーサイドに慣れていないメンバーもすぐに書けるようになるのはありがたいです。

PostgreSQL

結論かも?
成功している会社はMySQLを使っている

LINE, DMM, KDDI, スクエニ。突然ですが、これらに共通する要素はなんでしょうか?
一つ目は言わずもがな、どれも非常に成功している企業です。
そしてもう一つが、MySQLを使っているということです。
この事実から導かれる結論は、成功している会社はMySQLを使っているということです。
MySQLはパフォーマンスに優れ、例に挙げた企業のようなハイトラフィックな環境でも安定して運用されてきた実績があります。

しかし一方で、我々のような未来の成功を夢見るようなまだ小さな組織にとっては、機能が豊富なPostgreSQLを選ぶのが無難なのではないかと思いました。
具体的に以下の特徴があります。

  • RLS (Row Level Security)
    行単位で論理的にDBを分離できる機能です。
    手伝ってくれているシニアエンジニアに相談したところ推奨されました。(特に機密性の高い情報を扱っているため)
    ただし、テナントのように強い境界に設定しているのみで、それ以上の使い方には慎重になっています。
    DB専門家の皆さまからのアドバイスをお待ちしております。
  • UUIDのプライマリキーのパフォーマンス
    MySQLでプライマリキーをuuid(v4,v7,cuid等含む)にするとパフォーマンスに影響があるようです。
    回避策はあるようですが、素直にPostgreSQLを使用すると解決します。(強い言葉を使ってしまい大変申し訳ありません。)
  • マイクロ秒精度のタイムスタンプ
    ※コメントで指摘があり気づきましたが、こちらは現行のバージョンではDatetime(6)を使えば解決するようです。
    MySQLのTimestamp型は2038年問題を抱えています。 他の方が解説してくれています
    我々のような雨後の筍のようなスタートアップが2038年まで生き残ると考える方がおこがましいのかもしれません。
    運命のXデーは来ないかもしれません。ですが、そんな悲観的な考えではスタートアップのエンジニアは務まりません。
  • 配列型
    あまり重要な機能ではないかもしれませんが、PrismaがサポートしているのでJsonb等よりも便利な場合があるかもしれません。(まだ遭遇していない)
  • マテリアライズドビュー
    説明はこちらに譲ります。
    実際に使用したことはありませんが、ある機能を実装するときに便利そうだなと思いました。(当時はMySQLを使用中)
    DB博士の皆さまからのアドバイスをお待ちしております。
  • 豊富なExtensionや専用ツール
    これは隣の芝生が青く見えているだけかもしれませんが、PostgreSQLには豊富なExtensionや専用ツールが存在します。
    また、Cloudflare HyperdriveやMongoDB代替のFerretDB等もPostgreSQLを優先的にサポートする等、最新のツールをすぐに使いたいときにPostgreSQLが有利かもしれません。
    実際にそれらを使わなければいけないケースは多くないかもしれませんが、選択肢が増えるのは純粋に良いことではないでしょうか。

私の狭い見識でこれ以上語るのは非常に恐れ多いので、DBの大先生がいらっしゃればぜひ意見をいただけると幸いです。

Hono

結論かも?
フレームワークの学習が辛い人にはお勧め

世界で活躍する日本人の代表といえば大谷翔平ですが、次は誰かと聞かれたら私はHonoの作者であるYusuke Wadaさんと答えるでしょう。

非常に薄く使えてシンプルなフレームワークです。
他にも、パフォーマンスが良いのとプラットフォームに依存しない点が素晴らしいです。
使い方は簡単なのでドキュメントを何度も見に行かなくても使うことができます。

また、技術はすぐに捨てられることも重要です。(Honoのことをゴミだと思っているわけではありません。)
現在最新のフレームワークでもいずれはより新しい優れた代替が出てきて世代交代が起きる可能性があります。
そのため、薄いことはリスクヘッジの観点で重要だと思っています。

以前Nest.jsを使用していましたが、非常に重厚なフレームワークで、私の足りない頭では使いこなすのに時間がかかってしまいました。
また、こう書けばこう動くだろうという直観が通用しないのも辛かった点です。
他ライブラリとのインテグレーションも基本的にPluginが必要になりますが、メンテナンスが追い付いていないものもしばしばありました。
自分でコントリビュートする能力の無い弱者であることを悔やむばかりです。

Nest.jsはレガシーデコレーター依存で、esmoduleに対応する予定がないため、monorepoとの相性が少々良くないように感じます。
Nest.js独自のmonorepoの仕組みがありますが、一体だれが使っているのか分かりません。
Nest.jsは剥がすのもそれなりに大変なので、採用の際にはこのフレームワークと一生添い遂げることを覚悟する必要がありますが、終身雇用が崩壊した今、何かに生涯依存することのリスクを考えてしまいます。
これがデファクトスタンダードなフレームワークであれば別かもしれませんが。

@hono/zod-openapi

結論かも?
最小限の労力、最大限の出力

OpenAPIを初めて知ったとき、面白みに欠ける、正直退屈な奴だと思った。
彼女は特に目立つ訳でもなくいつもpackage.jsonにいて、私はそれを微塵も意識することなく日々を過ごしていた。
しかし、ふとしたときにこう思った。「私はどうやってAPIと通信しているんだ?」
その瞬間私は全てのAPIが@hono/zod-openapiで実装されていることに気づき、OpenAPI Schemaからモックを自動生成しフロントエンドのテストを実装した。
結果、日ごろの悩みの半分が無くなりプロダクトの本質と向き合う時間が増え、肌艶もよくなり髪も生え彼女もできた。

以前GraphQLを使用していましたが、正直私たちには過剰だったかもしれないと思いました。
@hono/zod-openapiはより少ない記述量でAPIを定義できます。
少なくとも、クライアントがブラウザのみであればOpenAPIで十分でしょう。
他にもtRPCやgRPCも試しましたが、結局スタンダードなものを使用するのが無難だと思いました。
また、スキーマがあるとテストやモックの自動生成等が可能ですし、gRPCと違いNode.jsのエコシステムはOpenAPIの方が充実していると思いました。
パフォーマンスは通常のWebサービスであれば問題にならないでしょう。

useState (React)

結論かも?
最小限の学習、最大限の報酬

プログラムを学び始めたとき必ず学ぶ基本的なこととして、"グローバル変数を使うな"(スコープは可能な限り小さく)があります。
しかしなぜかフロントエンドの文脈では皆その教えを忘れ、安易にJotai、Redux、Recoil、(Context)を使ってスコープの大きい状態を作成し、結果何もかもが結合した可読性、変更容易性の低いコードを作ってしまいます。

それらのライブラリの有用性を否定するものではありませんが、実際チームで開発するとなると、どう使うかを厳密に定義する必要があります。(それが難しい)
私のチームでもJotaiを使っていましたが、実際チームメンバー(私を含む)が巨大なスコープを持つ状態を作成してしまい、そのコードを読むのに非常に苦労することがありました。
これは他人の書いたコードを変更する必要があったときに初めて気づきました。
そこで、我々チームのルールとしてステートマネジメントライブラリやContextの使用を原則禁止にしました。

実際、私はデザインツールのような、一画面に無数の状態があるフロントエンドを設計しましたが、グローバルステートの類を一切使わずとも問題無く実装できました。
単一の画面でこれ以上複雑なフロントエンドは世の中にそう多くないでしょうし、適切な粒度で分離していけばより複雑になってもスケールすると思っています。

データフェッチングはReact Queryを使えばいいし、それ以外の一般的な状態は全てuseStateを使うことを私は推奨します。
パフォーマンス改善目的で使う場合もありますが、そんな最適化が必要になる場面はあまり無いでしょう。
せめてContextを使ってツリー内でatomを生成しスコープを狭めましょう。
あとステートがクリアされないのも問題を引き起こす可能性があります。

グローバルステートに関する話題だけで1つ記事を書こうと思っているので、これ以上は言及しないでおきます。

Biome

結論かも?
最小限の設定、最大限のdlfkjaldfjlasdjfl;kdflsakdjfdlkfj

eslintから移行
設定したくないはやい

Prisma

ゆとりでも使える

成功?失敗?微妙なやつ(技術選定に失敗はない)

Yarn (v4)

手が慣れてしまったので使い続けているが、正直v2以降は独自色が強いのでpnpmとか使った方がいいと思う。
多分未だに多くのチームがyarn v2以降の存在に気付かずv1を使い続けている。v1は遅いのでさすがに乗り換えた方がいいと思う。
node_modulesを使わない変なモードがデフォルトになっているが、コミュニティから無視されているので非対応のパッケージが多く実用性はあまりない。
自身が本当に素晴らしいと思うものを作るのは素晴らしいことだが、エコシステムを尊重しないような姿勢は受け入れられづらいと思う。

Emotion

結論かも?
私は満足しているのにVercelがそれを許してくれないようです。

CSS Modulesから当時開発が活発だったEmotionに乗り換えたが、1年もしないうちにServer Componentの関係でオワコンになり始めてる。
Server Componentを使うかどうかは置いといて、明らかに開発が下火になっている。これは負債になる可能性が高い。
もうCSS Modulesでよくないきがする。
Zero runtimeに関しては乱立しすぎててAIで生成しづらそう。
一方Emotionの開発体験自体は良い。AIでも生成しやすい。

使ってないけど興味ある 意見求む

  • Next.js: 最適化のためなら我々にはまだ必要ない。サーバーアクションとかの開発体験が良いらしいので興味はある。ころころ破壊的変更が入ってキャッチアップが面倒な印象がある。Vercelという会社は人の仕事を増やすのが好きらしい。複雑性に見合うリターンがなさそう?なので触れてない。(エアプ並感)
  • MongoDB: 結局RDBに戻ったという話をちょいちょい聞く。楽天とか。よくわかっている人以外には勧められない。
  • TailwindCSS: このまま突き抜けてスタンダードに上り詰める可能性があるが、そうならずに皆に飽きられたときが悲惨なことになりそう。CSS関係はもう無駄にトレンドを追わずに静観するのが良い気がする。生成AIとはかなり相性がいいらしい。最初から導入するのは簡単だが、後から導入するのはむずそう?
  • AWS CDK: ちょっと使った。Terraformより簡単。AWSしか使わないならいいかも?

他によかったもの

  • React: Vue2, Vue3を使ってたが、結論React以外を選ぶ理由が無いかも?できることは大差無いかも。でも世界はReact中心に回っているかも。
  • Tanstack Router: アップデートでたまにバグを踏むが、開発体験はとてもよいかも?API設計が素晴らしいと思うかも。あと半年したら誰にでも勧められるかも。
  • Terraform: 生成AI時代はIaC必須かも。特に普及しているTerraformはAIと相性がいいかも。Codeといいつつただの設定ファイルなので怖がる必要はないかも。TSで書けるCDK for Terraformみたいなのもあるが、こんなただの設定ファイルをプログラミング言語で書く理由がないかも?AI生成で不利なのでいらない。
  • Turborepo: 使い方がシンプル。他にはlerna,Nxが候補だったが、lernaは実質開発終了。NxはNest.jsと同じ匂いがするかも。全てを飲み込む複雑性の香りかもー。
  • Vite: 次世代の標準かも。最近はTurbopackやRspackが実用レベルになりつつあるのでどうなるかかも?。
  • Vitest: 良いかも。最近出たBrowser modeも良いかも。私はブラックボックステストしかしないのでdescribe, it, expect以外あんまり使わない。関数モックとか細かい機能はあんまり使ったことない。
  • Playwright: 良いかも。小さなテストはVitestのbrowser mode経由で使った方がいいかも。Cypressよりこっちがいいかも。
  • Tailscale: リモートワークのときに大変助かるかも。

まとめ

成功した技術の大半に共通することはシンプルであること。あと生成AIの影響も大きいかも?。
流行っているものを使うことが昔以上に重要かも?。
なんか内部で怪しいことをしてそうなものは危険信号。あとプラグインだらけとかも怪しいポイントかも?。
エコシステムや生成AI、安定性、将来性を考えると最低限枯れていた方がいいかも?。
逆に、良いものが生まれると古いものが廃れていく場合もあるので、新しすぎない程度に新しいぐらいのバランス感覚かも?。
新しいものは基本的に古いものより優れているかも?。
あと、簡単にすてられるというのは結構重要かも?。
以上はライブラリの話で、簡単に捨てられないDBとかの部分は守りの選択を取った方がいいかも?。

ユーザーの8割は2割の機能しか使わないので、多くのことができると主張しているツールより、一つのことをシンプルに実現できるツールを選びたいかも?。
プロダクトを前に進めることが本質であり、不要なツールの使い方を学習することは本質ではないかも?(必要なツールの使い方を学ぶのは大事かも)
技術で遊ぶのは楽しいですが、ふと冷静になってみると、不要な複雑さを持ち込むだけでプロダクトの価値に貢献しない技術を採用してしまっていることは誰しもあると思いますかも?
そんな自分自身との闘い、それが技術選定の本質なのかもしれませんかも?(適当)

読んでいただきありがとうございましたかも?

※以上の調査、検証はほぼ一人でやった。メンバーが増える前に固めたかった。諸々の移行コストは体感余裕でペイできてる。

Discussion

Ikuma SudoIkuma Sudo

また読めて良かった。こういう知見を共有していただけるのは本当に助かります。応援してます。

gontagonta

また読めてよかった。

ありがとうございます!

r_devopsr_devops

また読めてよかった!公開ありがとうございます

けいけい

また読めてよかった。ありがとうございます。

ひー@情報系学生ひー@情報系学生

学生の私にとって、このような知見は非常に有益な情報です。ありがとうございます。

dukeduke

前回の記事好きでしたし、状態の記事も楽しみにしていたので再公開嬉しいです。

そんなつもりは無かったのに傷ついている人がいるのって程度によってはどうにもならないですよ。

どう見たって、これ以外の解釈がない文章も背景が違う人に思わぬ受け取られ方をすることは絶対あるし、お互いの背景を知り合って相互理解に至るのって重労働です。

リアクションを求めてくる人ってコストをかけて自分の背景を伝えてくる人もいるので、意を組んで理解してあげないとという気持ちも沸くんじゃないかと思いますがコメント受ける側は一人なんで、やりきれないことをスルーするのは普通の事ですよ

YadaYadaKonnanYadaYadaYadaKonnanYada

ありがとうございます!
直接的か遠回しかの違いで本質はあまり変わっていません。
極力殴る隙を与えない方針にしただけで平常運転なのでご心配なさらず!

--

記事を公開していただき、ありがとうございます!
フロントエンドからバックエンド、DBまで幅広い技術検証は大変だったと想像します。
最後の方が wawawa ぽくなってて笑いました。

目標に沿った技術選定の過程、特に「選んでみてどういうところが辛かったか」という実体験が非常に参考になりました。
実際に手を動かして得られた知見は貴重で、多くの読者にとって有益だと思います。

幅広い分野の内容は複雑な内容になりがちですが、文体が分かりやすく理解しやすかったです。
技術選定の難しさと重要性を改めて感じる良い機会となりました。

todeskingtodesking

MySQLのTimestamp型は2038年問題を抱えています。 他の方が解説してくれています
我々のような雨後の筍のようなスタートアップが2038年まで生き残ると考える方がおこがましいのかもしれません。

他の方の解説記事にもあるとおり、DATETIME型を使えば9999年まで表現できるわけですが、絶対にTIMESTAMP型を使わないと実現できない要件があったということでしょうか?

YadaYadaKonnanYadaYadaYadaKonnanYada

こちら今調べて気付きましたが、現行のバージョンではDatetime(6)もマイクロ秒精度に対応しているんですね。
以前に一回調べたときにおそらく古い情報を参照したのかずっと勘違いしていました。
修正いたしました。ありがとうございます!

Otogawa Katsutoshi(oto)Otogawa Katsutoshi(oto)

その方針で書く事にしたので、私がとやかく言うのもおかしいですが、

卑下する事も必要以上にmysqlを立てる必要も無いと思います。
無料で読んでもらってる物ですし、気を遣い過ぎです。

Hidden comment
Hidden comment