Skip to content
GitHub Copilot is now available for free. Learn more

Artwork: Violet Reed

Marie Kondo your software stack with open source

Mindfully consider each choice—and remember that simple is not necessarily easy

Mike Melanson // November 15, 2022

The ReadME Project amplifies the voices of the open source community: the maintainers, developers, and teams whose contributions move the world forward every day.

As someone makes more money, expenses once considered luxuries can suddenly become seen as necessities: It’s called lifestyle creep. In the world of software development, we can suffer from a similar affliction: stack creep.

Where hardware limitations once restricted developers to a minimalist approach, increased processing power, memory, and storage have led many down a more maximalist path. It’s a cycle as old as computing itself and has had countless phrases and laws ascribed to it. Moore’s Law, for example, describes the doubling of processing power over time, while Wirth’s Law counters that this faster hardware causes developers to add unnecessary features and complexity, leading to less efficient software. The saying “Hardware is cheap, programmers are expensive” points out that it’s often cheaper to spend money on more hardware than to pay someone to optimize performance. We see these concepts play out constantly, as developers build for enterprise scale when they don’t even know if their project has demand, or they immediately reach for sprawling frameworks, packed with features they don’t need now but might someday, when the basic HTML, CSS, and JavaScript stack would easily suffice. 

But let’s not conflate minimal with good and maximal with bad. Complex problems—such as building user interfaces for the variety of devices and use cases encountered by the likes of Facebook—can require complex solutions, and the JavaScript library React, originally created there for this purpose, is maximal for good cause. Similarly, Kubernetesarose as a way to handle the scale of traffic experienced at Google, so its form follows its function. You don’t need to have Facebook or Google scale to benefit from React or Kubernetes, but problems arise when there is a mismatch between the complexity of the problem and that of the solution. For example, we justify the added complexity of that sprawling frontend framework with the initial development speed it gives us, but run into issues managing dependencies and difficulty troubleshooting down the line. Or we prematurely introduce a complex management layer like Kubernetes instead of just starting with containerization and Docker.

Thankfully, in the world of open source, developers have the freedom to customize their stacks to combat complexity. Just as organizing guru Marie Kondo urges her audience to mindfully consider whether each of their possessions “spark joy” before deciding to keep or discard them, we too can carefully consider the choices that make up our tech stack. Starting our exploration at the level of the individual developer choosing their stack, we can see how the open source ecosystem offers projects covering the spectrum from minimal to maximal. While open source does not necessarily imply or encourage minimalism, having access to minimalist frameworks to choose from does enable developers to right-size their stack. And when they can’t find a project that fits their needs, open source enables them to create one themselves. 

Foundations of minimalism in software development

Minimalism and maximalism in software development have been in a tug of war since the very beginning. Just as Wirth’s Law asserts, the moment hardware became more powerful, extraneous features and complexity found their way into codebases. Theories on why minimalism remained an appropriate approach were quick to follow.

In 1978, when RAM was still counted in kilobytes, the Unix Philosophy laid out a few tenets for software development that were later summarized as “Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.” You’ll see these ideas appear again and again as the Unix Philosophy serves as a basis for many thoughts on simple yet robust software design and development.

A decade later in 1989, Richard Gabriel, a computer scientist known for his work on the Lisp programming language, wrote an article about the language with a section titled The Rise of ‘Worse is Better,’ which argues that added functionality does not necessarily improve software quality. In fact, Gabriel wrote, “The lesson to be learned from this is that it is often undesirable to go for the right thing first. It is better to get half of the right thing available so that it spreads like a virus. Once people are hooked on it, take the time to improve it to 90% of the right thing.” In other words, a “worse” piece of software (with fewer features) is “better” (as a starting point) than software that tries to offer all features out of the gate. And as if to illustrate the tenuous relationship between a minimalist and maximalist approach, Gabriel wrote numerous follow-ups arguing both for and against his original premise.

Fast forward to 2011 when Rich Hickey, creator of the Clojure programming language, gave a talk titled Simple made easy where he argued that “we need to build simple systems if we want to build good systems.” In the talk, Hickey explains that we often conflate simple with easy, just as we do difficult with complex. “Oh, look; I only had to type 16 characters. Wow! That's great. No semicolons or things like that,” says Hickey. “This whole notion of sort of programmer convenience, again, we are infatuated with it, not to our benefit.” In Hickey’s view, choosing simplicity in software is key to our ability to understand it, and our understanding enables us to easily change and debug that software over time. Choosing ease up front while ignoring complexity, he says, will give us initial speed, but will slow us down over the long-term. The key, then, is to build software using pieces that are simple, not necessarily easy.

Lastly, in discussing the idea of minimalism in computing and software development, the Pareto Principle rears its head in various incarnations in software development. Commonly referred to as the 80/20 rule, the Pareto Principle says that 20% of causes are responsible for 80% of results. In terms of software, this can be thought of as 20% of features are responsible for 80% of the functionality, that 80% of users only use 20% of features, and even that 20% of bugs are responsible for 80% of errors. The implication here is that we can achieve 80% of our goal with just 20% of the code. This seems like an obviously important principle to keep in mind when producing minimalist software and choosing the elements of your tech stack.

Wires of all different colors plugged into a machine

It all starts with a choice…

Our exploration of minimalism in software development and the tech stack can start by looking from the perspective of individual developers creating new projects. 

For Benjamin Tanone, a software engineer who brings his minimalist beliefs into his software development, minimalist software development starts before a single line of code is written. Tanone says that software is a means to an end, and that your focus should be on solving your users’ problem, being mindful about what you bring into your project, both in terms of user-facing features and the tech stack itself.

“The term ‘minimum viable product’ is mostly used in the context of user requirements but it can be applied to engineering as well: Limit the things you bring into your product to what would actually benefit you,” says Tanone, who points to developers’ tendency to import libraries and add features without fully considering their implications. “Minimalism is about recognizing what you really need and then being mindful about getting rid of the things you don't. Obviously, you need to make sure you don't shoot yourself in the foot and build something that is truly unscalable, but at the end of the day the danger of that is usually lower than the risk of over-building.”

Digital product engineer Michael Aufreiter, who built a minimalist writing platform called Ken, similarly points to those initial considerations as important for more than mere aesthetics. For Aufreiter, frameworks and abstractions on the front end serve for prototyping, but he returns to fundamentals like writing raw SQL and vanilla JavaScript to build his actual product.

“To me, minimalism in software development is not only a desirable design goal, but a survival strategy. I’ve seen too many projects fail, many I’ve been involved in myself, due to an explosion of features and a complex technical foundation that stands on shaky grounds, but was just too tempting to begin with,” says Aufreiter. “More than ever before it is tempting to go the quick and easy way. There’s too many shiny things out there that promise to build your product at the snap of a finger. There’s front-end and back-end frameworks of all kinds, and APIs for almost every possible problem.”

Both hint at the balancing act developers face when choosing their stack: The need to weigh future considerations with immediate needs. Tanone says that one method is to set yourself up now so that you can introduce what you might need later. 

“For example, in the context of microservices and Kubernetes, instead of onboarding your services straight to Kubernetes, you could start by containerizing the application with Docker and see how it goes,” says Tanone. “Down the line, when you need to scale, maybe then you explore Kubernetes.”

Aufreiter points to open source as providing the flexibility needed to later make these changes. “What’s important to me is that I build on top of reliable open source technology that does not lock me in. The fewer software components I need to include, the less fragile my system will become,” he says. While Aufreiter may avoid abstractions and frameworks for much of his software stack, they play an important role on the back end. This difference illustrates that a minimalist approach can involve trade-offs regarding where you’re willing to accept the complexity needed to solve your problems. This depends in part on what skills you bring to a project and which it would be best to outsource.

"While I’m a big proponent of being close to the metal in code, the reality looks different to me when it comes to hosting infrastructure,” says Aufreiter, who instead uses managed cloud infrastructure platforms.

Sometimes, lean and minimalist tools and abstractions already exist. Other times, all available solutions to your problem involve an unwanted level of complexity, and then you’re left with another choice: to build something yourself.

Configuration curation as a minimalist alternative

Two realms where we’ve recently seen myriad minimalist alternatives appear are front-end web development and back-end cloud-native infrastructure. Both Kubernetes and front-end libraries like React have been recent targets of developers looking for simpler solutions to their problems. And both realms offer examples for how open source further enables minimalism. 

When we work from the foundation of open source software, we have visibility and access where we would not with proprietary software. We can choose what parts we want and discard what we don’t. We see this happen here in two primary ways: We build alternative implementations of other open source technology, even borrowing parts of the code we’re trying to simplify, or we abstract away the complexity that may lie not in the software itself, but rather in its configuration, through packaging and curation.

On the front-end, Alpine.js, htmx, and Preact offer examples of the former. They are among the crop of minimalist open source frameworks that give developers much of the core functionality provided by larger frameworks without the complexity, taking advantage of open source standards and software, such as the web browser, HTML, and JavaScript. On the back end, projects like K3s and k0s take the latter approach, offering slimmed down distributions of Kubernetes in much the same way that lightweight Linux distributions choose only the necessary components to supplement the Linux kernel for their use cases. Let’s look a little closer at how these Kubernetes distributions offer minimal alternatives before moving to the front end.

While K3s and k0s might be one step further down the stack than the managed back ends Aufreiter uses, they reduce complexity through curation and packaging. Using Rich Hickey’s definition, one might argue that Kubernetes is actually simple, though many point to their difficulties in setting up and operating it as evidence of its complexity. 

“What makes using Kubernetes quite difficult is that there are so many different infrastructure providers and storage providers and components you need to connect,” explains Miska Kaipiainen, VP of engineering at Mirantis, the company behind k0s. “It's not so much Kubernetes itself. It's this entire stack that runs on top of Kubernetes, basically, that creates the complexity.”

Projects like K3s and k0s remove that complexity by providing developers with an opinionated, yet minimal, abstraction. Both projects do this not only by making all the choices for you, but by packaging it all up as a single binary, dependencies included; when updates are needed, you simply replace the executable with the new one.

Kaipiainen says that there are plenty of great Kubernetes distributions for people who specialize in running Kubernetes, but they wanted to make something for developers instead. 

“Some bigger Kubernetes distros include all kinds of accessories bundled into the package,” says Kaipiainen. “k0s is a stripped down, bare bones Kubernetes distribution. We let our users install everything they want on top, but nothing extra comes included.”

And while k0s was created for developers, Kaipiainen points out that it is production-grade and could actually serve as the basis for the managed back-end tools that Aufreiter relies on.

K3s creator Darren Shepherd highlights an interesting point: Abstraction and batteries-included approaches can offer the simplest answer to a problem, without sacrificing functionality or introducing unnecessary complexity.

K3s is a lightweight Kubernetes distribution that focuses on making it simple to spin up a small, three-node, production-grade Kubernetes cluster for developers. “It’s an extremely opinionated package of Kubernetes that focuses on a very simple user experience to get it up and running. It doesn't remove any functionality, it just makes a lot of choices for you,” says Shepherd, who first created the project in 2018. “The vast majority of what I do is try to figure out how to package technology in a more usable way for end users. I take best practices and what we've learned over the years and then do it all for you, so you don't have to figure it out.”

When in doubt, build it out

The early 2010s saw the rise of frameworks and libraries such as AngularJS, React, and Vue, which made it easier to make browser-side user-interface components. Over time, however, these tools became larger and more complex as they tackled more difficult problems. In recent years, alternatives have appeared to help build these interactive components with as little code and complexity as possible.

Meanwhile, projects like Alpine.js, htmx, and Preact each take a slightly different approach, there are commonalities. For example, all are extensible, which means that the core functionality can remain laser-focused, while additional features can find a home in add-ons and integrations. They are all open source and are built with and interact with open source software and standards. And they all provide some extended functionality on top of other open source software or standards without obstructing access for other technologies, which means that they are interchangeable as well as compatible with other software. 

These projects follow many of the principles outlined in minimalist thinking in computing: They do one thing well and work together (the Unix Philosophy), they enable developers to reach a “worse” product quickly (“Worse is better”), without introducing complexity, and they operate without becoming intertwined with other technology (“Simple made easy”). 

Htmx creator Carson Gross credits the technology these projects are built on top of for some of their success, noting the contrast between them and other JavaScript frameworks.

“I might encourage minimalism in web development, but only because there's this incredibly complicated piece of software called a browser that has all this infrastructure baked into it that we can sit on top of. We can take advantage of that complexity and the abstraction it provides in a simple way, rather than putting a lot of complexity on top of that,” says Gross. “A lot of JavaScript projects take a browser and heap a bunch of incredibly complicated software on top without taking advantage of the infrastructure that the browser has sitting there.”

One of the reasons front-end libraries like React are so popular is that JavaScript is one of the only ways to add interactivity to a website. Writing pure HTML, developers can only send and receive data in very limited ways and are restricted to reloading entire pages when content changes, rather than just updating smaller parts. Gross sees these as arbitrary constraints that force developers to use JavaScript when HTML should suffice. Htmx is a JavaScript library that enables developers to build interactive components in pure HTML without having to write any JavaScript.

Alpine.js and Preact target some of these same constraints in basic HTML functionality. Like htmx they aim to be lighter weight than Vue or React, but unlike htmx they still rely on the developer to write some JavaScript.

Alpine.js creator Caleb Porzio says that Alpine.js is essentially a pared down version of Vue that he built from scratch, that “came out of this belief that you should be able to make a drop-down, a modal, and tabs, easily.” 

While React and Vue create their own “virtual” document object model (DOM) to replace the browser’s DOM, Alpine.js forgoes that option and simply interacts with the browser’s native DOM. Porzio built Alpine.js to be interchangeable and compatible with other frameworks, including Vue itself, and with its latest release, Porzio switched to using Vue’s reactivity engine, which means users can easily swap between the two frameworks. 

On the other hand, Preact, a three kilobyte alternative to React, uses “the thinnest possible Virtual DOM abstraction on top of the DOM” and provides an API that is “largely compatible” with the React API. 

While Preact creator Jason Miller says that the framework’s small size helps offer a justification to limit extraneous functionality, he says it isn’t the main goal. “Preact isn't necessarily trying to be the world's smallest framework,” he says. “It's trying to be the readable, understandable, minimally-indirected substrate on which you can build all of your specific stuff for ever-changing product requirements.”

Miller’s emphasis on understandability echoes Hickey’s point in Simple made easy that understanding is key to creating reliable software, another feature Miller notes. “You can't really change the foundation of a house after you pour it and let it set, just like it's hard to change that lowest level of framework when you pick it,” he says. “The original point with Preact was to demonstrate that you don't need a huge base on which to start, you just need a solid base.” 

The ability to easily troubleshoot is an added bonus of an easily-understood codebase, a characteristic that Miller points to as a way to differentiate between complex and simple systems. 

“When something goes wrong in your code sitting on top of Preact, you're two functions away from the thing that had the error. If you're a JavaScript developer, you already have everything you need to debug it,” he says. “To achieve some amazing end result with a complex system, there is a trade-off. The system subsumes complexity into the base layer and necessitates needing your own debugger.”

Can minimalist open source move standards forward?

All of these projects try to provide as much functionality as they can with as little code and complexity as possible by staying close to the layer they augment. Miller is explicit, however, that this is partly because he would like to see this functionality become offered as part of a standard, and not just for the sake of minimalism and interoperability.

“The goal for Preact is to very slowly disappear, shift behind the scenes, and focus on an ever shrinking little area of value—as the DOM gets better, as standards change, and as developer behaviors change—to the point where it's sort of like a little polyfill for some behaviors that are decidedly not going to get added to the web, like diffing,” says Miller. “I think that would be a totally reasonable end result for Preact.”

While Gross isn’t holding out hope, he agrees that “if HTML just gave you this functionality out of the box, we could build basic web apps with a lot more interesting functionality in them as standard HTML-based web apps. It would open things up quite a bit.”

The transition of particular components from open source project to standard, after all, has precedent in both the world of browser technologies and infrastructure. For example, the language CoffeeScript was created as an alternative syntax for JavaScript and demonstrated the viability of several ideas that later found their way into JavaScript itself. Today you can expect your operating system to come bundled with a networking stack, but there was a time that you might have needed to install third-party software to support standard network protocols. It’s not that hard to imagine future browser standards incorporating more interactivity outside of JavaScript, or Linux distributions including more out-of-the-box container orchestration functionality, ultimately resulting in more standardized, simpler stacks.

About The
ReadME Project

Coding is usually seen as a solitary activity, but it’s actually the world’s largest community effort led by open source maintainers, contributors, and teams. These unsung heroes put in long hours to build software, fix issues, field questions, and manage communities.

The ReadME Project is part of GitHub’s ongoing effort to amplify the voices of the developer community. It’s an evolving space to engage with the community and explore the stories, challenges, technology, and culture that surround the world of open source.

Follow us:

Nominate a developer

Nominate inspiring developers and projects you think we should feature in The ReadME Project.

Support the community

Recognize developers working behind the scenes and help open source projects get the resources they need.

Thank you! for subscribing