Meteor framework review
Over the last few years, I've worked extensively with the Meteor web framework.
After developing and maintaining a production Meteor application for several years, here's my take on it:
The Good
Get started quickly
Meteor is a really quick framework to get started with. Normally in JavaScript land, you can easily end up spending hours upon hours setting up and configuring webpack and babel and this build plugin and that transpiling plugin and what not, before you're able to write even your first line of code. The JavaScript ecosystem today is a hot mess, and Meteor is like a breath of fresh air in comparison. All you need to do is
curl https://install.meteor.com/ | sh
to install Meteor, and then:
meteor create simple-todos
And you have a Meteor project up and running!
Powerful functionality included OOTB
Once you have a Meteor project created, you will be able to write ES6+ code, have an application that can run on the latest web browsers, on older web browsers without ES6 support, and on a Cordova mobile app - all from a single codebase. It's a magical experience! You can also use dynamic imports to load your particularly heavy bits of code that are maybe used only on some specific pages, on those pages only. And Meteor will cache dynamically imported packages based on their hash so that you only need to load them once per browser until you update the package.
Custom Package Ecosystem
If you want to add more functionality to your application, Meteor has a package ecosystem at AtmosphereJS that you can use to install packages and extensions that have been built specifically for Meteor. And you can also use any npm package, or even mount an entire Express server within your Meteor application, and make use of all Experss plugins like PassportJS etc.!
For example, to use Sass, just
meteor add fourseven:scss
And you're good to go!
Or if you want to a login UI, just
meteor add accounts-ui
Or to add a client-side router along with server-side rendering, just do
ostrio:flow-router-extra
staringatlights:fast-render
Meteor has a powerful package ecosystem and a lot of functionality that modern JavaScript based web apps require hours of configuration and figuring out which out of a gazillion different plugins you want to use to accomplish, Meteor gives it to you almost for free and you don't need to think twice about it!
Free Realtime and RPC
Meteor is realtime by-default, and their realtime is pretty good. They will take care of failures, automatic retries, handling connection failures, and a whole lot more. You also get RPC in the form of Meteor Methods for free with the framework. Plus, all your RPC code runs on the client first and then the server, which provides some useful features:
- You detect errors without needing to run code on the server
- You can get an Optimistic UI - your UI will update without needing to wait for the response from the server because it can simulate the response from the server
- You have less code duplication as the same code can run on both the client and the server
Backwards Compatibility
Meteor has done an amazing job of maintaining backwards compatibility with new releases. It is very likely that if you run a Meteor application from 4 years ago on the latest version of Meteor, it will just work™️.
The Bad
The Meteor framework has a lot of good stuff going for it, but for me, the bad ultimately has outweighed the good. Here's why:
Slow - huge package sizes, constant optimization needed
With Meteor, an empty app is still several hundreds of kilobytes in size, and as you add more code and more packages, apps can easily reach into the megabytes. One of the reasons for this is that is an SPA, all the code for your entire application is loaded in one go. This also leads to long download times and slow application bootup, especially so on mobile. While this can be mitigated somewhat using dynamic imports, it's not very simple to do so and requires a lot of re-architecturing of your application.
Problems scaling - no best practices
When you begin needing to scale a Meteor application, you are faced with several challenges.
- Due to the framework being relatively new, there's not a lot of literature on scaling it - you're on your own, more or less
- Because of the by-default realtime functionality, Meteor maintains a copy of all the data that each client has subscribed to in the servers memory - this leads to high memory and CPU usage. You will need to move to horizontal scaling much earlier than with other tech stacks, and handling concurrent user loads is very challenging.
- Meteor encourages the use of publications to load data - this is realtime. However, the community has generally adopted using RPC (Methods) instead, as this is lighter on the resources. However, the official guides and tutorials all mention publications as the way to go.
- Due to the reasons mentioned above, Meteor is also a resource hog, and therefore is quite expensive to scale.
There are solutions coming out like redis-oplog that mitigate some of these issues, but scaling Meteor is still a much bigger engineering challenge than scaling, say, Rails or Express or SocketCluster.
Shrinking ecosystem
Lately, and for a long time now, the Meteor ecosystem has been shrinking instead of growing. Once popular packages are being abandoned left and right as their maintainers move on from Meteor. Kadira APM (Application Performance Monitoring), The Meteor Chef, Restivus are just some of the examples of once insanely popular pieces of the Meteor ecosystem that have now been abandoned.
I also want to mention here that VeliovGroup and dr-dimitru have been taking the baton from a lot of the abandoned projects and doing an amazing job of not just maintaining them but also improving them and adding new functionality!
As for the MDG (Meteor Development Group), it feels like their attention has moved more towards focusing on their flavour of GraphQL called Apollo, and Meteor is getting less attention by the core development team as a result.
You'll also notice that most of the common Meteor packages have barely any stars on GitHub, which is somewhat indicative of their popularity in the wider developer community and kind of signals that if the current lead developer were to step down, they might not necessarily have someone to pass the baton to.
While the added support for npm packages does mitigate this somewhat, npm packages don't always work with Meteor.
Huge resource usage
As mentioned above in the scaling section, Meteor has high resource usage. This is not just a problem in production, but also during development. On my machine, it takes almost two minutes for my largest Meteor application to boot up from a cold start, and it can take a good 30 seconds or more for the application to reload when I change some code. Compared to the 3-4 seconds that I experience with Django, Flask, Rails, or most other web frameworks, this leads to a lot of frustration and slows down development, running somewhat counter to the Meteor promise of increased developer productivity. There have been huge strides in this area especially when talking about reload speeds during development, but it's still not good enough for me.
Lock-in to MongoDB
I am not a fan of MongoDB. It has caused me so much grief I want to cry. Most applications do not stand to benefit from the promises of NoSQL databases, and most applications actually have data that is relational in nature. Users own content, that content has certain other content that belongs to it, all this content is part of a collection, and so on. SQL databases are built around the concept of relationships, hence why they're called relational databases, and NoSQL databases are absolutely not the right tool for that job in almost every case. Also, most SQL databases provide a JSON column type these days. When working with relational data in MongoDB, you can either fire of a million queries to fetch related data when using foreign keys, and deal with the performance implications that brings; or you can denormalize your data and then worry about keeping the data in sync, and suffer from increased disk usage and loads of duplication.
Either case is sub-optimal and most of the times, MongoDB is a fad more than something that's actually required, and will cause more pain than it will help.
Don't get me wrong, NoSQL databases have their place - it's just that they're often used where perhaps an SQL database is a better fit.
Common web app tasks are not too easy
Common requirements like job queues, REST APIs, search engines, all common requirements in a lot of web applications, are either not that easy to implement with Meteor, or there are plugins that are not actively maintained. Let me give an example:
- REST APIs - Restivus, the de-facto REST API plugin for Meteor, hasn't been updated since Jan 2017!!!.
- Job Queues - SteveJobs, the current most popular job queue for Meteor, while it sees frequent updates, there is still a lot of functionality that is standard with job queues that is not implemented with SteveJobs. Semaphores / job locking being a critical example. I used SteveJobs in an app, and as I scaled the app horizontally, my scheduled jobs started getting executed multiple times. I had a implement a distributed lock manually using Redis.
- Search engines - This is a pretty common functionality that a lot of web apps require. Django has Haystack, Rails has Searchkick, Laravel has Scout, and Meteor has..., nothing.
- File uploads - While there are a ton of file upload plugins for Meteor, I found it easier to just create a microservice to handle the task, as each of the plugins has some quirks and lacks one thing or another, and I faced performance issues with most of them.
Not every web app needs real-time
And perhaps this is one of the most important points here. The by-default realtime functionality makes it too easy to shoot yourself in the foot. I've seen a lot of movement towards dynamic SPA React apps with realtime everything, but most of the time this is overkill and overengineering. Facebook is not an SPA, for example. Most of the time, what makes sense is for parts of your page to have realtime functionality, like, say, a chat widget, or a comment box, or a view counter. But your entire app? Does an article really need to be realtime?
And even when it comes to functionality that absolutely is realtime, Meteor does not excel at it. I built a chat system using Meteor, and it barely handled more than a 100 concurrent users. And the optimistic UI caused messages to jump around. When the user load increased, messages started taking 10s of seconds of round-trip time. I tried redis-oplog and it barely helped.
After moving that component to SocketCluster, I saw massive gains in performance, increased reliability, and I could scale to thousands of concurrent users off a single 1GB DigitalOcean instance.
When I first started using Meteor, I was attracted to it due to its shiny glittering coat. And I've learned a lot of lessons the hard way because of that. Does this mean that I hate Meteor? No, not at all. It has a lot of promise, but it also needs to mature a lot before it's able to compete with the likes of Django, Rails, or Laravel. For now, I will not be starting any new projects in Meteor, unless its a side project that I do not intend to get into production with 1000+ users, because, frankly, who wants to spend 17 hours and 49 minutes configuring webpack?