What Is Lightweight Software? Revisiting the Definition
For many years, the Volkswagen Beetle was one of the most popular vehicles on the road. Originally weighing less than 2,000 pounds, it was among the lightest vehicles of its time. However, the Beetle was also hideously underpowered and strained to go up steep hills, laboring with more than two passengers. No one would’ve confused the Beetle with a sports car — it was often unreliable, with the performance profile of a house brick.
I mention the Beetle in relation to the current obsession with making “lightweight” software. With a quick spin through the various home pages of newer cloud native services and applications, you’ll find that nearly all of them are lightweight.
The implication, of course, is that lightweight is now a critical attribute of software. That makes sense — the best software is elegantly designed without bloat or extraneous code. However, measuring software quality merely by the number of code lines or binary size is about as fraught as assessing a vehicle’s quality by how much it weighs.
That said, lightweight can also line up with high utility, with a little package delivering other critical attributes. In contrast to the Beetle, the Suzuki Samurai is a light, cult-favorite car that was perhaps the best small off-road vehicle of the ‘80s and ‘90s.
To this day, it remains one of the best ever made — able to handle steep hills, dirt, gravel and mud. It has few complex dependencies and, most importantly, is reliable — you’re not going to get stuck on the side of the road with a Samurai. Consciously designed, it does what it is built to do very well, showing that lightweight can work well when other attributes are prioritized.
Lightweight Can Quickly Become Heavyweight
In today’s world of software, the term lightweight is overused. While many applications that call themselves lightweight may not take up a lot of space, they do a poor job in other areas. These supposedly lightweight apps are not self-contained or performant, and they add hidden technical debt that tends to emerge at the worst of times.
Curiously, this can mean you need to run more instances of them or beef them up with auxiliary components, sidecars and other modules — essentially making something that touts itself as lightweight actually quite heavyweight when the total footprint is considered.
Lightweight software can also include many dependencies to offload functionality and use third-party libraries or services. This can give supposedly lightweight software many more failure points, hidden sources of performance problems and, most importantly, significant security risks caused by exposure to many lightly policed elements of the software supply chain, such as packages or libraries.
Software modules in package managers like npm and PyPI, or in various Docker container images, are now among the fastest-growing vectors for cyberattacks because bad actors recognize the power of inserting themselves maliciously into the software supply chain.
The Five Factors of Lightweight Software
Remember the canonical Twelve-Factor App that provided a solid rubric for web application design? Well, here we’ll outline the critical factors of lightweight applications. There are five.
Lightweight software should be:
- Highly optimized
- Easy to fix
Let’s unpack them one by one and discover why each is important.
While “small” can be measured by lines of code or binary size, one snapshot in time only tells half the story. Even with a steadily growing user base, truly lightweight software tends to expand in size minimally over time. Minimal growth indicates that the maintaining team or organization has made a commitment to design for timeless utility, while being wary of adding functionality and cruft just to handle more specialized use cases. Smaller design is aesthetically ideal, for sure, but it is only table stakes. And parsimonious code counts achieved by outsourcing huge swatches of functionality to third-party libraries and packages are not small at all (see the section below on “self-contained”).
Truly lightweight software runs anywhere, from tiny Raspberry Pi to giant servers in the cloud. It can run just as easily on a Kubernetes cluster in a container as it can inside a WebAssembly sandbox or Firecracker VM. Portability matters because it indicates that the software was designed to be environment and system agnostic to do its job, wherever it happens to be deployed.
Authors of lightweight software minimize dependencies and seek to keep their applications as self-contained as possible. While this can mean fewer bells, whistles and limitations on how the application can interact with the outside world, self-containment reduces externally controlled points of failure and sources of performance or security problems. Unfortunately, self-containment is now out of favor.
Most web applications are majority third-party code with all the inherent dependencies. A culture of dependency, as well, tends to engender an attitude of outsourcing solutions to harder problems that might be better solved in your own code. While understandable, an app that’s majority third-party code is not lightweight because it increases the application’s overall footprint.
This is where the real extra work comes in to make a lightweight application stand out. The flip side of self-contained and low dependency is doing hard things that may not often be addressed in most applications. For example, memory management is difficult to execute, but it can be a powerful way to improve throughput, reduce latency and enhance scalability.
Tackling hard problems at the application layer (e.g., memory, process and thread management, and runtime engines) is increasingly rare, but teams that build timeless applications often do this work to ensure their products will continue to function well for years to come.
Easy to Fix (Smallest Possible Attack Surface)
“Surface area” is a broad term that describes not just the size of the application, but also its exposure to dependencies, outside connections, neighboring internal processes and more. This is closely related to being self-contained.
Smaller surface area also directly translates to less risk and fewer complications while making it easier and faster to scale because configurations are less complex. In particular, if an application is facing potentially uncontrolled (and hostile) environments like the internet, then keeping surface area to an absolute minimum pays enormous dividends down the road.
Why We Need More Truly Lightweight Apps
Simply put, it is much easier to build on something that’s dependable and was thoughtfully designed for the long haul. This is why many enterprises still use applications that are 20 or even 30 years old. Inertia is hard to overcome. Some of these applications are not great, while others are a joy to work on. For example, MySQL, PostgreSQL, NGINX and many of the longest-lived core applications have staying power because they are lightweight and suitable for both legacy and modern application approaches.
As we climb up the stack, we rapidly move away from that mindset, baking in more applications that may proclaim to be lightweight but are anything but. This is increasing our collective technology debt and exerting a drag on the software world.
More and more, we see DevOps and Platform Ops teams forcing problems by adding capacity when a smaller set of lightweight tools would do the job better and at less cost with less technical debt.
Following the five factors above may not be the easiest road to drive, but it is the better road — one that’s reliable and will hopefully be taken more often in future software development. We need more Suzuki Samurais, the unsung, lightweight heroes with high utility, to take us into the future, a future where past is prologue and application architectures are truly lightweight.