Choosing boring technology for building websites
21st October 2022I was discussing with colleagues the other day that there's an entire generation of peers who have never made a website any other way than a single-page application with React.
Despite what some overly-invested thought leaders will tell you, SPAs and React are probably not the sensible default, and never have been.
So I decided to articulate my own thought process for choosing how to build a website* in 2022; partly to consolidate my own thoughts around defaulting to boring technology, but mostly to share with my colleagues what happens if you swallow the red pill.
*"Website" is somewhere in the vague artificial Venn diagram intersection of Storefronts, Content Websites and PIM Applications holotypes.
I start by asking some pretty simple questions which quickly eliminate options.
Does it need a CMS? Has that already been chosen? If you do need a CMS and it's already been chosen for you, you can choose to either build into that stack (e.g. Twig templates with Craft/Drupal) or adopt a headless architecture and bring your own frontend. Guess which one is simpler.
Is there an existing design system of UI components that must be used? If you need to implement an existing design system, and that system is already packaged in a particular flavour of frontend framework (e.g. React) then you're already being pushed one direction. This may conflict with your prefered CMS architecture.
Is the site static brochureware or does it have complex dynamic functionality? If the content rarely changes then static site generators, CDNs and aggressive caching are a sensible option. If you need content from external data sources, user authentication, ecommerce etc then you probably need to consider server-side business logic and potentially reconsider building on top of a CMS.
Based on answers to these questions I'll work through the following list of sensible defaults. Top to bottom. Simplest and most boring to most complex and complicated. Developer experience is not king.
1. Static site generator
I default to Eleventy on Netlify.
- Pre-rendered HTML that can be hosted virtually anywhere (FTP, GitHub Pages, Netlify).
- Fetches dynamic data at build time if required including a headless CMS.
- Continuous deployments out of the box with GitHub and Netlify.
- Vanilla JavaScript sprinkles where required. CSS using Sass. Compile with Gulp or Parcel.
- Serverless functions available on Netlify for simple dynamic functionality.
2. Small CMS
I default to Craft with Docker.
- Use the built-in templating approach e.g. Twig but structure as components with Fractal.
- Vanilla JavaScript sprinkles where required. CSS using Sass. Compile with Gulp or Parcel.
- Embrace community plugins, even if these are counter to your ideals of "clean code".
3. Multi-page web application
I default to Express, EJS and Sequelize with Docker.
- Well-trodden path to get up and running with routing, models/ORM and templating.
- Vanilla JavaScript sprinkles where required. CSS using Sass. Compile with Gulp or Parcel.
- Add Turbolinks or HTMX for a SPA feel.
4. Fullstack web application with React
I default to Remix, Sass/CSS Modules and Sequelize.
- Best if forced to consume a React-based design system.
- Benefits of the multi-page app paradigm and embraces progressive enhancement.
- Actively avoids client-side state management complexity.
- Deploy targets can still be Netlify/Vercel as a quick default.
Notice I don't include create-react-app
or Next.js in my list of defaults.
I've watched too many teams (my own included) spin their wheels developing Backends for Frontends APIs that are only ever consumed by a single coupled SPA! An artificial solution solving an artificial problem. Remove the deliberate SPA and this artifical problem goes away.
If a team is developing a separate Node API for a separate React SPA frontend, more often than not this can be simplified by pushing both into a single monrepo. Then maybe distil further to a single Next.js app with API routes. Then maybe distil even further to just use Remix and remove the API layer entirely!
Also notice I don't include Ruby on Rails, .NET Core MVC or Laravel in my list either. These are all truly excellent frameworks, and given that most of what we do is still putting strings into databases and then taking them back out again Rails is still one of the best options for MVP web apps in 2022.
But my above list is also a gross over-simplification of the decision process. There are dozens of other factors that come into play such as technology choice fitting into a wider organisational engineering strategy and the technical experience of the team developing and maintaining the thing, to name but two.
I haven't even mentioned enterprise content management systems, alternative React metaframeworks, Web Components, Astro and islands architectures 🤯 All good solutions, (mostly) solving genuine problems. But like a React single-page-application they are just tools in a very large toolbox. Use them by exception, not by default.
Remember, under engineeing is just fine.