What Music Can Teach Us About The Way We Share Code
The way we share and consume music has undergone a dramatic transformation in recent years. Gone are the days of amassing piles of physical media like CD-ROMs in hopes of cataloging our favorite artists and albums. Instead, we‘re now awash in a sea of digital content, dynamically accessed through services like iTunes and Spotify.
This seismic shift didn‘t occur in a vacuum; it was a direct response to the limitations of the old model. Consider the drawbacks of the once-mighty CD:
- Inconvenience: CDs were cumbersome to store, organize and transport
- Inflexibility: Creating custom mixes was a painstaking process
- Stagnation: Updating collections with the latest tracks was a chore
- Bloat: Discs were padded with filler just to access the hits
- Poor Discoverability: Remembering which albums contained which songs was a constant challenge
In many ways, the state of code sharing today resembles this bygone era of music consumption. Despite the promise of component-based architectures and modular design, achieving effective code reuse at scale remains an elusive goal.
The Modular Revolution
The concept of building applications as collections of discrete, reusable components has been the north star guiding developers for decades. From the early days of object-oriented programming to the rise of Service-Oriented Architecture (SOA), we‘ve long sought to abstract the complexities of software into neatly encapsulated modules.
In the realm of web development, this modular revolution has reached new heights with the widespread adoption of component-based frameworks like React, Angular, and Vue. By decomposing interfaces into self-contained, reusable building blocks, these tools have made it easier than ever to create rich, interactive experiences.
Framework | GitHub Stars | npm Downloads (Monthly) |
---|---|---|
React | 160k+ | 31M+ |
Angular | 71k+ | 3M+ |
Vue | 175k+ | 5M+ |
Source: GitHub and npm (as of May 2023)
On the backend, we‘ve seen a similar shift toward modularity with the rise of microservices. By breaking monolithic applications down into loosely coupled services that communicate over well-defined APIs, teams can develop, deploy and scale functionalities independently.
More recently, the serverless movement has taken this concept even further by enabling developers to build applications as collections of granular functions that abstract away infrastructure concerns. With platforms like AWS Lambda and Google Cloud Functions, the notion of a "unit" of code has been reduced to a single function invocation.
The Difficulty of Sharing
Despite these advancements, efficiently sharing code across projects remains a significant challenge for most organizations. The typical approaches – package repositories, monorepos, shared libraries – each come with substantial drawbacks.
Package Proliferation
In theory, publishing reusable modules as packages (e.g., to npm or Maven Central) should facilitate easy sharing. In practice, it often leads to a sprawl of single-purpose libraries with complex interdependencies and versioning headaches.
A study by Rafa Paiva of Ohm Studio found that a typical web application pulls in over 1000 transitive dependencies from npm. This amounts to around 75% new code in their node_modules directory every week [1].
Creating and maintaining all these packages involves substantial overhead. Each one needs its own repository, build configuration, and release process. Updates become a painful exercise in coordinating across disparate codebases. It‘s not uncommon for teams to waste days or even weeks on the ceremony of extracting, publishing, and reintegrating shared components.
Monolithic Monorepos
Some organizations, particularly large tech companies like Google and Facebook, have embraced monorepos as an alternative to package proliferation. Rather than splitting shared code into separate repositories, they maintain a single massive codebase that contains all their projects and libraries.
While monorepos can streamline certain aspects of code sharing, they come with their own set of tradeoffs. For one, they require significant tooling and infrastructure to scale – Google‘s monorepo contains over 2 billion lines of code [2].
Monorepos also tend to impose tight coupling between projects, as changes can easily introduce unintended consequences. This can hinder autonomy, as teams must carefully coordinate their efforts. Atomic commits become challenging, as unrelated changes frequently end up bundled together.
Tools like Lerna attempt to make monorepos more manageable by providing utilities for managing multi-package repositories. However, they still involve substantial complexity and require buy-in across the organization.
Bloated Shared Libraries
Another common approach is to extract reusable code into shared libraries that are bundled into each project. While this avoids some of the overhead of package management, it introduces a different set of problems.
As shared libraries grow in scope, they tend to accumulate baggage in the form of rarely used or overly specific functionality. This bloat slows down build times and increases the surface area for bugs.
Worse, because shared libraries are typically versioned and released as a single unit, they can become a bottleneck for the teams that depend on them. Making a change to a single component requires a full release cycle of the entire library. This can discourage frequent iteration and lead to stagnation.
Moreover, discovering what functionality is available in a shared library can be difficult. Without good documentation and tooling, developers often resort to spelunking through the source code or relying on tribal knowledge.
An iTunes-like Revolution
What we need is a way to make code sharing as frictionless and dynamic as the way we now consume music. We need the equivalent of an iTunes for components – a platform that enables developers to easily discover, consume, and remix modules on-demand.
This is the vision behind Bit, an open-source project that aims to revolutionize the way teams collaborate on building software. Bit‘s core insight is that effective code sharing requires decoupling the representation of components from their physical location in the file system.
Instead of forcing developers to choose between package proliferation and monolithic codebases, Bit enables a more fluid approach. It allows teams to share components directly from their existing repositories, without the overhead of extracting them into separate projects.
Here‘s how it works:
- Track: Developers use Bit to mark the components they want to share, directly in their source code.
- Isolate: Bit automatically isolates each component and its dependencies, creating a standalone version that can be published and consumed from any codebase.
- Share: Components are then made available through a remote "scope", which acts as a virtual component marketplace. From here, they can be easily discovered and installed by other developers using familiar tools like npm or Yarn.
- Sync: As components are consumed and modified across different projects, Bit tracks the changes and syncs them back to the original source. This enables a networked model of collaboration, where improvements and bug fixes can flow freely between codebases.
Bit‘s distributed scopes architecture enables fluid code sharing
In practice, this approach offers several key benefits:
- Discoverability: Developers can easily explore and learn about available components through Bit‘s rich web interface, which includes live examples, automated docs, and test results.
- Reusability: Consuming shared code is as simple as installing a package, but with the added flexibility of being able to import the raw source and make changes as needed.
- Maintainability: Because components are synced bi-directionally, there‘s always a single source of truth. Changes can be made in any consuming codebase and propagated back to the original, without the need for a separate contribution process.
- Autonomy: Teams can update and iterate on components at their own pace, without being tightly coupled to a central library or monorepo.
- Scalability: As the number of shared components grows, Bit‘s decentralized model ensures that complexity remains manageable. Each team can curate its own set of scopes, without the need for global coordination.
"The key to building large apps is never build large apps. Break your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application"
– Justin Meyer, author of JavaScriptMVC
By making it easy to share and reuse small, focused components across projects, Bit enables a more modular and composable approach to building software. Just as iTunes made it possible to create dynamic playlists that span multiple albums and artists, Bit makes it possible to assemble applications from a diverse catalog of interoperable building blocks.
Conclusion
The way we‘ve historically shared code, based on the metaphor of static libraries, is fundamentally at odds with the dynamic and collaborative nature of modern software development. We‘ve reached a point where the overhead of traditional approaches, like package management and monorepos, is starting to exceed their benefits.
To truly unlock the potential of component-based architectures and modular design, we need a new model for code sharing – one that embraces the lessons of the music industry‘s transition from physical media to digital streaming.
Bit points the way forward by providing a fluid and frictionless platform for publishing, discovering, and remixing components across repositories and teams. By decoupling the representation of shared code from its physical location, it enables a more dynamic and networked model of collaboration.
Just as iTunes revolutionized the way we consume music by making it easy to access individual songs on-demand, Bit has the potential to revolutionize the way we build software by making it easy to share and reuse granular pieces of functionality.
As more teams adopt this approach, we may be on the cusp of a new era of software development – one where applications are assembled from vast catalogs of plug-and-play components, and where innovation is accelerated through the free flow of ideas and improvements across organizational boundaries.
In a sense, we‘re moving from a world of monolithic symphonies to one of remixable tracks. And that‘s music to every developer‘s ears.