The Beauty of Smol Things
• Reading time: 6 minutes
Every once in a while, things become too much. Nature demands more energy from the big and bold things to survive, but less so from the small ones. As with the seasons, music and fashion, software isn’t exempt from this phenomenon — many apps hit the reset button in pursuit of bygone purity. Instead of focusing on the dull plethora of to-do apps, I want to highlight a few impactful moments of rejuvenation in the recent history of web engineering.
Frontend engineers were aghast when Flux was released. It proposed a simple app architecture by mandating a unidirectional data flow. Flux allows any part of an app to broadcast messages, and behavior to be consolidated in store objects. This replaces MVC, where Facebook as an organization struggled with redundant and derived data at scale. The initial public release comprises a mere 237 SLOC and only defines the message dispatcher — everything else is a pattern for developers to apply. It’s a pleasure to read these few lines, knowing that they have sparked a small revolution.
Flux spawned a bunch of spiritual successors like Flummox (which I used in my 2015 bachelor thesis) and Alt that focused on the integration with frontend frameworks and reducing boilerplate code. The domain of state management had seen many solutions before in all shapes (baobab) and sizes (Backbone, Meteor), trying to push updated data to every corner of an app.
While Flux added utilities to its API surface, Redux came around. It simultaneously extended and simplified Flux, bringing it closer to vanilla JavaScript. Actions that were function calls are now plain JS objects and action handlers that mutated singletons are pure functions, „reducers“. The v1 Readme explains the concept in no more than three lines. Even though Redux is view-agnostic, it offers a simple built-in React integration. Redux v1 does all of this in 222 SLOC.
In his introductory tutorial, the creator Dan Abramov implements a working store within barely two minutes. The video is over in the blink of an eye, and this is what makes it so elegant. It rather feels like some sort of axiom than code golf. I wish more library authors published an honest „You might not need this library“ section in the appendix.
The tremendous success of Redux shows that apps of all kinds can be built with an abstraction simple enough to be read and understood in a few minutes. Other powerful tools like Underscore.js and CoffeeScript were produced using literate programming, a technique that enables code to be written and read like prose. It’s popular among data scientists but never became mainstream.
Ever since, we have moved on to greater ventures. React has gained features to help mitigate large bundle sizes, which enables us to deliver more features (and get away with more dependencies). Next.js is no longer just a toolkit for isomorphic (excuse me, universal) apps, it spearheads the implementation of React specs that are not even built into React itself yet. VC money now fuels some open-source projects, which accelerates innovation at the cost of commercial expectations.
When creators benefit from vendor lock-in, consumers will expect more features. Marketing suggests it’s never been easier to build and support a large-scale app out of the box while „beating Amazon’s performance“. An ecosystem of tech influencers and workshop peddlers thrives from complexity that comes as collateral. Everyone who’s committed to the game wins and the wheel keeps turning. Along the way, there is some undeniable innovation but also lots of FOMO.
This problem is not unique to JavaScript. Some enterprise Java frameworks and apps are among the worst offenders — when documentation is scarce or not publicly accessible, complexity is exploited for gatekeeping and rent seeking. Pay for a ticket to enter the show!
There is often a lighter alternative. In the frontend domain, Gatsby has Astro. Astro has Fresh. Next.js has client-rendered React itself. React (44kb) has Preact (4kb), Redux (6kb) has Zustand (1kb), Recoil (23kb) has Jotai (2kb). A naive Jotai implementation can be expressed in 84 SLOC.
Obviously, neither of them are the same thing, but it helps to take a step back and consider what you really need at the moment. In many cases, the job can be done with old-school server-only rendering and a few sprinkles of JavaScript, where modern libraries like Hotwire, Htmx and Alpine can help with progressive enhancement.
At Ren, I am responsible for a large-scale web frontend, and I push the team to keep things small. Before adding a dependency, we vet its approach to a solution and carefully weigh up its size impact against what it brings to the table. If an implementation exceeds a certain length or complexity, we try to find a more concise solution while staying expressive. Even though we scale our company up and add features, we keep the codebase tight.
This endeavor is neither radical nor new. You may have noticed I have merely been paraphrasing Occam’s razor, a problem-solving principle that is not only applicable to software engineering. Back in 2011, microjs.com sought to direct attention to microframeworks and -libraries that solve common problems. Most entries are now outdated, but it’s quite telling that moment.js used to be smaller than 5kb before it got kicked from the site (it went from 3kb at v1.0.0 to more than 70kb at v2.29.x). Better deliver some value after your users have downloaded your multi-megabyte bundle and invested brain cycles to learn yet another app.
Certainly, the briefness of code is not equivalent to its efficiency, and it’s fair to reach for a fully featured framework when you already know where you are going. Aiming for compactness should never be an end in itself and instead remain a discipline like weeding your garden. Small libraries have less friction to get started with, to learn and hack the internals. It’s about the ambition of reducing a solution to its essence, creating a substrate for follow-up innovation.
The web platform has never been more abundant with functionality. We need to look closer at the power we already hold and not give in to the ones that try to fatten us for profit. It’s the smol things in life.
Appendix
Smol things:
- https://medium.com/choojs/a-better-frontend-experience-7b0498c85658
- http://ashkenas.com/docco/
- https://github.com/koajs/koa
- https://github.com/vercel/swr
- https://github.com/developit/mitt
- https://github.com/nanostores/nanostores
- https://tinybase.org
- https://motion.dev
- https://github.com/morishitter/picostyle
- https://github.com/0no-co/wonka
- https://onethingwell.org
- https://archive.org/details/ipod-shuffle-ad-from-wired-32005_3262005645_o
Not so smol things:
- https://rxjs.dev
- https://angular.io
- https://www.framer.com/motion/ (to be fair, it’s just the bundle size, the API is still very minimalistic and super powerful)
- https://kentcdodds.com/blog/how-i-built-a-modern-website-in-2021