· 6分で読了

ボーリング・テクノロジー

# 雑談
この記事は中国語から自動翻訳されたものです。翻訳によりニュアンスが失われている場合があります。

ソフトウェア開発で次々と新しいものが生まれる時代に、僕はこの定番記事——Choose Boring Technology、「退屈な技術を選べ」をあらためて振り返りたい。

開発の現場では、言語選定が時代遅れだと不満を言うことがよくある。PHP の屎山コードは全部 Rust か Golang か、思いつく限りのどんな言語でもいいからリファクタリングすべきだ、とか。データベースも Postgres や MySQL ではもう古い、Mongo、DynamoDB、Cassendra、Neo4j を使ってこそ大規模トラフィックに対応できる、などと考えがちだ。

たいてい、「何か新技術を導入すれば今ある問題を解決できる」という発想は、開発をまだ十分に整理して考えられていないことを示しているにすぎない。ただし、このことを実感するには、ふつう時間の蓄積が要る。新技術を見ると目を輝かせる少年から、脂ぎった中年のおじさんになるまでだ。

こういう話を他の理解していない開発者にすると、彼らはそれを「新しいものを学びたくない老人の発想」だと思うだけだ。しかし、浮き沈みを見てきて、第三の境地である「山は山、水は水」に至って初めて、退屈な技術こそがしばしば最良の技術だと分かるのだ。

僕らは生来、退屈を好まない。退屈は停滞や新鮮味の欠如を意味するからだ。だがソフトウェアの世界では、「退屈」は安定、成熟、そして実戦で検証済みであることを意味する。技術の刺激は、その裏で保守運用の代償を払っていることが多い。

Vercel の継続的な推進もあって、多くの SaaS サービスが Next.js でフルスタック開発を行っている。LLM と Vercel 自社プラットフォームの強みを組み合わせれば、素早く製品を出して、そのまま手間なくデプロイできるのは確かだ。ただ、その一方で、そこで生まれる複雑さにも僕は気づかされた。

たとえば React コンポーネントを書くにも、フロントエンドとバックエンドの差異を理解する必要がある。Server Actions / Server Component / Client Component の選択も、彼らが主張するより良い UX を実現するためのトレードオフだ。

別に Vercel が悪いと言っているわけではない。Next.js との統合は非常によく、SSG / SSR にも対応し、純粋な静的生成もできる。SSR 版では、Next.js にサーバーが必要な関数を自動で Edge Functions に変換し、自動デプロイ、ロールバック(Rollback)、そこそこ使えるログとアラートの仕組みまで備えてくれる。これらはかなりありがたい機能だ。

ただし、欠点がまったくないわけでもない。要件がだんだん複雑になると、ライブラリ選定だけでもかなり頭を悩ませる。

  • 権限管理
  • フォーム検証
  • JWT などの仕組みの実装
  • データベースの migration 管理
  • Websocket や SSE
  • Background Job / Scheduled Job / Cron Job、あるいは何らかの非同期処理機構

あるいは、Edge Functions とローカル開発の違いを認識したせいで、コードによって挙動がまったく変わってしまうこともある。

これは選択の自由とも言えるが、立ち上がったばかりの会社や開発者にとっては、こうした場所で時間を使ってハマるのは、たしかに面白い体験ではあるものの、製品を早く出したい会社にとっては CP 値の高い選択ではない。多くのフレームワークはかなり早い段階でこれらの問題を解決している。たとえば先に挙げた仕組みは、Django / Laravel / Ruby On Rails ならいずれも備えている。

僕の今の仕事では、画像処理を多く扱う関係で CPU-bound の最適化が必要になり、最終的に AWS SQS で解決した。ただ、それ以外の部分は Django の標準機能だけで実装している。Django は非同期サポートがそれほど強くないものの、実際にかなりの時間を節約してくれた。デプロイもかなり注意を払うべきポイントだ。というのも、ここは簡単に複雑化してしまうからだ。


では次に、僕が選ぶならどうするかを話そう。僕は、退屈なフレームワークには次のような条件が必要だと考えている。

  • 標準で完全な Database Adapter と Migration 機構を備えていること:見落とされがちだが、migration があることは極めて重要だ
    • これは時に諸刃の剣でもある。たとえば一部の DB には uuid の実装がなく、アプリ側で uuid を生成して DB に書き込む必要がある
  • 上記に加えて、便利で柔軟な ORM があること:Django や Ruby On Rails のモデルは書いていてとても気持ちがいい。同時に、必要なときには Raw SQL Query を叩けることも重要だ。ORM がもたらす抽象化や柔軟性のなさを嫌う開発者が多いのは知っているが、その利便性と保守性を乱暴に無視するのは現実的ではない。
  • ログイン、権限制御の仕組みがあること:少なくとも API レベルで権限制御ができるべきだ。たとえば Django ならこうだ。
# request が来たとき、設定された permission に基づいて制御される
class MyProfileAPIView(APIView):
    permission_classes = [IsAuthenticated, xxxGroupPermission]
  • Background Job と統合できること:Django でよく併用される Celery や、Ruby On Rails の ActiveJob のようなものだ
  • Cache をサポートしていること:できれば adapter があり、in-memory と Redis を自由に切り替えられるのが望ましい
  • Websocket の抽象化をサポートしていること:たとえば Django の channel、Ruby On Rails の Action Cable だ
    • Websocket の要件は、ある日突然必要になることがある
  • ローカル環境で簡単に立ち上がること
  • 標準で管理画面を生成できること:Model と UI オプションを定義するだけで管理画面を作れる
class File(MyModel):
    device_id = models.CharField(
        max_length=255,
        verbose_name="設備ID",
        null=True,
        blank=True
    )
    
admin.site.register(File, FileAdmin)

こう書けば、Django は対応するページを自動生成してくれる。 データベース内の値を変更できるし、簡単なカスタマイズも可能だ。正直に言えば、この機能が後になって極めて重要だと僕は気づいた。

開発者自身に管理画面を作らせるなら、技術選定だけでもかなり時間がかかる。さらにフロントエンドとバックエンドを分離したいとなれば、UI、API、データベースの接続にも追加で時間が必要になる。それに比べると、Django はページこそ素朴だが、Model を宣言するだけで仕事の 90% を削減でき、そのうえ完全に正しいことを保証できる。非常に便利な機能だ。

どのフレームワークにも多かれ少なかれ制約や欠点はある。しかし僕は、「良い退屈なフレームワーク」には次の七つの特徴があるべきだと思う。

  1. ✅ 非同期または軽量並行処理をネイティブでサポートしていること(green thread / coroutine)
  2. 🧱 安定したデータベース統合と migration 機構があること**
  3. 🧩 使いやすく、しかし強制ではない ORM があり、Raw SQL を自由に使えること
  4. 🔐 ログインと権限制御の機構が標準であること
  5. ⚙️ Background Job やスケジューリング機構と統合できること(Celery / ActiveJob)
  6. 🪄 管理画面 UI を自動生成できること(たとえば Django Admin)
  7. 💾 完全な Cache と Websocket のサポートがあること

華やかな技術は注目を集めるが、本当に嵐を乗り切れるのは、いつだって退屈で信頼できる選択肢なのだ。

後記

DHH は Rails World の Keynote で、今の Web 開発の現状はあまりにも複雑すぎると述べていた。昔はファイルを SFTP でそのまま投げ込めばそれで公開できたのに、今では CI/CD を回すだけで 15 分もかかる。僕は DHH の見方に全面的に同意するわけではないが、Web 開発者なら多少なりとも「過度に複雑だ」と感じたことがあるはずだ。