Software can be finished - Ross Wintle
There’s quite a crossover between resilience and longevity:
- Understand the requirements
- Keep scope small and fixed
- Reduce dependencies
- Produce static output
- Increase Quality Assurance
There’s quite a crossover between resilience and longevity:
- Understand the requirements
- Keep scope small and fixed
- Reduce dependencies
- Produce static output
- Increase Quality Assurance
- Install Stuff Indiscriminately From npm
- Pick a Framework Before You Know You Need One
- Always, Always Require a Compilation Step
We’re at a point in the most ecosystems where pulling in libraries is not just the default action, it’s seen positively: “Look how modular and composable my code is!” Actually, it might just be a symptom of never wanting to type out more than a few lines.
It always amazes me when people don’t view dependencies as liabilities. To me it feels like the coding equivalent of going to a loan shark. You are asking for technical debt.
There are entire companies who are making a living of supplying you with the tools needed to deal with your dependency mess. In the name of security, we’re pushed to having dependencies and keeping them up to date, despite most of those dependencies being the primary source of security problems.
But there is a simpler path. You write code yourself. Sure, it’s more work up front, but once it’s written, it’s done.
I hold this truth to be self-evident: the larger the abstraction layer a web developer uses on top of web standards, the shorter the shelf life of their codebase becomes, and the more they will feel the churn.
This is grim:
If you look at the data below on how popular websites today are actually transpiling and deploying their code to production, it turns out that most sites on the internet ship code that is transpiled to ES5, yet still doesn’t work in IE 11—meaning the transpiler and polyfill bloat is being downloaded by 100% of their users, but benefiting none of them.
There’s a big difference between the interface to a thing being one line of code, and the cost of a thing being one line of code.
A more acute rendering of this sales pitch is probaly: “It’s just one line of code to add many more lines of code.”
And as Chris puts it:
Every dependency is a potential vulnerability
The best reason to write a library in plain JavaScript is that it lasts forever. This is arguably JavaScript’s single most underrated feature. While I’m sure there are some corner cases, JavaScript from 1999 that ran in Netscape Navigator will run unaltered, alongside modern code, in Google Chrome downloaded yesterday. That is true for very few programming environments.
And yet:
Of course, most people’s experience with JavaScript is that it ages like milk. Reopen a node repository after 3 months and you’ll find that your project is mired in a flurry of security warnings, backwards-incompatible library “upgrades,” and a frontend framework whose cultural peak was the exact moment you started the project and is now widely considered tech debt.
This is a terrific talk by Jack on how to deal with the tooling involved in modern front-end development:
- Maintaining control,
- Dependency awareness,
- Lean on browser primitives,
- Have an exit strategy.
The thinking behind the minimal JavaScript framework, Strawberry:
Even without specialized syntax, you can do a lot of what the usual frontend framework does—with similar conciseness—just by using
ProxyandWebComponents.
The pros and cons of dependencies in your toolchain.
I think some tools are a good idea. But as few as possible, and the easier they are to stop using, the better.
Now Michelle asks:
Suppose we want to stop using Tailwind one day?
Turns out it’s a bit of a roach motel, much like most JavaScript frameworks: you can get in but you can’t easily get out.
So whenever possible, the safest, and most future-proof bet is to use the native features of the web platform.
This is a great case study of switching from a framework mindset to native browser technologies.
Though this is quite specific to Jack’s own situation, I do feel like there’s something in the air here. The native browser features are now powerful and stable enough to make the framework approach feel outdated.
And if you do want to use third-party dependencies, Jack makes a great case for choosing smaller single-responsibility helpers rather than monolithic frameworks.
Replacing lit-html would be an undertaking but much less so than replacing React: it’s used in our codebase purely for having our components (re)-render HTML. Replacing lit-html would still mean that we can keep our business logic, ultimately maintaining the value they provide to end-users. Lit-Html is one small Lego brick in our system, React (or Angular, or similar) is the entire box.
Rather than thinking, “how do I combine a bunch of disparate content, templates, and tooling into a functioning website?”, you might think “how do I start at a functioning website with content and then use templates and build tooling to enhance it?”
I think Jim is onto something here. The more dependencies you have in your build process, the likelier it is that over time one of them will become a single point of failure. A progressive enhancement approach to build tools means you’d still be able to launch your site (even if it’s not in its ideal state).
I want to be able to view, edit, and if need be ship a website, even if the build process fails. In essence, if the build does fail I can still take all the source files, put them on a server, and the website remains functional (however crude).
A cautionary tale on why you should keep your dependencies to a minimum and simplify your build process (if you even need one):
If it’s not link rot that gets you then it’s this heat death of the universe problem with entropy setting in slowly over time. And the only way to really defend against it is to build things progressively, to make sure that you’re not tied to one dependency or another. That complex build process? That’s a dependency. Your third party link to some third party font service that depends on their servers running forever? Another dependency.
The modern web wouldn’t be possible without big ol’ JavaScript frameworks, but—but—much of the web today is held back because of these frameworks. There’s a lot of folks out there that think that every website must use their framework of choice even when it’s not necessary. And although those frameworks solve a great number of problems, they introduce a substantial number of trade-offs; performance issues you have to deal with, complex build processes you have to learn, and endless dependency updates that can introduce bugs.
On framework-dependency and longevity:
So it’s not even so much about being wary of React or Vue, it’s about not making assumptions, being cautious and cognizant of future needs or restrictions when proposing a tech stack. Any tech stack you choose will ultimately become a ball-and-chain, not just those based on JavaScript frameworks. It’s just that the ball can sometimes be heavier than it needed to be, and you can anticipate that with a little foresight.
I never knew that the way I add other people’s code to my projects is called “vendoring.” I thought it was just copying and pasting.
My stack requires no maintenance, has perfect Lighthouse scores, will never have any security vulnerability, is based on open standards, is portable, has an instant dev loop, has no build step and… will outlive any other stack.
I’m very selective about how I depend on other people’s work in my personal projects. Here are the factors I consider when evaluating dependencies.
- Complexity How complex is it, who absorbs the cost of that complexity, and is that acceptable?
- Comprehensibility Do I understand how it works, and if not, does that matter?
- Reliability How consistently and for how long can I expect it to work?
I really like Rob’s approach to choosing a particular kind of dependency when working on the web:
When I’m making things, that’s how I prefer to depend on others and have them depend on me: by sharing strong, simple ideas as a collective, and recombining them in novel ways with rigorous specificity as individuals.