Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 8.1 + Ruby 4.0.1で作り直したenno.jpアプリをfly.ioでリリースしました

こんにちは、hachi8833です。

2013年から私が運営している日本語のあからさまなエラーを検出するWebサービス、enno.jpをRails 8.1とRuby 4.0.1で完全に作り直し、2月8日からリリースしました。使い方はリニューアル前と基本的に同じですが、多数の改良を加えました。

リニューアルの概要については更新情報に手短に記載してありますが、本記事ではRailsやSaaSなどについてもう少し説明します。個別について詳しくは追って記事にしたいと思います。

1: SaaSをHerokuからfly.ioに乗り換えた

SaaS選定の基準は「SQLite3が使えること」「東京リージョンがあること」としました。

Herokuは現在に至るまでSQLite3を利用できず、render.comは2026年2月時点でまだ東京リージョンがない(Singaporeはある)ため、fly.ioに決定しました。

fly.ioを使ってみて

後発だけあって、ダッシュボードが整っていて使いやすい印象です。


fly.ioのダッシュボード

fly.ioの「マシン」という概念は、HerokuのDynoに少し似ている気もしますが、課金はマシン数というより「割り当てリソース量(CPU、メモリ、ボリューム、転送量など)」に依存します。また、fly.ioの「マシン」にはHerokuのようなメンテナンスモードが標準では用意されていないので、メンテナンス画面は自分で表示する必要があります
HerokuのDynoと異なり、マシンは常に動く前提なので、マシンを止めるとfly ssh consoleもできなくなります。また、fly ssh console環境にはpsptreeコマンドが標準では入っていません。

fly deployコマンド一発でデプロイできるのと、デプロイの設定がfly.tomlでほとんど済む点はありがたいです。fly deployはDockerが前提で、Rails生成のDockerfileとbin/docker-entrypointがそのまま使われるのも便利です。

ただしgitを介さないデプロイなので、不要な巨大ファイルなどは.dockerignoreにも追加して除外する必要があります。現在はローカルからfly deployコマンドでデプロイしていますが、将来はGitHub ActionsのCI連携でデプロイするようにする予定です。

UptimeRobotで監視

最小限の監視として、今回からUptimeRobotというサービスを使って5分に1回/upで死活監視を行うようにしました。UptimeRobotのこの監視は50件まで無料で使えます。個人アプリなら十分だと思います。


uptimerobot.comより

参考: UptimeRobot: Free Website Monitoring Service


偶然ですが、今回のenno.jpのリニューアルと前後して、Herokuが事実上のメンテナンスモードになることが報じられました。滑り込み感半端ないです。

2: Railsに余分なものを極力足さない

今回のリニューアルの目標のひとつが、「できるだけRails wayに沿ったidiomaticなRailsアプリにする」でした。以下のrails new以後、できるだけ素直な作りを維持しました。

$ bundle exec rails new . --main -c tailwind --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-jbuilder --skip-system-test

パスはなるべくデフォルトで

Zeitwerkはapp/services/app/components/のようなディレクトリはパスの追加なしでオートロードしてくれますが、app/assetsapp/viewsapp/javascript/などはデフォルトでオートロードから除外されます。

参考: Rails の自動読み込みと再読み込み - Railsガイド

以前の私はコンポーネントをapp/views/components/に置いていたのですが、標準のapp/components/に置く方が素直だよとAIにツッコまれたのをきっかけに、ViewComponentなどで使うパスはなるべくカスタマイズせずにデフォルトのものを使うようにしました。

3: SQLite3を利用

以下のシリーズ記事で紹介されているように、Rails 7.1からSQLite3への対応が強化され、Rails 8でひととおり完了したので、production環境で使って実感してみたかったのでした。

Railsシリーズ記事: SQLite3を活用する(全14回)

参考: 🛤 Rails 8はSQLiteで大幅に強化された「個人が扱えるフレームワーク」(翻訳)|YassLab 株式会社

enno.jpでもSQLite3を採用し、前述のようにそのためにfly.ioを選びました。DBをファイルとして扱えるのはなかなか便利ですね😋。

SQLite3のバックアップについて

SQLite3のバックアップをどうするか調べました。以下の記事が参考になりました。

参考: 最近のlitestreamと安DB界隈 --2024年の記事です

fly.ioにはlitestreamの作者であるBen Johnsonがいるのが強みと思えます👍。
なお同記事によるとlitestreamの開発が2024年頃に滞っていたようですが、現在は再び活発に動いています。productionで使うならlitestreamはいけそうです。

benbjohnson/litestream - GitHub

SQLite on Railsシリーズ(04)LitestreamでSQLiteをバックアップしよう(翻訳)

なお自分はSQLite3周りをもう少しシンプルにしたかったので、当初以下のTursoというサービスを使ってみました↓。
しかしTursoのラッパーであるlibsql-activerecordがなかなか思うように動かず、リポジトリも2年前から更新されていないのでやめました。

tursodatabase/libsql-activerecord - GitHub

enno.jpの今のサービス内容だと、クラウドベースのSQLite3はトゥーマッチ気味と思えたので、代わりに、fly.io上でsqlite3 #{DB_FILENAME} ".backup #{backup_filename}"でSQLite3ファイルをバックアップしてローカルに保存する簡単なスクリプトを作りました。

4: 開発環境をDocker化せず、miseで環境をセットアップした

私も少し前まで開発環境のDocker化(主にVS Codeでのdevcontainer)を試してみました。その前はdipを使って開発環境をDocker化していました。

参考: Dev Containerでの開発ガイド - Railsガイド

Docker ComposeとDipで開発用コンテナを再利用可能にする(翻訳)

しかし今回のリニューアルでは、enno.jp開発環境をDockerとdevcontainerなしで構築しなおしました。
きっかけは、昨年のRails World 2025でDHHが「開発環境のDocker化は面倒を増やす」「開発環境のDocker化はRDBMSだけでいい」と発言していたのを見たことでした。

再現可能なローカル開発環境をDockerなしで構築するために、以下の記事にも書いたmise(mise-en-plus)を使いました。RubyやLintツール、環境変数をmise setupだけで一発構築できるのは便利ですね😋。

miseは便利: タスクランナー兼ツールバージョン管理&環境変数管理ツール

jdx/mise - GitHub

5: テストはminitestのみ、システムテストなし

テストはminitestのみとしました。

 test
├──  components
│   ├──  patterns
│   ├──  previews
│   ├──  proofreads
│   └──  shared
├──  controllers
│   ├──  inflections_controller_test.rb
│   ├──  patterns_controller_redos_test.rb
│   ├──  patterns_controller_test.rb
│   └──  proofreads_controller_test.rb
├──  fixtures
│   ├──  files
│   ├──  inflections.yml
│   └──  patterns.yml
├──  helpers
├──  initializers
│   └──  warning_test.rb
├──  integration
│   └──  buffer_management_stability_test.rb
├──  jobs
├──  models
│   ├──  concerns
│   ├──  inflection_test.rb
│   ├──  pattern_redos_test.rb
│   ├──  pattern_test.rb
│   └──  text_matcher_test.rb
└──  test_helper.rb

参考: Rails テスティングガイド - Railsガイド

また、システムテストやcapybaraを使わないことにしました。これもDHHの以下の発言がきっかけで、システムテストの遅さに辟易していたことが主な理由です。

代わりにコンポーネントのテストを書いています。

システムテストがないおかげでテストは快速です。

$ bin/rails test
≈ tailwindcss v4.1.18

Done in 125ms
Running 172 tests in parallel using 8 processes
Run options: --seed 54695

# Running:

............................................................................................................................................................................

Finished in 1.986801s, 86.5713 runs/s, 406.6839 assertions/s.
172 runs, 808 assertions, 0 failures, 0 errors, 0 skips

現時点の課題は、Stimulusをテストする方法です。

6: ViewComponent v4とTailwind v4でコンポーネント化

ViewComponentとv4とTailwindがともにv4になったことでやっと諸々落ち着いたので、これをベースにコンポーネント化しました。

見てのとおり、コンポーネントで使うStimulusコントローラもここに置いています。

 app/components
├──  application_component.rb
├──  patterns
│   ├──  category_select_component.html.erb
│   ├──  category_select_component.rb
│   ├──  category_select_controller.js
...
├──  proofreads
│   ├──  error_list_component.html.erb
│   ├──  error_list_component.rb
│   ├──  error_list_controller.js
│   ├──  error_list_scrollback_controller.js
...
└──  shared
    ├──  copy_button_component.html.erb
    ├──  copy_button_component.rb
    ├──  copy_button_component_controller.js
    ...
    ├──  dialog_component.html.erb
    └──  dialog_component.rb

なおApplicationComponentViewComponent::Baseを継承しただけで中身はまだ空です。

viewcomponent/view_component - GitHub

Rails: パーシャルよりもViewComponentを選ぶべき理由(翻訳)

tailwindlabs/tailwindcss - GitHub

Rails: Tailwind CSSはコンポーネントに向いているCSSか?

Lookbookのプレビュー

lookbook-hq/lookbook - GitHub

ViewComponentにプレビュー用エンジンであるLookbookを組み合わせています。

プレビューはAI(もっぱらCopilot経由)で書かせ、test/components/previews/以下に配置しています。
localhost:3000/lookbook/で以下のようにコンポーネントをプレビューできます。

なお、最初に何をコンポーネント化するか迷っているのであれば、ぜひダイアログボックスをコンポーネント化することをおすすめします。

HTML: 「JavaScriptなし」で動く最新の多機能確認ダイアログを構築する(翻訳)

Tailwindベースのデザインシステムとアフォーダンス

「デザインシステム」としてのデザイントークンをapp/assets/tailwind/application.css↓に登録し、これを元にデザインの統一化を図っています。私はデザインは当然素人なのですが、今後Webデザイナーに依頼することがあってもよいようにしてみました。

@theme {
  /* フォント - 日本語最適化(Inter + システムフォント) */
  --font-sans: 
    "Inter",
    -apple-system,
    BlinkMacSystemFont,
    "Hiragino Sans",
    "Hiragino Kaku Gothic ProN",
    "Yu Gothic UI",
    "Meiryo",
    "Segoe UI",
    sans-serif,
    "Apple Color Emoji",
    "Segoe UI Emoji";
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /* Primary(Custom Blue) */
  --color-primary-50: #f0f9ff;
  --color-primary-100: #e0f2fe;
  --color-primary-200: #bae6fd;
  --color-primary-300: #7dd3fc;
  --color-primary-400: #55ADF9;
  --color-primary-500: #4BA3F8;
  --color-primary-600: #3D8AE9;
  --color-primary-700: #0369a1;
  ...
  }

デザインシステムについて詳しくは以下の記事をどうぞ。

フロントエンド開発者がデザインに向き合うための厳選ベストプラクティス7選(翻訳)

また、必要に応じてapp/assets/tailwind/application.cssに「アフォーダンス」を登録して、要素にまたがる基本スタイルを抽象化しました。「アフォーダンス」は常に通常のTailwindユーティリティクラスで上書きできます。

@utility af-btn {
  :where(&) {
    @apply inline-flex items-center justify-center px-4 py-2 text-sm leading-5 font-semibold whitespace-nowrap border border-transparent rounded-2xl shadow-sm shrink-0 transition-[color,background-color,border-color,text-decoration-color,fill,stroke] duration-300 ease-in-out;
  }

  @variant focus-visible {
    :where(&) {
      @apply outline-2 outline-transparent outline-offset-2 ring-2 ring-offset-2 ring-offset-white;
    }
  }
}

CSS: フロントエンドアーキテクチャに「アフォーダンス」層も必要な理由(翻訳)

7: recheckツールによるReDoS予防

enno.jpには9,500件を超える日本語エラー検出用の正規表現が登録されています。

これらの正規表現からReDoSを徹底的に排除したかったので、以下のrecheck(Scala言語で書かれています)をGoでラップしてCLIで使えるようにし、全件のReDoS検査を実施しました。

makenowjust-labs/recheck - GitHub

バックトラックに極力依存しないよう、普段から*+|を極力使わない正規表現を心がけていたおかげで、検査の結果、ReDoSのリスクがある正規表現は9,500件のうち数件しかありませんでした🎉。

はじめての正規表現とベストプラクティス3: 冒頭/末尾にマッチするメタ文字とセキュリティ、文字セットの否定と範囲

以下はReDoS検査用のgorecheck実行の様子です。

さらに、enno.jpのメンテナンス画面でも個別にReDoSを検査できるようにしました。


はじめての正規表現とベストプラクティス10: 危険な「Catastrophic Backtracking」前編

参考: Ruby 3.2 で ReDoS を排除する - RubyKaigi 2023

8: パフォーマンス

以下はGoogleのPageSpeed Insightsでリニューアル後のenno.jpを測定した結果です。こんなハイスコアを出せたの初めて😂(もっとも広告を表示する前の状態ですが)。

コンポーネントを適切に導入できたこと、各種バリデータの出すissueを1つ1つ修正したのが効いたようです。JSのパフォーマンスについては、Tailwindを見直してレスポンシブの再計算を減らしたのも効きました。

関連記事

日本語エラーチェックサイトenno.jpを作った理由


関連記事

該当する記事がありません。

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。