Letâs talk about âstatic sitesâ / âpublic sitesâ / âmarketing websitesâ â whatever you might call them in your organization. Itâs your façade. Your companyâs digital face. Where all of your SEO juice pours in and you hope that Google indexes favorably! That project that you probably donât touch all that often, probably donât want to, and yet somehow get co-oped into fully rebuilding every 2-3 years (why is that?!). Youâre reading this on ours right now đ.
If youâre anything like us, Iâd bet that yours is built on some kind of âJAMstackâ technology. You know, that craze of
itâs a static site but… â¨coolerâ¨
or
it has a dynamic front end and đŞAPIs!! So… powerful
that hit in ~2020? Have you heard much about those tools and systems in the last couple of years? We havenât đŹ. Apparently, as of August 2023, the JAMstackcrazeisdead. So where does that leave us? Whatâs next? Do we have to rebuild our public site again, this time on the next new hotness? *sigh*. In the last five years weâve rebuilt from Gridsome to Eleventy to NextJS, each taking a significant amount of time and effort.
And, for the record, Gridsome is certainly dead:
The Eleventy project seems pretty quiet these days:
And, while the NextJS project is fairly active and alive, itâs definitely not something Iâd call âsimpleâ or âstaticâ anymore… more on that later:
But letâs rewind and ask my favorite question.
Why do anything at all?
The obvious question you may be thinking to yourself is, âwell sure the JAMstack hype is over but why does that mean you have to rebuild anything?â And youâd be wise for asking! Just because the marketing craze for a particular framework or style is over doesnât mean you suddenly have a broken application! There are tons of Angular v1 apps out there that still work. Heck, look at every WordPress site still running half the internet đ.
But then therein lies the reason for change: would you want to go work on one of those Angular v1 apps? Would you be excited to go work on the internals of a WordPress site from a couple of years ago? For us itâs a resounding:
And where thereâs too much friction to touch something, thereâs a dying app. We want updating our public site to be a joyful process! Not a grueling one.
So, ultimately, what weâre talking about here is developer experience and ergonomics. We donât actually care about the JAMstack craze ending as it pertains to dramatic articles or marketing campaigns, but it does matter to us in that there were a lot of promises with JAMstack that didnât pan out.
đ Note
đśď¸ Side-bar rant that has little to do with our own marketing site since everything here really is static: I canât help but notice and begin to roll my eyes, just slightly(!), as all of these ânext gen static site generatorsâ have moved to support dynamic endpoints. After years of talking about how build-time-generated pages are the future â¨, theyâve all succumbed to the reality: back-end computing is necessary for anything resembling actual application logic!
For all the talk of JAMstack, these frameworks are sure starting to look a whole lot like PHP with serverless Functions rather than Jekyll… đŽâđ¨đ. Okay, Iâm sorry! Back to the content!
Feeling Pain
One of the things we feel really strongly about on our team is the pain principle, or what some call âpain driven developmentâ: wait until something causes you pain to work on it or fix it. Essentially the flip-side of YAGNI. That allows us to work on the things that are really important to our users (what causes them pain) while making do with systems that might annoy us, but arenât yet painful.
Unfortunately, our NextJS site became painful. And some of this relates to the âpromises with JAMstack that didnât pan outâ I mentioned before. JAMstack tools were always promised as being simple, easy-to-understand, and quick to get working correctly. They were supposed to be the prodigal children of simplicity and ship-to-production paradise!
Thatâs… not been our experience. Weâre not here to write a rant list, but here are a few highlights of what became painful:
Fragmented Tooling. While there are several evidences to this point, none stands taller than image-handling. Every JAMstack framework seems to have its own solution for images, but theyâre all complicated. And in most cases, the tooling you use to construct pages and display images on your machine as you build the site is a fully separate process and procedure than how you load and prep those images in production. Local development and production processes for images often feel like two completely different workflows. The end result being that images work fine while building the site but doesnât work at all when you take it live. Thatâs super frustrating.
Local Functions. Serverless functions in the cloud have long been touted as easy and scalable while being an actual-deployment nightmare. Unfortunately this remains true with the JAMstack abstraction layers built on top of them. Trying to test them locally with Netlify or Vercelâs abstractions is a maze of emulators and makes debugging essentially just trial and error. And even if you do get them to production, it can be difficult (or just impossible) to figure out which version of the function is running, what itâs logging, how itâs executing, and why itâs sometimes slow.
Composable Features. Serverless functions and images arenât the only components named under the new âComposable Webâ umbrella â basic web forms are in there too. âComposableâ in the sense that âwhen you need them, you just plug them in to your site!â Just like Legos đ. But they stub your feet just like Legos too. Web forms are some of the most basic, most original components on the web. But when using JAMstack platform tools to handle form submissions, we find over and over that we canât test these forms until we push them live. Weâre constantly pushing to production to implement, test, and hot-fix forms. Production headaches because of untestable forms? Thatâs crazy.
Content Complexity. You might think that a âstatic siteâ means static files, but youâd be mistaken! NextJS allows for a wide range of static and dynamic routes and often blurs the line in the middle. Letâs not even get into Reactâs front-end re-hydration system. You can have some back-end (dynamic) pieces, some static (build-time) pieces, some React on-hydrate pieces, and some purely user-interactive javascript pieces all running in the same page. If that sounds complicated, itâs because it is!
NextJS isnât static, anyway. We moved to NextJS because we thought it would be a simpler, static experience for a marketing site that doesnât need much. Unfortunately the reality wore in on us over a couple of years â NextJS isnât static and is just another web back-end that we need to learn the ins and outs of, learn to maintain, and learn to work with. The promise of static-site-simplicity just isnât here.
Now, back to the âJAMstack is deadâ bit â with most JAMstack projects being dead, close to dead, or in some lesser degree of development, we donât have any hope that these gripes will get better. The complexity, the layering, the difficulty in just getting some simple images working for a blog-post… these are all small cuts that ultimately cause pain. And itâs enough pain to do something about it.
Besides, if we see this âfailed to compileâ error page ONE MORE TIME….
A Change is Gonna Come đľ
So whatâs next? What can we do that will get us to a place of less pain? It canât be JAMstack, that much is clear. Even outside of NextJS for this site, we found similar pains and issues with Gridsome and Eleventy previously. Packaging back-end features as drop-in plug-ins for âstaticâ sites has proven to be a no-go over the years. We want a robust system with a long history, flexibility available when we need it (simplicity when we donât), and no âcomposable featuresâ â it should all be in the same box. Also, everything should run locally!
To which we had the natural response (that we think most people have):
That feels like itâd be way more power and size than we need…
And, as one person on the internet (where the best advice comes from) put it:
Don’t use Rails for this.
[itâs like] buying an airplane because it has peanuts in it.
Remember, this is still just a static marketing site with some blog posts and landing pages.
Cue Adam again:
Yeah but… so what? If thereâs less pain here and we donât use those features, who cares?
Which sold us on giving it a try! Let the Rails Static Site journey begin…
The Rails Static Site
âRailsâ and âStatic Siteâ are two phrases that donât ordinarily go together. In fact, once we dig in even a little bit, we realize that we have no idea how these phrases should go together. How does one create a ârails static siteâ? What does that even mean?? âStaticâ traditionally meant âno back-end runtime applicationâ which is precisely what Rails is!
Well, weâre not the first to venture into these lands. Thereâs a whole landscape and history here we can explore. Here are just a few of the options we discovered.
Option 1: Build a Rails app and also, a local crawler. The idea here is that you build your Rails app fairly normally but, once done, you also build out some kind of crawler process. When running locally you just run the Rails app. When going to production, your CI process executes the crawler, puts all the plain HTML files the crawler found into a directory, and ships that to a static host as your website.
This is a clever idea, for sure. When we decided to try Rails we really meant running a live Rails application on a live server, not trying to find a hacky way to turn a Rails app into a legitimate static website. So… kudos for the cleverness. Weâre a hard pass on this model. It sounds a little complicated for our liking and, again, weâre after simplicity. Also, this adds a layer of potential confusion to the stack of what-runs-where. Fun idea though!
Option 2: Use a Rails-reverse-proxy to actually run a static website inside of your Rails app. This one took me a few minutes to wrap my head around, but it is a legitimate path forward. This is TestDoubleâs static-rails project. Now, it seems like this project was built closer to when JAMstack was hot, so likely an accommodation layer to allow folks to run proper JAMstack systems and Rails in tandem, but nonetheless, the idea goes like this.
Here’s what it does:
In development, static-rails launches your sites’ local servers and then proxies any requests to wherever you’ve mounted them in your Rails app so you can start a single server and transition work between your static sites and Rails app seamlessly
When deploying, static-rails will compile all your static assets when rake assets:precompile is run, meaning your assets will be built automatically when pushed to a platform like Heroku
In production, static-rails will serve your sites’ compiled assets from disk with a similar features and performance to what you’re familiar with if you’ve ever hosted files out of your public/ directory
So.. itâs interesting. In development itâll run your JAMstack project as a fully separate process, essentially running the Rails app and JAMstack app in parallel and proxying appropriate requests between them. In production itâll compile your JAMstack app (which I suppose then means it must be fully static, not dynamic at all) then serve it a la /public. In general I thin this feels like gluing two non-cohesive apps together… and ultimately we still have a great deal of complexity to keep track of here as there are no shared assets, components, or concepts between the two apps. This is mostly just a convenience of orchestration. Itâs not a project solution.
Option 3: Just run Rails. This is where we get a little tongue-in-cheek. At this point, weâre really not even suggesting a âstaticâ site at all. What if we just built a simple Rails app with hard-coded (ERB) markup? What if we just ignore the M in MVC and serve up some views from our controllers?
As it turns out, Rails has gotten really fast over the last decade. If you mix in the quickness of not hitting a database in your request, sprinkle on some Turbo cocktail sauce, and top it off with Cloudflare, youâve got a very fast website from a Rails app.
â Tip
We didnât actually find it until after we rebuilt our public site and thus we have our own home-baked code that does essentially all the same things, but we recommend checking out sitepress. Weâre not going to get into the nitty gritty of how to set your Rails app up for âstaticâ content, but Sitepress does a fantastic job of making that easy, smooth, and simple. I (Jon) have since used it for another static site I converted to Rails and find it to be quite delightful. While you can write pure HTML and ERB in Rails from scratch, Sitepress adds a nice layer for getting Markdown to play quite nicely with Rails too (which is how these blogs are written!).
Just Run Rails
As I mentioned in the tip, weâre not going to dive too deeply into how one actually sets up a Rails app for static content â though Adam did just release a video that does. At a very high level, just fire up rails new and begin writing views! Hard-coded ERB and HTML is static content. In fact, the welcome page that comes out of the box with rails newis a static page â some hard-coded markup served by a virtually empty controller.
There are a few things we do want to address though, both from experience and logic. Let the flame-war begin!
Rails is too big of a tool for the job. Trust us, we felt that way too. But the reality is that once youâre familiar with Rails as a project system (the conventions, if you will), running static content in Rails is actually quite a small endeavor. You get to essentially ignore the concept of Models altogether, likely ignore Controllers completely (if using something like Sitepress), and just focus on writing a few layouts and static views as needed.
In fact, and especially if youâre using Tailwind, suddenly your Rails project turns into simply the ERB views backing each page. Sure, youâve still got a config directory and all the other out-of-the-box files, but the project exists almost entirely in ~/app/views. Itâs pretty simple!
Rails is complicated. Okay, this is true. Rails is a full fledged application development framework and holds a lot of strong ~~opinions~~ conventions about how MVC ought to be implemented (e.g. being Resourceful). Weâre not claiming that Rails is simple, just that itâs simpler than some of the popular JAMstack systems now in common use. Understanding Railsâ conventions around MVC and the basics of the server/client model are, for sure, easier to understand than how to write server-side React code that may-or-may-not actually run server-side, then get delivered to a client, re-hydrate, and may-or-may-not do things on the client side then too. Yes, Rails is complicated. No, Rails is not as complicated as many of these JAMstack semi-dynamic systems.
Rails requires a server! Sure does! But so does every static site and every accessible thing on the internet ever. Just because a static site is files served from a plain web-server (CDN-node) doesnât mean itâs not a server. We still recommend running green-field Rails apps on Heroku, and at that abstraction layer, itâs about the simplest âserverâ setup around. Plus, itâs been around a long time and there are answers for every question you could possibly have along the way. (Looking at you, Vercel!) The server isnât a bad thing!
…Servers cost money. You can happily run a static-content Rails app on Heroku for $5 per month. That is a rounding error in the monthly budget of most people that write code professionally. This isnât a serious argument.
…Servers canât scale well if we go viral! đ If only there was an autoscaling plugin for Heroku that offered a forever-free tier specifically designed to handle an application going viral and automatically scaling the app up to handle it! đ Judoscale what? đ
Okay, but seriously, there is a valid concern to address here. Ignoring those somewhat-dynamic âstatic+â frameworks that will also depend on servers, one of the benefits of truly static sites is that their static files can be distributed out to CDN nodes and have no traditional origin server. Thatâs a big marketing point for static sites. But is it really that impactful?
The commonly-cited other end of the spectrum here is a traditional Wordpress site. Given its nature and how itâs built, Wordpress is generally not scalable. You canât run more than a single Wordpress server/instance without some major tweaks, major issues, or running an entirely different type of Wordpress. So it is actually likely that youâd have trouble scaling up to meet traffic demands when running this setup and suddenly going viral. Compared to Wordpress, the pure serverless/CDN-node nature of a truly static site can be a big impact!
But compared to Rails? Rails sits right in the happy middle between these two worlds. Thereâs a back-end running Ruby on a server, but that server is also very scalable. Thatâs what our system, Judoscale, does! It simply spins up more Rails servers when traffic ticks up. If your servers grow to meet your trafficâs demands all the time, isnât that virtually the same as being purely static on a CDN anyway? The net result is the same: always having plenty of capacity for your traffic load.
đ Note
Just for the record, Judoscale itself handles nearly 2,000 RPS and runs on the same Heroku hardware that the Eco and Basic dynos run on. And we have multiple database calls in every request! A static-content-only Rails app on even just one Heroku Basic dyno could easily handle thousands of requests per second.
YAGNI and Peace
Letâs wrap this up with YAGNI â You Ainât Gonna Need It. The ultimate white flag against premature optimization. Thereâs a balance between YAGNI and developer happiness, peace, and joy. Sure, Rails is a powerful framework and you could make the case that you donât need all of that power â not yet, at least. Therefore, presume you wonât need it and donât use it. But the reality with tools is that we often need to build with what weâre comfortable with, even if thatâs just a very small piece of that toolâs overall utility.
As the saying goes, itâs not actually about using the right tool for the job, itâs about using the right tool for the team.
Using Rails for our public site is the right tool for our team. It took us several years and other roads to figure that out, but weâre really happy with where weâve landed. Aside from familiarity and comfort with Rails, weâre thrilled with how much room we have to grow, should we ever want to. Rails being batteries-included is a great comfort for some recovering JAMstackâers!
So what do you say?
Give it a shot â write your next static project in Rails, as static-content, not a static site.