技術のトレンドと開発テクニックの知見を、無料で公開します!
いかに無駄な努力をせず、効果的にトレンドに沿ったアプリ開発ができるかを研究してきました。
自分が一番知見のある、フロントエンドの分野中心に見解を述べたいと思います。
結論から言うと、
React, Next.js, Typescript, Tailwind, react-query, prettier, Stylelint, auth0, tRPC, Prisma, playwright, vscode, github actions, PostgreSQL, Terraform, Flutter
これらの技術スタックが今後ますます流行り、開発体験の良いものになると思います。
最初にReactから述べます。
数年前は、React vs Vueの論争がありましたが、Reactが勝者に落ち着いたかと思います。
これは、Google Trends や npm trends、その他エコシステムの充実度とその加速度から見て明らかだと思います。近年ますます、型をちゃんとあてて、堅牢に作っていく重要性が見直されてきていますが、ReactはVueと比べ、Typescriptの相性が良いのも大きな理由の一つです。
また、VueはコミュニティベースのOSSであるため、開発の進捗が悪かったり、そもそも開発のインセンティブや組織が弱いです。また、コミット履歴見ると、Vue(Nuxt.jsもそうだが)は少数の天才に依存している状態であると思います。
いけている先進的なライブラリも、Reactファーストに作られていることも大きな理由の一つです。エコシステムの大きさと質が段違いであると思います。
Google Trends で検索すると、Reactは英語圏中心に圧倒的人気である一方、Vueは中国メインで、あとは、日本みたいな人気状況です。
Reactは、今後IT人材が爆発的に増えるインドでもインタレストが高いことからも、今後の人気も決定づけるでしょう。何より、アメリカで人気であると言うことは、絶大な影響力があります。
Next.js
Next.js vs Nuxt.js の論争も数年前までありましたが、
これも、企業がバックにいるかいないかがとても大きく、Nuxt.jsの開発進捗に比べ、Next.jsの開発スピードが早く、機能や品質のアドバンテージが多いです。
また、ホスティング先にVercelを用いることで、デプロイが簡単に出来たり、画像の配信の最適化が出来ます。VercelとGithubを連携させることで、PRのコードごとに自動的にデプロイし、プレビューリンクを生成することができます。これによって、local環境でBuildしなくとも、PRのレビューができます。
そのプレビュー環境では、デザイナー含む開発メンバーから、コメントでフィードバックをもらうことができるので、開発体験が上がります。
また、Buildが非常に速いです。
さらに、Rustで書かれた、TurbopackでさらにBuildの高速化されます。
これもVercelのプロジェクトであり、技術力の高さが伺えます。
viteやcreate-react-appなどもありますが、あえてそれらを使用する理由は小さく、Next.jsだけ見てれば良いと思います。
Next.jsはもはやSSRのためのツールではなく、多くのフロントエンドの開発体験を上げることに貢献します。
Typescript
型安全を強く意識した方が良いです。
コードが肥大化すればするほど、型があることによって、エラーを未然に防ぐことができます。
めんどくさいし、学習コストもあるかもしれないが、型の素晴らしさに感動する瞬間が来ると思います。
anyの型を許容しないくらいのスタンスで良くて、かけたコストに対して十分メリットを享受できます。
Tailwind
ReactやVueなどのコンポーネント指向のUIライブラリの登場で、美しくコンポーネントを作ろうとするとCSSがコンポーネントに依存するため、CSS設計はより重要ではなくなってきています。
何より、Tailwindは視認性が良いです。templateを見るだけで、どの要素にどんなstyleがあたっているか、レスポンシブ対応も含め直感的に分かります。
これが今までのようにCSSと分離されていると、ファイルを何度も往復する必要があったり、また、直感的なCSSのクラス名を考えるコストが生じます。
Tailwindを使うと、ほぼCSS設計しなくて良いので、それらのコストを削減でき、CSSが属人化するという問題も避けることができます。
Tailwindを使わない場合、PostCSSを推奨します。
近年人気のTailwindやその他UIフレームワークなども多くがPostCSSを採用していることからも、信頼性がありますし、導入コストが非常に低いためです。
Next.jsでは、Built-In CSS サポートがあり、特定のコンポーネントでのみ適応される CSS を適用したい場合は、CSS Modulesの使用を推奨します。
react-query(TanStack Queryに名前が変わりました。)
TanStack Queryを使うと、ステートマネージメントと、ローディングの処理、エラーハンドリングが驚くほど簡単になります。
Prettier
ESLintだけでなく、Prettierも併用して入れた方が良いです。
eslint --fixだけでもformatの修正は行われますが、
それに対応していないところをprettierがformatをかけてくれます。
eslint-config-prettierを用いると、format修正でPrettierとESLintとの間でコンフリクト起きている箇所を解消してくれます。
Stylelint
CSSのLinterです。次世代CSS構文やAltCSSの解析も可能です。
CSS構文エラーやコーディングスタイル違反の発見、スタイルの自動修正が可能です。
auth0
セキュリティが関わってくる大事な認証部分は、基本自前で立てるべきではないと考えています。
auth0かfirebase auth、cognitoが候補になりますが、auth0が柔軟性とリッチさが頭一つ分以上に違うと思います。
対応しているプロバイダーの数や、2段階認証、そしてその2段階認証先の選択数などアドバンテージが多いです。
ただし、その分値段もするので、一人当たりのユーザー単価が高いサービスにより向いているでしょう。
※ 追記: NextAuth.jsやsupabaseの認証機能も非常に良い
tRPC
tRPCは素晴らしいです。Web開発で、Nest.js, apollo-server, Express.jsで迷っているのであれば、tRPCを推奨します。
tRPCは、GraphQLなどを使わず、スキーマやコード生成なしに、完全にタイプセーフなAPIを簡単に構築し、利用することを可能にします。
これで、ますます、TSでフロントエンドとバックエンドを書いていくプロジェクトが増えていくと思います。
こういった最新のツールも、ReactやNext.jsファーストでDocumentやサンプルコードが作られていることが多いので、ますますそれらライブラリを使用する動機は高まると思います。 https://trpc.io/docs/nextjs
Prisma
Sequelize、TypeORMなどのSQLクエリビルダなど従来のORMの代替するものです。
他のORMと比べ、より型安全に書けますし、ドキュメントが豊富です。
また、カスタマイズ可能なSQLデータベースマイグレーションを自動的に生成し、移行ファイルを生成せずにデータベースに変更を加えることができます。
PostgreSQL
Aurora対抗のGoogle Cloud新サービス AlloyDB for PostgreSQLがリリースされたのも大きな理由です。
ちょっと前に流行っていたFirestoreは、多対多のリレーションが増えるほど、データを複製するなどして管理しきれなく詰んで行くので、中規模以上のプロダクトにおいては、メインDBとしては使わない方が良いです。仮に、データを複製しない方法でそれができてるように見えても、セキュリティに問題を抱えています。(多対多のリレーションだけでなくとも、セキュリティを意識すると、それ相応のセキュリティルールを書く必要があるため、実装コストはたいして変わらないです。)
仮にFirestoreを使うのであれば、階層構造を意識し一対多のリレーションを作ると良いです。もともとそういう風に設計されているので、クエリーが簡素になりますし、セキュリティルールも当てやすくなります。
なんだかんだ、普通のRDBの偉大さに気づきます。
PostgreSQLはMySQLより、より柔軟で、速いです。
NoSQLは単調なデータでクエリー速度が大事なものにのみ使用するくらいのスタンスで良いと思います。
Playwright
E2Eテストは、競合のCypressよりPlaywrightを推奨します。
Playwrightでは、ユーザー操作を記録し、そのコードを自動生成する機能があります。
何より、microsoftのossであることが強いです。
CypressはCIで回すと、よくタイムアウト問題が生じ、体験が良くなかったです。
vscode
エディタはvscodeを推奨します。なぜならextensionが豊富で、質の良いものが多いためです。
terminalもgitの管理もこれ一つで出来ます。
github actions
CIはgithub actionsを推奨します。
新規でCircleCIをあえて使う理由はないと思います。
Terraform
クラウドのインフラを、コードで記述することにより自動で構築できます。Terraformを利用することでオペレーションミスが無くなるほか、複数環境でコードを再利用することで効率化を図ることが出来ます。
Flutter
ほとんどのネイティブアプリはFlutterで良いと思います。React Nativeはサードパーティの集合体で、meta(facebook)がちゃんと管理しきっているわけではなく、不毛なエラーで消耗することが多いと思います。
一方、FlutterはGoogleがちゃんと組織的に運営しており、必要なウィジェット(コンポーネントに相当)の品質も高く、種類も非常に豊富です。コミュニティの規模も大きく、アクティビィティも非常に高いです。
次に、フロントエンドを中心に開発の細かいテクニックを紹介します。
-
Testing Trophyを意識して、フロントエンドのテストを書きましょう
https://kentcdodds.com/blog/the-testing-trophy-and-testing-classificationsこの画像は、reactで一番よく使われているtestライブラリ react-testing-library の作者であるKent C. Dodds氏によって作成されました。
彼は、バックエンドと同じ文脈でフロントエンドでもunitテストに重きを置かれている現状を嘆き、このTesting Trophyを作りました。
各テストの書くべき総量を、画像のようにトロフィーの体積の比率で表しています。体積の大きい箇所ほど、より多くのテストを書くべきと言う考えを表現しています。
フロントエンドのテストはコンポーネント単体の動作保証よりかは、コンポーネント同士の相互作用、すなわちソフトウェアとして動作が保証されているかと言う観点がとても大事です。
より多くのintegrationテストを書くことによって、よりソフトウェアとしての動作を保証することができます。
今は、APIサーバーを立てなくとも、Mock Service Workerというライブラリがあり、サービスワーカーの機能を使って、超簡単にモックAPIが作れます。 https://mswjs.io
Kent氏も、react-testing-libraryとMock Service Workerを使って、より多くのintegrationテストを書くことを強く推奨しています。
CIでテストする際も、フロントエンドで完結するため、バックエンドを意識するコストが低減します。
以上のことも踏まえるとフロントエンドのテスト方針の理想は以下になると考えます。
・型: しっかり目に書いて型安全を強く意識する。
・unitテスト: jestを用いて、再利用が想定された共通関数のようなモジュールや、UIフレームワークでいうmodal、 tag、 paginationなどのコンポーネントの層のみに絞る。
・integrationテスト: react-testing-libraryとmsw用いてしっかり目に書く。
・E2Eテスト: playwright用いて、抽象度高く、より少ないテストコードでより多く網羅できるテストを意識し、ゆるく書く。
また、テストのコストパフォーマンスと、テストを壊れにくくするための観点で、実装の詳細をテストしないことも重要です。以下リンクが参考になります。
https://kentcdodds.com/blog/testing-implementation-details
https://github.com/goldbergyoni/javascript-testing-best-practices
個人的には、型をたくさん書いて、その分テストを減らすという考え方が好きです。また、Kent氏はnext.js開発元のvercelのCEOのtweetの"Write tests. Not too many. Mostly integration."を拝借して、次の記事を書いています。
https://kentcdodds.com/blog/write-tests
https://twitter.com/rauchg/status/807626710350839808?s=20 -
コンポーネント設計について
基本的に、メジャーなUIフレームワークのコンポーネントの設計を参考にしながら、コンポーネントを作ると良いです。
それらのコンポーネントの引数や粒度とかを参考にすると、必然的に、汎用化はしやすくなります。
Reactで一番人気のある、Ant Designはかなり使いやすいので、Ant Designのコンポーネントインターフェースがおすすめです。 https://ant.design/components/overview/
また、コンポーネント設計で大事な考え方は、依存性の注入(DI)です。
結果、テストもしやすくなります。
逆も言えて、テストがしやすいコンポーネントを意識すると、美しいコンポーネントが作れます。 -
Atomic Designに関して
基本的に、否定派です。
Google Trendや海外の記事やOSS見ても、日本くらいでしか流行っていない印象です。
実際に、著名なUIフレームワークのコンポーネントのコードを見ても、Atomic Designで構築していないことから、海外の一流のフロントエンジニアはそういった粒度で考えていないことがわかります。
Atomic Designだと、5層になるので、例えば、SSRで一番上のpageのファイルからデータを流すのであれば、バケツリレーが長くなり、冗長すぎて実装コストが増大します。
基本的に以下の三層構造で十分だと考えます。
・親(layoutや権限チェック、data fetchの層)
・子(孫のコンポーネントを束ねて機能として意味を持たせる層。データフローが冗長であれば、この層でdata fetchして状態を持っても良い。)
・孫(UIフレームワークでいう、modal, tag, paginationなどのコンポーネントの層。) -
データのフェッチのテクニック
データのフェッチでは、POSTやUPDATEでデータの更新があった場合、再度、データをフェッチするみたいなロジックを描いているケースもありますが、コンポーネントとステートの性質を生かして、リクエストの結果が200okであれば、再度フェッチして更新するのではなく、POST / UPDATE した値でstateを更新して、再レンダーしましょう。
そうすることによって、余計なAPIリクエストしなくて済んで、APIの負荷が下がりますし、ユーザーに対して、レンダーの速度も早くなります。 -
多言語化のテクニック
文章をkeyにすると、templateの視認性が上がります。多言語化のkeyをドット繋がりの変数にしていると、今どこのtemplateを編集しているかと困惑することが多いと思います。
英語や日本語の文章をkeyにすることで、多言語対応していない時と同等に近い開発体験が得られます。変数名が文章になるので、わかりやすい変数名を考えるコストも削減できます。 -
ブランチの切り方
ブランチの切り方は、main -> staging -> develop -> feature or hotfix が一般的でベターだと思います。 -
技術的負債に対して
技術的負債をリストアップし、優先度つけてFixすることは大事です。 -
コード規約に関して
コード規約をreadmeなどに書いて、PR化することによって、チームの合意形成のもと、コードの設計の認識の統一や、気軽に技術的な品質を向上させる環境を作ることができます。
メンバー全員からPRのApproveを貰ったら、それが規約になって、今後のPRレビューの対象になるという世界観です。新しく開発メンバーに加わる方も、その規約に見ることで、スムーズに開発にコミットすることが可能になります。 -
状態管理に関して
基本的には、TanStack Queryで状態管理するのが良いですが、もしReactでVuexみたいにライトに状態管理したいのであれば、Zustandを推奨します。
Reactのcontextを使ったパターンでは、状態に更新があったときに、再レンダーする必要がありパフォーマンスが落ちるため、大規模な状態管理には向いてないです。 -
バリデーションに関して
バリデーションはTypescriptファーストのzodを推奨します。
バックエンドにtRPCを採用するのであれば、フロントエンドのformと同じzodのバリデーションを利用することができます。 https://github.com/colinhacks/zod -
Buildしたコードのdeploy先に関して
Hostingサービスを積極的に使うことを推奨します。著名なものであれば、NetlifyやFirebase Hostng, Vercelがあります。
簡単にホスティング出来ますし、CDNやPRのプレビューリンクの機能などが備わっています。
Next.jsを使うのであれば、Vercelで良いと思います。 -
Storybook使い方
Storybookは、UIフレームワークのDocumentのように意識して作ると良いと思います。
そうすることによって、エンジニアがどういった引数で、どういった挙動になるかが把握しやすく、開発効率が高まります。何でもかんでもコンポーネントをStorybookに載せれば良いと言うわけではなく、例えば以下リンク
https://ant.design/components/overview/
のサイドバーにあるような粒度でコンポーネントを作ることで、UIフレームワークのDocumentようなstorybookを作成することができます。ですので、StorybookのDocumentの美しさと、コンポーネント設計の美しさは表裏一体の関係です。
そもそもそういった意図でStorybookが作られたのでは、とも思っています。 -
チーム開発のメンタリティ
誠実さと思いやりを持って開発に取り組むことが、チームとして貢献できることにつながると考えています。 -
コンポーネントのデータフローに関して
汎用化したコンポーネントなどを束ねてページを構成するコンポーネントでデータフェッチして、それら子のコンポーネントにデータを流すで良いと思います。(あるいは、pageディレクトリのファイルからデータフェッチしても良いが、SSRできるメリット以外は、冗長になるだけです。)
汎用化したコンポーネント内で、データフェッチしたり、グローバルステートからデータを読み取ったりするなどの構成もありますが、やはり、引数でそれらを受け取った方が、綺麗にコンポーネントを汎用化でき、データの依存関係なく設計できます。 -
moment.jsではなく、軽量なday.jsを使いましょう https://github.com/iamkun/dayjs
少なくとも、date系のモジュールをスクラッチして車輪の再発明はしないようにしましょう。
ドキュメントも充実しているので、積極的に、day.jsを使いましょう。 -
ChatGPTを利用しましょう https://openai.com/blog/chatgpt/
かなり専門性の高い箇所でない限り、有益なサンプルコードを提供してくれます。
しかもそれは、検索しても出てこないような、codeのアイディアであることも多いです。
また、英語のテキストデータをより多く使って、チューニングされているので、英語で質問をした方が、良い結果が返ってくることが多いです。 -
React Hook Formを使いましょう https://react-hook-form.com/
Formのハンドリングがしやすくなります。 -
nullかundefinedか
undefinedが自動的に設定されることを考慮すると、原則undefinedに寄せる方がルールの統一がしやすくなると思います。
ただし、DOM系のAPIではnullが使用されることもあるので、その場合は柔軟に対応しましょう。
公式のwikiに書かれてある、TypeScript開発チームのコーディングガイドでは、nullでなく、undefinedが推奨されています。
https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined -
react-useを利用しましょう
react-useに存在するのHooksであれば、車輪の再発明してHooksを作るよりかは、積極的にreact-useを使いましょう。使い方もわかりやすく書かれてますし、そもそもの品質と信頼性が高いです。
https://github.com/streamich/react-use -
CIの対象とタイミング
developブランチへのマージのPRでは、lintとformatのチェックをするCIを回しましょう。
ちゃんとBuildが通っているかもCIでチェックしましょう。(Pushの時に、hookでチェックするパターンもありますが、冗長なので、CIの方でチェックできてれば良いです。) -
エラー通知の仕込みと、そのテクニック
Sentryでエラー通知がslackやemailで来るようにしましょう。
また、リロード時や認証時に、uidを仕込むようにしよう。
そうすることによって、より、バグの特定がしやすくなります。
環境tag(production, staging, development)も仕込むことで、通知されるslackのチャンネルを分けることができたり、sentry側のdashboardでフィルタリングすることが出来ます。
基本的に例外エラーが通知されるので、try-catchでエラーハンドリングしている箇所で、そのエラーを通知したい場合は、catch内で throw new Error() すると良いです。
また、しっかりとエラーハンドリングをすることが、通知されるエラーからノイズを除去できることに繋がります。 -
SPAかSSRかの基準
Google bot であるクローラーも最近のバージョンアップでレンダリング精度が高まったとはいえ、
正しくjsの実行を対応してくれるとは限らないのが現状ではないかと想定されます。
ですので、SEO対策として、SSRしてクローリングしてもらう必要があるでしょう。
また、TwitterなどのSNSで商品の画像も表示させたいのであれば、SSRしましょう。
それ以外は、初期の表示速度が重要であれば、SSRする理由になるでしょう。 -
質の高い情報を集める方法
Googleの検索は英語で検索するだけでなく、ロケーションの設定をUSにして検索した方が、情報の質が違います。
USに本社があるから、USの検索エンジンが最新のものであるのは至極当然です。
以下リンクでus版で検索することができます。
https://www.google.com/?gl=us&hl=en&pws=0&gws_rd=cr -
if文でネストが深くなる
早期リターンを意識しましょう。 -
開発のトレンドを把握する方法
npm trendsでダウンロード数を比較したり、githubのstarの数で人気度を確認することができます。また、githubのPRやissueの数で、開発のアクティビティが図れます。githubのコミット履歴を見て、開発が今でも盛んか、止まっているかなど把握することが出来ます。開発が今でも続いているかは重要な要素で、バグが起きた際に、対応されなくなって、困るからです。 -
APIリクエストのテクニック
APIリクエストの順番が大事な箇所以外は、Promise.allでAPIリクエストを並列処理して、速度の向上を目指しましょう。 -
Google Tag Manager(以下GTM)を仕込みましょう
これまでのGoogle Analyticsでは、収集したいイベントがあれば、ソースコードに編集を加える必要があったり、その数が多ければ、コードの見通しが悪くなったりする問題がありました。
GTMを使うことで、それらはGTM側で制御することが可能になります。
GTMで収集したログを、Google Analyticsを見て、デザインを改善の提案が出来るエンジニアは価値が高まります。 -
JWTのトークンのハンドリングに関して
フロントエンド(React)では、認証情報(例えばJWT)をセッション管理するために、いくつかの選択肢があります。いくつか一般的な方法を紹介します:Cookieに保存する:
JWTをcookieに保存すると、ユーザーがブラウザを閉じた後もセッションを維持できます。しかし、これはXSS(クロスサイトスクリプト攻撃)に対して脆弱です。LocalStorageに保存する:
これもユーザーがブラウザを閉じた後もセッションを維持します。しかし、これもXSSに対して脆弱であることに注意が必要です。メモリ(state)に保存する:
ReduxやReactのContext API、React Queryなどを使ってメモリにJWTを保存することができます。これはXSSに対しては安全ですが、ユーザーがブラウザを閉じるとセッションが消えます。最も安全な方法は、HTTP Only Cookieを使うことです。これはJavaScriptからアクセスできないので、XSS攻撃から保護します。ただし、これはCSRF(クロスサイトリクエストフォージェリ)攻撃に対しては脆弱なので、それに対する防御策も必要となります。
-
interfaceとtypeどちらを使うべきか
interfaceで出来ることはほぼtypeで表現でき、interfaceだと知らないうちに型が拡張されていたということが生じるため、ほとんどのケースはtypeで良いと言う考えもあります。しかし、objectに対してinterface用いて、それ以外の値などの型に対して、typeを用いると言う考えの方が、分かりやすさとか、自然な感じがあるので、そちらを推奨します。 -
コードレビューのコメントに関して
修正依頼か、質問か、あるいは提案かなど、コメントの意図や重要度を明確にすることで、無駄なワークフローを防げます。 -
useMemo, useCallbackをいつ使うべきか
useMemo, useCallbackの生成コストより、再レンダーが重くなるもののみ、useMemo, useCallbackを利用することを推奨します。
基本的には、useMemo,useCallbackを使う意義は主に2つのケースに限られます。
・高コストな計算を含む関数の場合
・関数が依存性として渡される場合(例えば、useEffectの依存配列内や、propsとして別のコンポーネントに渡される場合)
これらのケースが該当しない場合、useCallbackを使用することによるパフォーマンスの向上はそれほど大きくありません。むしろ、useCallbackを使いすぎると、関数のメモ化自体が新たなパフォーマンスの問題を引き起こす可能性もあります。
以下リンクの内容参考になります。
https://kentcdodds.com/blog/usememo-and-usecallback -
Reactの設計に関して
このRepositoryがReactでモダンな設計をする上でものすごく参考になります。
https://github.com/alan2207/bulletproof-react
今後のエンジニアリングに関して
今後の世界はますますAI駆動になっていくので、AIを必ず意識した方が良いです。
人々はより多様化し、コミュニティが複数でき、web3によって、よりこの動きが解放されていくだろうと思っています。
プログラミング言語で言うと、
TypescriptとRustとPythonに注力すると良いです。
Typescritpは、今回述べた内容からは自明であると思います。
RustはCやC++の低レイヤーな言語を代替可能で、より開発体験をよくするものです。
ブロックチェーンでも、技術力があって、注目されているプロジェクトでは、近年ますますRustで書かれていることが多いです。
最近では、低レイヤーなパフォーマンスが重要な処理だけでなく、通常のAPIとしても採用されているケースが増えてきているように思います。
PythonはAIやビッグデータ処理の需要で、人気を維持していくと思います。
最後に
良かったと思ったら、いいね❤️していただけると嬉しいです!