TLDR: After about 3 weeks of using Meteor seriously (8 hours a day), I decided that, despite its benefits, it would be shortsighted, and perhaps lethal, for my organization to use Meteor. Meteor may be right for you, but it isn’t for me. This is the article I wish I’d read before investing a month on it. Want to convince me otherwise? Please make a compelling counter case.

Update 10/22: While I haven’t reviewed the source code or tried it myself, Cluster looks to be a great package for scaling Meteor, including web sockets. Actually, it looks like a fantastic solution.

Last summer, after hearing much hype about Meteor, the self-proclaimed JavaScript app platform, I decided to give it a shot. Meteor is a vc-funded project that claims to help developers write and deploy apps across multiple platforms, using only JavaScript, faster than ever before (We haven’t heard that before, have we?). Among their touted benefits:

Reactive data — real-time updates to clients over what is basically REST for web sockets (called DDP) that is enabled by tailing the Mongo oplog and connecting it with users who have an established SockJS session.

Galaxy — their PAAS which is supposed to take the pain out of deploying Meteor apps.

Radically Less Code — “Accomplish in 10 lines what would otherwise take 1000, thanks to a reactive programming model”

Optimistic UI — Users see changes instantly, and conflicts are resolved “magically”

Share code between client and server

Write once run everywhere

Less time integrating components

It’s all open source! (MIT licensed)

At first, I did the “Hello World” stuff, and shortly thereafter built a simple multiple choice quiz app with an admin for questions and a UI for taking the tests. The spacebars template syntax was simple, and using Blaze templates directly, it was easy to add components written in other libraries (such as jQuery). In addition, some people have contributed fantastic, time-saving packages, which allow you to add features like define schemas with validations for Mongo collections, automatically define forms from those collections, and validate them client and server-side. Other packages let you define filterable data tables with just a few lines of code, or give you a complete accounts UI. I spent about two days on the project, wrote surprisingly few lines of code, and decided that it was worth giving Meteor a closer look.

A few months later, after attending local meetups, reading up on suggested materials from a former Meteor core developer, more research and a few more days of experimentation, I began working on a much more complex application. Meteor would have a lot of responsibility, as it would house my users and much of their data, provide my web app, and in effect, be the API for mobile clients who would authenticate and communicate with user services over DDP.

Now that I have some time with Meteor under my belt, I felt it appropriate to share what my development experience has been like.

Meteor’s Merits

Command line tools. You get a REPL for interacting with your server-side code that includes tab completion and code reloading, much improved over the standard Node REPL. It’s possible to do this with packages like nesh in NodeJS (and I would argue nesh is better), but it is a nice Meteor feature.

You get a REPL for interacting with your server-side code that includes tab completion and code reloading, much improved over the standard Node REPL. It’s possible to do this with packages like nesh in NodeJS (and I would argue nesh is better), but it is a nice Meteor feature. Integration between REPL and build system. The build system automatically adds the APIs you expose in Meteor packages to your REPL environment.

The build system automatically adds the APIs you expose in Meteor packages to your REPL environment. Instant, high quality web socket API. The Publish / Subscribe / Call model provided by Meteor makes it obscenely easy to create interactions between client and server side code over the web socket API enabled by DDP. You can literally just define methods via Meteor.methods on the server and call them via Meteor.call on the client. Because of live-query, you can use Meteor.subscribe to instantly connect clients with the server. Meteor then tails the Mongo op-log, figures out which SockJS clients care about those updates, and sends the updates back.

The Publish / Subscribe / Call model provided by Meteor makes it obscenely easy to create interactions between client and server side code over the web socket API enabled by DDP. You can literally just define methods via Meteor.methods on the server and call them via Meteor.call on the client. Because of live-query, you can use Meteor.subscribe to instantly connect clients with the server. Meteor then tails the Mongo op-log, figures out which SockJS clients care about those updates, and sends the updates back. Feels snappy for the end-user. Meteor assumes operations are successful and updates the UI instantly. This makes Meteor apps feel really snappy after the initial load.

Meteor assumes operations are successful and updates the UI instantly. This makes Meteor apps feel really snappy after the initial load. Most interaction with Meteor is in a synchronous style. If you’re wary of callbacks, you’ll love Meteor.

The Dark Side

I’m sure there are many more merits to Meteor than the ones I laid out, but Meteor’s current pitfalls kept me from discovering them. What follows is a very harsh, and perhaps petty smelling critique. However, given the money and effort that goes into promoting Meteor, and the incredible lack of deep, well-written prose advising the risks, I feel it’s necessary. When you have raised the millions that Meteor has, and use the type of marketing language they do to steer developers into spending their time, and staking the success of their organizations, on the tools you build, you must be held to a higher standard.

The packaging and build system. Once you truly dive into the build and package system, you’ll find that all those wonderful module patterns from Node and ES6 are completely useless to you in Meteor, because Meteor provides its own package system which interops poorly with the rest of the Node infrastructure. In each package, you define a package.js file which tells Meteor which packages your package uses, what external dependencies it needs, and which NPM modules to load. However, while you can create build plugins to modify how certain types of files are built, you can’t use any Node libraries (or any ES6) which aren’t injected into the isobuild environment, and you can’t modify how the package system itself works. I learned this the hard way when trying to add the React-Bootstrap package to Meteor, finding that it was not possible to do something like api.export(‘ReactBootstrap.*’), so that other UI components in my system could have direct access to ReactBootstrap components, without namespacing them, such as RB.Input. This would ensure ReactBootstrap components could use each other in composable ways, without me needing to patch the library just for Meteor . I spent half a day plumbing the depths of the Meteor internal build tools to add this feature and submit a pull request, and found that Meteor’s internal self-test feature was astonishingly slow, poorly written, buggy, and in addition, didn’t actually work. The PR itself was to be pretty simple — a change in the regex Meteor uses to validate export patterns, and a quick iteration over the “Foo” object in “Foo.*”. Meteor’s broken internal tests prevented me from doing this. ES6 modules? export * from ‘child_module’;

Once you truly dive into the build and package system, you’ll find that all those wonderful module patterns from Node and ES6 are completely useless to you in Meteor, because Meteor provides its own package system which interops poorly with the rest of the Node infrastructure. In each package, you define a package.js file which tells Meteor which packages your package uses, what external dependencies it needs, and which NPM modules to load. However, while you can create build plugins to modify how certain types of files are built, you can’t use any Node libraries (or any ES6) which aren’t injected into the isobuild environment, and you can’t modify how the package system itself works. I learned this the hard way when trying to add the React-Bootstrap package to Meteor, finding that it was not possible to do something like so that other UI components in my system could have direct access to ReactBootstrap components, without namespacing them, such as RB.Input. This would ensure ReactBootstrap components could use each other in composable ways, without me needing to patch the library . I spent half a day plumbing the depths of the Meteor internal build tools to add this feature and submit a pull request, and found that Meteor’s internal self-test feature was astonishingly slow, poorly written, buggy, and in addition, didn’t actually work. The PR itself was to be pretty simple — a change in the regex Meteor uses to validate export patterns, and a quick iteration over the “Foo” object in “Foo.*”. Meteor’s broken internal tests prevented me from doing this. ES6 modules? export * from ‘child_module’; Lack of control. I needed to add what I thought were some very simple features to Meteor’s authentication system. I wanted a user to select some options BEFORE performing an OAuth process, and be able to send those options from the client, validate them on the server, and then let Meteor’s normal user creation process take over. Maybe I’m spoiled by having experience with outstanding platforms like AuthO, but I found Meteor’s authentication system to be incredibly lacking — to the point that I found it hard to believe many organizations were really using it for more than trivial authentication scenarios. In order to simply pass custom fields on OAuth account creation, for example, I needed to monkey-patch the Authentication code. Soon after, I found that I couldn’t control the logic for how social accounts were merged (i.e., if a user logs in with both their Facebook and LinkedIn accounts) between providers, and I submitted a Pull Request for getting a hook into that process. Mind you, this was just a hook to alter a query from server-side code — it didn’t alter the behavior of Meteor directly at all. The only feedback I got in several days on this PR was someone complaining of security concerns on trusting whether or not a third party had sufficiently verified an OAuth account. This was just my use case for the hook — not the hook itself. Which brings me to my next points…

I needed to add what I thought were some very simple features to Meteor’s authentication system. I wanted a user to select some options BEFORE performing an OAuth process, and be able to send those options from the client, validate them on the server, and then let Meteor’s normal user creation process take over. Maybe I’m spoiled by having experience with outstanding platforms like AuthO, but I found Meteor’s authentication system to be incredibly lacking — to the point that I found it hard to believe many organizations were really using it for more than trivial authentication scenarios. In order to simply pass custom fields on OAuth account creation, for example, I needed to monkey-patch the Authentication code. Soon after, I found that I couldn’t control the logic for how social accounts were merged (i.e., if a user logs in with both their Facebook and LinkedIn accounts) between providers, and I submitted a Pull Request for getting a hook into that process. Mind you, this was just a hook to alter a query from server-side code — it didn’t alter the behavior of Meteor directly at all. The only feedback I got in several days on this PR was someone complaining of security concerns on trusting whether or not a third party had sufficiently verified an OAuth account. This was just my use case for the hook — not the hook itself. Which brings me to my next points… Dirty hacks are encouraged over developer control. There is this absurd idea that you should be able to do whatever you need to do without modifying Meteor itself. Perhaps because the Meteor project itself is slow to move, the community often resorts to dangerous or inconvenient hacks to hook into Meteor’s core logic, rather than adding extra hooks to Meteor itself.

There is this absurd idea that you should be able to do whatever you need to do without modifying Meteor itself. Perhaps because the Meteor project itself is slow to move, the community often resorts to dangerous or inconvenient hacks to hook into Meteor’s core logic, rather than adding extra hooks to Meteor itself. Community experts are in short supply. From the admittedly short exposure I’ve had to Meteor, I can assert that when you get past trivial UI focused issues in Meteor, you’re totally on your own. Because of the marketing behind Meteor, experts are in high demand — and are therefore extremely expensive.

From the admittedly short exposure I’ve had to Meteor, I can assert that when you get past trivial UI focused issues in Meteor, you’re totally on your own. Because of the marketing behind Meteor, experts are in high demand — and are therefore extremely expensive. You’re married to Mongo. Remember the discussion on all those cool real-time features? They’re totally dependent on Mongo. Mongo is your cache and persistent storage. Meteor is supposedly working on support for other databases, but those projects don’t seem to have a high priority, and the community supported options have not be adopted into the core.

Remember the discussion on all those cool real-time features? They’re totally dependent on Mongo. Mongo is your cache and persistent storage. Meteor is supposedly working on support for other databases, but those projects don’t seem to have a high priority, and the community supported options have not be adopted into the core. Your code reuse is going to suffer. You’ll find that you need to make so many little hacks and tweaks to outside packages that migrating out of Meteor will be difficult without rewriting lots of your app. In addition, you’ll find tying into other systems in your infrastructure and limiting the scope of what Meteor does frustratingly difficult.

You’ll find that you need to make so many little hacks and tweaks to outside packages that migrating out of Meteor will be difficult without rewriting lots of your app. In addition, you’ll find tying into other systems in your infrastructure and limiting the scope of what Meteor does frustratingly difficult. Bureaucratic and slow moving. The project is more like a big company or university than a startup or scrappy, fresh project. Your issues will be ignored, your pull requests won’t get merged, and in general, if you spend any amount of time digging into Meteor’s guts, you’ll find it very difficult to talk to a human who can answer serious questions. Granted — I’m impatient — but won’t you be impatient when servers are crashing, or millions of users are experiencing a bug — and you need to talk to the person who wrote the deep framework code to solve your issue?

The project is more like a big company or university than a startup or scrappy, fresh project. Your issues will be ignored, your pull requests won’t get merged, and in general, if you spend amount of time digging into Meteor’s guts, you’ll find it very difficult to talk to a human who can answer serious questions. Granted — I’m impatient — but won’t you be impatient when servers are crashing, or millions of users are experiencing a bug — and you need to talk to the person who wrote the deep framework code to solve your issue? They’re solving their problems, not yours. When you control your code, I mean really control your code, you own your destiny. With Meteor, you’re adopting blanket decisions that are focused on making developers doing trivial things more productive, and making deployments to Meteor’s own hosting infrastructure simple. They want you to be reliant on them completely. That’s their business model. The problems that you need to solve are obfuscated behind community support, sometimes poorly written and poorly tested code, and a code base that changes slowly. Think about this — do you really want to stake the future of your company on a persistent web socket connection between clients and servers scaling in production? Do you really want to limit your productivity and capabilities to what the Meteor core team has decided to support?

When you control your code, I mean really control your code, you own your destiny. With Meteor, you’re adopting blanket decisions that are focused on making developers doing trivial things more productive, and making deployments to Meteor’s hosting infrastructure simple. They want you to be reliant on them completely. That’s their business model. The problems that need to solve are obfuscated behind community support, sometimes poorly written and poorly tested code, and a code base that changes slowly. — do you really want to stake the future of your company on a persistent web socket connection between clients and servers scaling in production? Do you really want to limit your productivity and capabilities to what the Meteor core team has decided to support? The mobile-ready argument is hype. I challenge you to find a single mobile application written in Meteor that feels like a native app. Meteor is dependent on Cordova, it just wraps JavaScript and HTML in a WebView. I downloaded and installed every single Meteor app I could find, and couldn’t find any which felt remotely polished. It may work for internal apps, but you’re not going to create the next great app with Meteor. React Native? I’m much more excited about that.

I challenge you to find a single mobile application written in Meteor that feels like a native app. Meteor is dependent on Cordova, it just wraps JavaScript and HTML in a WebView. I downloaded and installed every single Meteor app I could find, and couldn’t find any which felt remotely polished. It may work for internal apps, but you’re not going to create the next great app with Meteor. React Native? I’m much more excited about that. Builds and tests are slow. If you’re used to prototyping user interfaces with live-server and seeing your changes instantly, Meteor is going to drive you insane. It takes 5–10 seconds on my very snappy machine (16gb ram + ssd) to rebuild a package on change.

If you’re used to prototyping user interfaces with live-server and seeing your changes instantly, Meteor is going to drive you insane. It takes 5–10 seconds on my very snappy machine (16gb ram + ssd) to rebuild a package on change. Most interaction with Meteor is in a synchronous style. If you just want JavaScript to be JavaScript, and want to make your own decisions on when to use a callback, when to use a promise, and when to use a future, you’re sort of hosed. Meteor is very opinionated about asynchronous code. You’ll notice this was listed as both a merit and a disadvantage of Meteor.

Alternatives

Update 10/22: Some commenters have noted that the alternatives listed aren’t great. True. What Meteor does is complicated — it provides a lot — and building things like oplog tailing, scaling socket servers across clusters, are non-trivial things which Meteor does well.

I’ll be the first to admit that there are some excellent ideas coming out of Meteor. However, the truth is that you don’t need Meteor to use the ideas. Here is a comprehensive list of alternatives by Ilya Petrov. I’ve already told you that Nesh gives you an awesome REPL experience. Meteor’s sockets are just implemented on top of SockJS, and oplog tailing is, well, just tailing with some filtering (here’s an implementation in a few hundred lines of code), and logic on which clients should receive notifications over SockJS, and what the UI should do when it receives those updates. SystemJS gives you a truly outstanding outstanding build tool. React gives you an outstanding, component driven view system. VertX is designed to give you full-stack reactive support — and works with Java, JavaScript, Ruby, and Groovy.

If you value control over your codebase and your own ability to make decisions about what is right for your infrastructure and what isn’t, you should think carefully, and fully understand all design considerations, before selecting Meteor. I believe that long term, if you pick Meteor because it’s hot, and not because you understand it, your costs will be higher, your development team will be limited by Meteor’s design decisions. When you face deep issues, you’ll be on your own in a vast, tightly-coupled codebase.