Welcome to a new series on architecting your next web application! I feel obligated to let you know right off the bat, if you're reading this hoping for a clear-cut, definitive, one-size-fits-all answer, you aren't going to get it. You'll come to this conclusion as we spend some time covering different parts of web application architecture, but there is no magic hammer. Every decision comes along with tradeoffs, but learning to manage those tradeoffs is an essential part of improving as a developer.
That's where this series comes in. In it, we're going to go in-depth about some of the most important decisions you will make when starting to build your next web application. To start things off, we're talking about what a lot of the internet has dubbed "the almighty monolith."
What is a monolith?
Monolithic applications have been around since practically the beginning of the web. Throughout that time, they've gone by plenty of different names: server-based applications, traditional web applications, multi-page applications; the list goes on and on. In their purest form, however, monolithic applications are typically ones where their entire architecture (i.e., front-end and back-end code) lives in the same codebase and is entirely served from a web server. It's important to note that a monolithic app is still completely capable of calling out to external APIs for added functionality; the important detail here is that every time a user wants to request that the application DO something, the server handles that request.
Benefits of monolithic proportions
Even though monolithic web apps have been around for a long time, they persist as a strong backbone for many companies and software platforms. Just because they might be considered "old tech" by some doesn't mean that they aren't a viable application architecture--it's much the opposite, in fact.
One of the most convincing reasons to build a monolithic web app is the sheer speed of development. If you need to get your application up and running as soon as possible, creating a monolith is an option worth exploring. Since monolithic codebases include the entire application's code in one place, you'll spend less time figuring out how to communicate between services in a fast and secure way, and more time figuring out how to perfect your specific business logic.
SEO optimization (maybe?)
There was a point in time where monolithic (server-rendered) apps were the only option for any web-based application that needed to be optimized for search engines. This isn't necessarily the case anymore since Google's web-crawlers are getting better by the day. However, it's not an exact science, and especially if you're looking to show up in the rankings of other, non-Google search engines, having your application render on the server is a significant benefit.
As a reminder, in a monolith, all requests are handled by the server. This can be a huge benefit or a considerable drawback depending on a project's requirements, but in the case of security, it is practically always a benefit. When all requests are handled by the server, the application's security is managed via sessions, which typically take the form of signed cookies.
Authentication with signed cookies is a significant part of building a secure web application. Cookies on their own don't have much security baked (ha, puns) in, but signing a cookie solves the security issue. When a cookie is signed, the server can quickly check to see if the cookie was modified in any way during transit, which can prevent all sorts of malicious activity from occurring in your web app.
Of course, one could argue that a JWT could also take the form of a signed cookie though, so what's the difference?
Session authentication is baked right into every browser and works out of the box, unlike JWTs. Also, I should stop right here and say that I'm not advocating that JWTs should be done away with entirely--there are fantastic uses for them, but we'll get to that in another post.
I've touched on this across this entire post, but I feel like it deserves its own section. Monolithic applications tend to lean heavily on functionality that is already built into most browsers. Aside from the session cookies that most monolithic frameworks use for their authentication that I mentioned above, the example that springs to mind is routing. Routing in a monolithic web application is as simple as changing the URL in the browser and running a request. There's no magical hand-waving or complex logic that determines where to go; you simply give the browser the URL you want to navigate to, and the browser brings you there.
How about the downsides?
It can be a challenging task to build a reactive front-end experience for users with a monolithic application. In an era where everything is an app, most users are expecting a seamless experience, and there are times when a full page reload would disrupt the experience. If your app NEEDS to avoid full page reloads at all costs, then you may want to steer clear of monolithic applications.
However, this has a caveat. There are libraries and packages out there that make building reactive front-end user interfaces quick and easy, even inside the bounds of a monolithic application. Packages like Livewire (which I will definitely be covering in a later post), Turbolinks, and Phoenix Liveview all have slightly different ways of keeping a monolithic app's interface reactive to what's going on in the system.
That said, just like the SEO benefits section, the days of sacrificing a smooth front-end user interface for all of the other benefits of a monolithic application may soon be behind us.
Scaling requires a little more thought
As soon as a monolithic application needs more oomph than a single server can give, things get a little more complicated. Granted, if you know how to solve these types of problems, the complexity fades away, but for a first-time monolithic app creator, quickly scaling up can be a daunting task. As always, with monolithic apps, vertical scaling (that is, making the server bigger and more powerful) is a good option, but there comes the point where that just isn't an option anymore. Once that happens, some coordinated steps (and possibly a few code changes) need to take place so the app can stay running. Contrast that with a more distributed system--if any part of the system starts getting overwhelmed, creating another server with that part of the system installed on it can quickly alleviate the problem.
I'll be going into more detail about the things to think about when horizontally scaling a monolith in a future post, so stay tuned!
More work per request
Going right along with scaling requiring a little more thought, monolithic apps, by their very nature, put more strain on the server per request. Mostly, this extra strain comes from having to render an HTML view AND running the business logic. This directly contrasts with a more distributed system, since in those situations, the server is only responsible for running the business logic, not for rendering the user interface.
Adding mobile apps (or other front-end clients) gets complicated
Depending on how a developer writes their monolith's code, it can be hard to reuse it if a mobile app or third-party software needs to talk to the same back-end logic. These other applications usually don't want their API endpoints sending back rendered HTML; they want some other standard format like XML or JSON. That means a lot of the existing endpoints will need to be rewritten (risking regression errors) or duplicated to an API-specific endpoint (risking duplication errors). As with most things, there are ways around this, but in the end, it will almost always require more developer time to make it compatible.
How to build a monolith
Since monolithic web apps come in many shapes and sizes, there isn't one specific way to build a monolith. However, since monoliths have been around for a long time, a wide variety of frameworks exist to give you a jump-start. Here are a few of my personal favorites (in no particular order):
- Laravel (PHP) - Laravel is a leading open-source framework for building monolithic applications in PHP (although it can be used very successfully as purely an API to feed other software as well). It has a ton of functionality built-in, with a lot of other libraries and packages provided by the Laravel team or the community that brings even more functionality to the framework. On a personal note, this is my framework of choice.
- Django (Python) - Django is, like Laravel, a leading open-source framework for building full-stack web apps, but it uses Python as its language of choice. Django fully embraces the Python style of programming by focusing on simple, easy-to-read code that packs in a lot of functionality. Personally, this is the framework that I have used the least, but some of the best programmers I know swear by it.
- Ruby on Rails (Ruby) - Rails is probably the most famous out of the bunch. It's prized by startups and large companies alike for its exceptionally developer-friendly language and style, which follows the vision of its language of choice: Ruby. Rails, like Laravel and Django, allows for rapid development of the entire stack and includes a bunch of features for building full-stack web apps right out of the box.
- Phoenix (Elixir) - If functional programming is more your speed, but you still want a full framework to help you quickly build out a monolithic web app, Phoenix is a fantastic option. Phoenix promptly rose to popularity with the addition of Liveview, a websockets-based library to help build reactive front-ends within the bounds of a server-rendered app, and is an exciting contender in the monolith space!
Monoliths are an excellent way to build web applications. They're fast, secure, and easy to build, and with most full-stack frameworks taking care of a lot of the nitty-gritty details for you, you can spend more time working on perfecting your business logic instead of perfecting the app's underpinnings.
It's important to note that monoliths aren't a golden hammer--there are a lot of situations where they fall short. For example, if you are going to need to widely distribute the system or if other applications will have to interface with your app, monoliths may end up making the process a bit more difficult in the long-run.
If this all seems a little overwhelming for you or for your app idea, don't fret! One of the most critical parts of architecting an app is that, at some point, it doesn't really matter what you use to build it. As long as you can get your product to market and into the hands of users, you can always optimize and change things later on. Don't be paralyzed by the choices, especially when you're starting an application. If you begin to feel like you're stuck on this decision, my advice is to pick something you're comfortable with and go that way. Remember, one essential skill of being a developer is learning to balance the tradeoffs that you introduce into your system. If not having the "perfect" stack is causing you to stall out and not write your app, then maybe that's a tradeoff that should be taken more into consideration!
But what do you think? Are you a fan of the almighty monolith? Maybe you wouldn't touch a monolithic codebase with a 40-foot pole? Hit me up on Twitter and let me know what you think!
- An explanation of Monolithic applications
- Information about JWT
- Phoenix Liveview
- Laravel: a framework built on PHP
- Django: a framework built on Python
- Ruby on Rails: a framework built on, well, Ruby!
- Phoenix: a framework built on Elixir
- Twitter (as always, hit me up with questions, comments, or conversations!)