Innovating beyond libraries and frameworks
I believe we should look beyond libraries and frameworks and rediscover the value of patterns and principles, and I’d argue that it would lead us to have less breaking changes and add more longevity to the stuff we build.
I have been a big fan of the write libraries, not frameworks argument for a while now. Lately, I’ve come to ponder that there might be a fruitful expansion to this argument, that we should start to value principles over patterns, patterns over libraries, and libraries over frameworks.
Let’s clarify some terminology:
-
Framework: This is (usually) someone else’s code that calls your code. In order for this to work your code will need to conform to constraints set down by the framework. These constraints are often firm boundaries that’s hard to code around. On the flipside by coding within the framework’s conventions and constraints you tend to get a lot of useful functionality out of the box making coding quicker.
-
Library: This is (usually) someone else’s code that you call from your code. A library tends to be some code that imposes fewer constraints on your code as compared to frameworks. By using one or more libraries you’re able to reuse someone else’s code to solve your problems. Libraries are easier to combine and interchange, while putting frameworks on top of frameworks can lead to a bad time.
-
Pattern: This is a descriptive, reusable approach to writing your code (see: software design pattern). Patterns range in their vaguenes, applicability and prescriptiveness. Examples of programming patterns include early-return pattern, builder-pattern, actor model, model-view-controller, onion architecture, microservices, majestic monolith, monorepos, and flux architecture. These are just some patterns that I can think off from the top of my head, but there are many books that cover programming patterns.
- Principle: This is some general guidance or philosophy expressed as rules (like a rule of thumb) that helps you write good code. Although programming is a very young field — for instance compare this to the field of carpentry who have been building houses for thousands of years — developers have managed to distill some useful principles to aid themselves in their work. Examples of principles include SOLID principles, Don’t repeat yourself (DRY), You Aren’t Gonna Need It (YAGNI), Agile manifesto, Law of Demeter, Hyrum’s law. There are many more to be found out there, and as I write this I just discovered this site that collects programming principles, nice!
Having listed all these helpful points, I must also add a caution here about not to overuse them and/or apply them in ill-fit contexts (see: Law of the instrument). Case in point, here’s an article titled The Wrong Abstraction where developer Sandi Metz gives an excellent critique of developers’ tendency to apply the DRY principle at all costs.
Who maintains the maintainers? #
So, I’m not here to argue that we should stop using frameworks and libraries, but we should recognize that using them is a tradeoff where we gain some functionality by incurring some costs. For instance there’s a cost in having to keep up with updates. Codebases that lag behind on updates can become vulnerable to security exploits. Conversely, if you uncritically install the latest updates you open the door to supply-chain attacks where malicious code is inserted into one or more packages.
Beyond security-related costs there’s also the risk that the code you rely on becomes abandon-ware because an author burns out or an in-house team changes focus. Apparently, all sorts of stuff is discontinued all the time. And to this you might retort, “it’s open-source we can always fork it.” That’s true, so will you maintain it? I think it’s useful to ask oneself when adding any given dependency what’s the chance of this being abandoned and if so what do I do then, maintain it or switch it out? Libraries tend at least to be more replaceable.
How do we gauge whether some code project is in danger of being abandoned? We tend to look at the recent activity, just try searching Github for “is this dead”. Faced with an onslaught of such questions authors can either declare bancrupcy and archive the project, not respond to the issue and fade into oblivion, or put the pedal to the metal by pushing out new features and expanding the code’s scope to solve any problem ever encountered by any user of the code. Succumbing to such a feature-fever quickly leads to breaking changes, necessitating codemods, documenting upgrade-paths and responding to issues by developers who are desperately trying to keep up with all the changes. Any technology that matures and stabilizes risk being declared “dead” and shunned by a hype-sensitive industry which is something I covered in How good code dies.
Others’ code can also impose a hidden change-cost that makes it painful to upgrade or move away from it. Frameworks and libraries often introduce concepts that are specific to them. In other words all the experience that you build up by using and debugging this tool risk being useless if you decide to move to another framework or library. Bat an eyelash and your framework releases a major version upgrade removing old framework-specific concepts and thereby by making all your hardwon experience irrelevant anyway. I think this cost is particularly insidious especially coupled with the sunk-cost fallacy.
Beyond libraries and frameworks #
I believe that in our search for new and better ways to build technology we should not confine our thinking to only looking to libraries and frameworks for answers.
So, what lies beyond?
To start to answer that I’d like to point to some interesting undercurrents I’ve stumbled upon on the web. First, there’s an interesting talk by Adrian Holovaty titled A framework author’s case against frameworks where he argues that it’s fully feasible to build rich web applications without a framework. Second, I was pleasantly surprised when I read the Remix Run framework’s philosophy:
We abstract enough to optimize your app’s performance […], without hiding the underlying technology. Learn how to prefetch assets in Remix with links, and you’ve learned how to prefetch assets in any website.
This quote reads like an antidote to the change-cost I covered above. It’s a plus if learning some technology means learning transferrable knowledge.
Third, I learned about the concept of the Stackless Way and read a very interesting article by Elise Hein who explored this nonorthodox idea of going stackless by not having a build-step and not using a framework.
When I built my workshop on web components I also opted to explore how far I could go with a stackless approach. What I found was that our modern browsers are very capable and that I was able to write rich functionality without pulling in external code. It felt quite exhilarating and freeing. Yes, there’s still some rough edges to this approach. However, what’s missing might not be mature frameworks or libraries but rather a cookbook of patterns and principles.
Imagine that, an industry that’s a little less reliant on libraries and frameworks because we invest time into discovering and cultivating patterns and principles. I believe that’s a future with less maintainer burnout, less breaking changes and better, more long-lived programs.
Moving forward you’ll still find me using frameworks and libraries, and maybe I’ll even author some new one’s. Nevertheless, this stackless approach and searching for useful patterns and principles all the while prioritizing transferrable knowledge is something I aim to explore more.