Modifying the Vue prototype is "evil monkey-patching"
Kerry Boyko
Posted on April 28, 2021
If you're not familiar with 'monkey-patching', it's when you modify the prototype of an existing language feature. Like adding a new method to Array.prototype, or redefining Array.prototype.reduce because it's just slightly more efficient to use lodash's version.
It's a feature in a lot of high-level languages that use some type of inheritance, specifically Ruby, Python, and (naturally) Javascript.
But just because you can do something in Javascript doesn't mean you should. Monkey-patching can lead to namespace collisions if two developers have their own methods - and you won't really know "which runs first" until runtime in most cases. (It's also really hard to unit test, since both methods will be tested and get correct assertions in isolation.) Additionally they create a discrepancy between the original source code and observed behavior.
Most importantly for the purposes of this article, it's a change that isn't immediately visible to those who use the code after you. You think you're telling the computer to do one thing, but you're actually telling the computer to do something else. That, of course, is how you get bugs.
Let's switch gears for a moment to the Vue framework.
First, I think Vue is great. It is well designed, written, documented. I like the way it's going with Vue 3.0 and the Vue Composition API, I like Vue templates, I like .vue files, I like that it's about 1/3rd the size of React. I like a lot of things about it.
But if Paulie Walnuts held a gun to my head, and asked me what my favorite framework is, I'd say React.
And the reason is quite simple: Vue gives you a lot of ways to do the same thing, and not all of those ways are good ways. Computed vs. Watch, callbacks vs. emits, mapGetters vs. mapState vs. this.$store...
All are equally valid. None of these methods are canonically wrong or right. They all work. This is part of the reason Vue is much easier to learn and get going with quickly than React is. But you start to lose out as the program and the team starts to scale. If you give a team of 10 developers 10 different ways to code something, you will end up with 10 different ways to do the same thing in your codebase, and a lot of highly-coupled code with duplicated logic.
At Deverus, where I first started working with Vue in 2017, we hired some (good) outside contractors which put configuration information on Vue.prototype.$config, constants on Vue.prototype.$constants, and all our asynchronous API methods on Vue.prototype.$API. They were probably following one of the many blog posts which suggested this pattern, like this one from Telerik.
But this created a problem - and that problem was that now every Vue component - even presentational ones that really did no logic - now had access to every API call, had access to every configuration setting, had access to every constant, even if they weren't needed. They also had the ability to redefine them at will.
The most problematic was the this.$API
methods. If there was a problem, you'd have to search through every component to see what components were using this.$API, see where the data was being stored. More often than not, multiple calls were being made to the same API method when one would do - data was often duplicated (or worse, duplicated, then became out of sync) in many different data()
observables of components.
This wasn't wrong. It's considered a valid Vue pattern. And if you're using this pattern, you're not using Vue wrong, but for us, it led to these problems. We had to create more style rules (in our case, put all the API calls into the Vuex store, not components, and pass down the data via mapped getters,) and refactor the application so that we could create a more testable application that was easier to add features to.
So - that's a bit of a long way round to get to the point of this post - which is to say that while there's no official best practice guide that says you shouldn't modify, or "monkey-patch" the Vue prototype, I'm saying that from my experience you're probably going to have a bad time if you do.
I suppose that vue prototype modification isn't "monkey-patching" the way that modifying the prototype of, say, Array or Object is. But once you add the Vue framework to an application, it really does become so integral that the Vue prototype takes on a lot of the same importance as a core language feature. React developers expect React to behave like React every React application they work on. Vue developers... well, in an ideal world they should be able to rely on Vue, but because prototype modification is a thing, they can't always.
This can be further frustrating because monkey-patching the Vue prototype is how vue-router and vuex (and a lot of other good plugins) work -- and work well. And it is extremely easy, for example, to get your route params from this.$route.params, compared to the hoops you used to have to jump through passing your parameters to your component with React (though the useRouter hook makes it really easy now).
I'd like to think that they're exceptions. See, one of the things Vue has going for it is that because it doesn't try to stop you from doing stupid things, it allows you to do very clever things as well. And vue-router and vuex are very clever indeed.
And I'm not saying that you or your team isn't clever. But if you're using Vue in production for a mission critical app, changes are you're dealing with things that make your team less clever than a decidicated core of open-source developers making sure that they can rigourously test, type, and develop software over many iterations.
A team that doesn't have time to pay off technical debt is going to lose "cleverness". A team being pressured to add a new feature before a deadline is going to lose "cleverness." It's one of the reasons why open source purists like Richard Stallman (*1) believe that all programming should be open source - a view I don't share, but one that has enough of a point that I can't fault him for holding it.
So in the vein of "real world" programming for commercial applications (as opposed to open-source or academic purposes), monkey-patching the Vue prototype probably isn't something you want to get in the habit of.
As always, looking to have a discussion in the comments below - I'm sure my experience wasn't universal, and plenty of people have been clever with Vue in their teams.
In the meantime, if you want to take a look at some other suggestions I have for keeping large Vue projects organized, checkout my styleguide for Deverus (based on Vue 1.0) and my styleguide for the Vue Composition API back when it was in proposal form. I'll probably update and combine both and put them here on Dev.to soon.
Evil monkey image by Jason Scragz, used under CC-2.0-Attribution
Footnote:
(*1) Correction: An earlier version of this article refered to Richard Stallman as Eric Stallman, (confusing him with fellow open source evangelist, Eric S. Raymond.) I regret the error.
Posted on April 28, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.