Captain Codeman Captain Codeman

Web Development Without a Framework

How difficult is it to assemble the pieces you need

Contents

Introduction

As President Kennedy once implored:

“My fellow developers, ask not what your Framework can do for you. Ask what you can do for your framework!“.

Yeah, I know, not quite the exact quote. But sometimes it feels that you have to do more to help your framework work than your framework does to help you.

Two years ago I was working with Angular 2 and if you managed to get your compressed javascript payload under 200Kb you thought you were doing pretty well. I remembered about it when I realized I can now have an app with far more functionality and be easily within 15Kb.

Maybe it’s time to ask yourself, what is your Framework really doing for you … and do you still need it?

What is a Framework?

First of all, let’s be clear what we mean by a framework. For me a framework embodies some application architecture and, as long as you fill in the right pieces in the right way, it will call your code and make it run. But the framework is the thing in charge and you work in the way that it dictates. Frameworks can be great when you are getting started because they provide some of the structure that you may not know how to build yourself but if you don’t mind “some assembly required” then libraries provide a lot more flexibility and less lock-in. The theory is you can pick and chose the libraries you want to use and replace any of them as you need if something better comes along. You could consider a Framework to be a ready-assembled group of libraries but the framework makes the rules.

Are Frameworks Still Required?

But times change. Just as jQuery made perfect sense and provided valuable abstractions to mask the myriad browser inconsistencies, but is less valuable now that things have become more standardized, so too many of the features that frameworks provide are no longer quite so necessary as increasingly the browsers have the capabilities built in directly.

Whatever framework you use, it’s easy to fall into the trap of assuming that it’s needed but the reasons it was needed some 2+ years ago, or whenever you started using it, may no longer apply or be as valid. The browser platform is developing all the time so it’s good to re-evaluate our choices from time to time to make sure we still need them and we’re not just using them because they have become a comfy blanket that we’re familiar with.

Think about it, imagine your browser had a web framework already built in, developed in native code, fast and nothing extra to download. Wouldn’t you want to use it? Or think of it another way, if you were already using the inbuilt framework, would you really “buy” the idea of adding an extra 100Kb of JavaScript to your app just to do what it could already do, with a slightly different API? Does it add enough value to justify the cost?

Instead, we can use the platform itself whenever possible and, where it makes sense, use libraries to make using that platform a little easier and more convenient, so long as they don’t cost too much in terms of performance and are stepping stones to what the platform may adopt in future.

What We Need

There are still things we need and that make sense to re-use across projects rather than re-invent and re-implement for each one so I’m certainly not advocating no-dependencies whatsoever but it’s beneficial to treat them as libraries we need to do specific tasks. So let’s look at the main pieces and options we have for using libraries to provide the functionality we need and how we can put them together.

This is an opinionated list based on what I use for my development, you should be able to replace the pieces with what you need though.

Security

If you have a web app, you have users and you need to authenticate them. If you don’t then you just have a static website so stop overcomplicating things and just use your favourite CMS or site generator such as Jekyll or Hugo or write your own.

I’m a big fan of Firebase Authentication because it’s “cheap as chips”, works great and is very easy to use. So I’m using that but you could plugin whatever other auth system you use instead.

State Management

The two-way data binding used to be all the rage but as an approach it’s really fallen out of favour and is dying out (like an evolutionary dead-end). Once you experience the benefits of one-way / immutable data flows using something like Redux you will never go back. So use Redux and learn to use it correctly - it’s well worth the investment.

Although there are alternatives, it’s worth remembering that there is a whole ecosystem of tools and libraries for Redux. We’ll use some of the common ones to make handling remote API calls and other async actions easier plus some associated libraries to efficiently create views of the data in our store.

Routing

The web is all about URLs, and mapping URLs to views in your app is what the router does. For a Single Page App it also prevents links to views re-loading the app each time and instead intercepts them and switches out the views so everything is super-fast.

The active view and the parameters for it are just state like any other and that state should be in the Redux store. If we use middleware to keep the Redux store in sync with the browser address-bar then our app can just work from that state.

Views / Rendering

We want our views to be encapsulated components and this requires two things - components, or classes, and some way to render them into HTML to show in the browser. Fortunately, the WebComponents standard now provides a component system natively built in to the browser complete with lifecycle methods we can hook into. So that just leaves the rendering of our state, via the components, into HTML. While we could code the DOM manipulation ourselves, this is one of those pieces that can be made generic and there is a new game in town for HTML view templating called Lit-Html.

TL;DR; Lit-Html lets you define the HTML template as a special type of ‘tagged’ string and can then replace the variables within it whenever those variables change but super-efficiently so it’s not wasting time updating any of the pieces that didn’t.

Think of it as the DOM-diffing of React but without having to do any of the diffing - checkout this benchmark comparison of javascript framework performance that shows it’s fast.

Persistence

This used to mean a backend JSON REST API but the the cool kids will laugh at us if we’re not “serverless” and truth be told, serverless is really convenient and as we’re using Firebase for Authentication, we may as well use Firebase for the the Datastore as well.

You may have heard of, or already tried, the Firebase Realtime Database but there’s also a newer Firestore database that kind of merges the RTDB with the Google Cloud Platform Datastore and gives you nicer structure to your data (vs the “big JSON doc” of RTDB) while keeping the live data syncing capabilities and even adding some nice offline capabilities out of the box. It can be significantly cheaper as well and provides easier / richer querying (it will be very familiar if you’ve used Google Cloud Datastore)

Source Language and Build

Many frameworks provide a CLI to build the app so we’re going to need something to build ours too. While we could use JavaScript and ES Modules for this, there is a lot of benefit to using TypeScript - we get some nice intellisense due to the static typing and it does the transpilation to JavaScript that can run in a browser so we can utilize newer language features. Babel is another option for doing this but I “want me some” intellisense and static type-safety!

We’re going to use Rollup to manage the bundling process for it - that can remove code that we don’t use and let us use plugins to provide other build features we might want (such as minification of the transpiled javascript).

It means we can use tomorrow’s JavaScript features (like the spread operator) to make development easier and faster but have it run with the JavaScript engines of todays browsers.

Libraries

Here’s the complete shopping list of libraries we’re going to use and what they deliver for us:

  • Web-Components for component encapsulation and lifecycle
  • Firebase Authentication for authentication
  • Firebase Firestore for persistence
  • Lit-Html for HTML template rendering
  • Redux for state management
  • Redux Routing for synchronizing browser URL state with the store
  • Redux Thunk for asynchronous actions (e.g. talking to firestorm)
  • Redux Reselect for efficiently selecting data from the store
  • Universal Router for mapping the URL to components and parameters
  • Typescript for development with strong types and intellisense
  • Rollup for bundling and optimizing the final code

Project and Demo

I’m still working on tidying a few things up (esp. the routing part) and will post more about how it works and how the pieces fit together plus some of the design decisions, but you can checkout the source code at:

https://github.com/CaptainCodeman/web-app-starter

You can see the running demo which is very simple but has all the pieces necessary to start building a real app with and the JavaScript payload (excluding the Firebase runtime) is currently just 14Kb compressed.

Let me know what you think and if you have any specific questions or suggestions on what to cover in future.