Why We’re Sticking With Ruby on Rails at GitLab – The New Stack
When David Heinemeier Hansson created Ruby on Rails (interview), he was guided by his experience with PHP and Java. On the one hand, he didn’t like how the verbosity and rigidity of Java made Java web frameworks complex and difficult to use, but he liked their structural integrity. On the other hand, he liked the initial affordability of PHP but was less fond of the quagmires such projects tended to get into.
|Java||Easy to use, well structured|
It seems like these are exclusive choices: either you become approachable and messy, or well-structured and difficult to use, choose your poison. We used to make a similar, and equally difficult, distinction between server-class operating systems such as Unix, which were stable but difficult to use, and client operating systems such as Windows and MacOS. which were accessible but crashed a lot.
Everyone accepted this dichotomy as given by God until NeXT put a beautiful, accessible and fluid GUI on a solid Unix foundation. These days, “server-class” Unix not only runs beautiful GUI desktops, but also most phones and smartwatches.
So it turned out that accessibility and crashes were actually only related by historical accident, and the same goes for accessibility and clutter in web frameworks: they are independent axes.
|Difficult to use||Approachable|
And these independent axes opened up a very desirable open space in the lower right corner: an accessible and well-structured web framework.
With its strong, metaprogrammable Smalltalk heritage and good Unix integration, Ruby proved to be the ideal vehicle for the creator of Ruby DHH to fill that desirable lower right corner of the table with Rails: an extremely accessible, productive and well structure.
|Difficult to use||Approachable|
|Well-structured||Java||Rubies on rails|
When GitLab co-founder Dmitriy Zaporozhets decided he wanted to work on software to run his (and your) version control server, he also came from a PHP background. But instead of sticking to the familiar, he chose Rails.
Dmitry’s choice may have been prescient or fortuitous, but he served GitLab extremely well, in part because David was able to achieve his goals for Rails: accessibility with a good architecture.
Sid is Co-Founder, CEO, and Chairman of the Board of GitLab, Inc., the DevOps platform. Sid spent four years building recreational submarines for U-Boat Worx and while at the Dutch Ministry of Justice and Security (Ministerie van Justitie en Veiligheid) he worked on the Legis project , which has developed several innovative web applications to help legislate. He first saw Ruby code in 2007 and liked it so much he learned to program. In 2012, as a Ruby programmer, he met GitLab and discovered his passion for open source. Soon after, Sid brought GitLab to market, which now has over 30 million registered users, from startups to global enterprises.
In the previous section it was assumed that modularity is a desirable property, but as we have also seen it is dangerous to just assume things. So why, and in what contexts, is modularity really desirable?
In his 1971 article “On the criteria to be used to decompose the systems into modules” David L. Parnas gave the following (desired) advantages of a modular system:
- Development time should “be shortened as separate groups would work on each module with little need for communication”.
- It should be possible to make “drastic changes or improvements to one module without changing the others”.
- It should be possible to study the system one module at a time.
The importance of reducing the need for communication was further emphasized by Fred Brooks in “The month of the mythical man“, with the added communication overhead one of the main reasons for the old adage that “adding people to a software project late makes it later”.
We don’t need microservices
Modularity has generally been as elusive as it is highly sought after, with the default architecture for most systems being the big ball of mud. It is therefore understandable that the designers were inspired by arguably the largest existing software system: the World Wide Web, which is modular by necessity; it cannot function otherwise.
Organizing your on-premises software systems using separate processes, microservices combined using a REST architectural style, helps to enforce module limits through the operating system, but at significant cost. This is a very cumbersome approach to achieving modularity.
The difficulties and costs of running what is now a freely distributed system are significant, with some of the performance and reliability issues documented in the famous distributed computing errors. In short, performance and reliability costs are significant because function calls that take nanoseconds and never fail are replaced by network operations that are three to six orders of magnitude slower and fail. Failures become much more difficult to diagnose if they must be traced across multiple services with little tooling support.
You need a fairly sophisticated DevOps organization to successfully run microservices. It doesn’t really make a difference if you’re working at a scale that requires that sophistication anyway, but it’s very likely that you are not google.
But even if you think you can handle it all, it’s important to note that all of this accidental complexity adds to the original essential complexity of your problem; microservices do nothing to reduce complexity. And even hoped for modularity improvements are not guaranteed at all, usually what happens instead is you get a mud ball distributed.
By making a good architecture accessible and productive, Rails allowed GitLab to develop a modular monolith. A modular monolith is the exact opposite of a distributed ball of mud: a well-structured, well-architected and highly modular program that runs as a single process and is also boring as possible.
Although structuring GitLab into a monolith has been extremely beneficial to us, we are not dogmatic about this structure. Architecture follows needs, not the other way around. And while Rails is a great technology for our purposes, it has a few downsides, one of them being performance. Fortunately, only a tiny fraction of most codebases is actually performance critical. We use ours Gitaly daemon written in Go to handle actual git operations and PostgreSQL for non-repository persistence.
Finally, our modular monolith rotates our open core business model to be just a beautiful theory in a practice reality. While Rails doesn’t accomplish this on its own – it would be our wonderful contributors and engineers – it does lay the right foundation.
To harvest the true advantages of open source, the source code made available must be accessible to contributors. To maintain architectural integrity against contributions from a wide variety of sources, and to keep a clear line of demarcation between open and closed components, the code must be very well structured. Sound familiar?
Wouldn’t it be better to have a proper plugin interface? Or better yet, a service interface modeled on microservices? In a word: no. Not only do these approaches impose deployment and integration hurdles that go well beyond “I made a small change to the source code,” but they often enforce overly rigid architectural constraints. Anticipating all future expansion points is a fool’s errand, which we thankfully didn’t get into and don’t have to.
With our boring modular monolith, users and other third-party developers can contribute and make enhancements to the core product, giving us tremendous leverage, coupled with an unbeatable pace and scalability of innovation.