Making Monorepos Breakproof With Anton Stoychev - JSJ 694

## Metadata
- Author: [[JavaScript Jabber]]
- Full Title: Making Monorepos Breakproof With Anton Stoychev - JSJ 694
- Category: #podcasts
- URL: https://share.snipd.com/episode/97912eb0-18c3-4f6d-a604-c831b4a764fa
## Document Notes
## Highlights
- **From PHP And IE6 To Frontend Infrastructure**
- Anton traced his career from PHP and IE6 to frontend infra and performance optimization at Yotpo.
- That history shaped his focus on build tooling, upgrades, and developer experience.
Transcript:
Anton Stoychev
Absolutely. So, again, thank you for introducing me. I'm Anton. I am currently working at Yopo as a core platform team, Frontend Infrastructure officially named, right? So it's a team serving the whole company and other developers within it. I've been doing this kind of thing, frontend JavaScript, whatever you want to call it. Like JavaScript on all specters, let's call it. Even touched in the good old days, a PHP kind of a templating world and websites and WordPress. And ages ago. Oh, boy. Yes, it's like, I don't know, 20 years ago or something like this. I'm not sure. So from IE6 support until today, kind of, the world, that's me. And I've naturally had the desire or interest into the nitty-gritty stuff of frontend. How do we get there? How does the production work? What do we do to make it super fast? But how does that piece work? How do CDNs work? And I kind of naturally ended up doing this kind of thing, which is build optimizations, tooling, optimizing the tools, making sure they work and making sure upgrades work. How do we release? How do we upgrade? And this is the sort of thing that the team does, of course, alongside core stuff like design system. ([Time 0:02:14](https://share.snipd.com/snip/61bafa36-b0d2-400a-aa95-7183c1eb30f4))
- **Tool Dependency Coupling Creates Upgrade Lock-In**
- Monorepos often lump unrelated tools into one dependency bag, creating upgrade coupling and cascading breaks.
- Isolating tools lets you upgrade a single tool without forcing wide runtime or framework upgrades.
Transcript:
Anton Stoychev
And of course, have quality gates, some people call them code checks. Usually you can see them on things like GitHub, on your PRs, and basically guarding you and helping you, by the way. Especially in the AI world where someone needs to put the brakes on some of the code or that's get generated. So it's kind of a double important to have some code checks. Of course, you can wing it, aka vibe it, and just see what's up. And then, I don't know, I ask another AI agent to maybe refactor it. Sure. But I think- AI to refactor AI?
Steve Edwards
Is that what you said?
Anton Stoychev
I think so. I think maybe there is a future where we don't care about quality. We just use the model as of today. And then think about two years in the future, the model will be better. So they will generate something better. And we ask them to just improve the code base. There is a world maybe.
Steve Edwards
Well, the problem with that though, is the AI is training itself on the old stuff. Yes, yes, yes, yes.
Anton Stoychev
On itself, maybe, yeah. So, we wanted to kind of enforce this and do this in a company that has been around for more than 10 years. And I think it's natural, if you have projects for more than 10 years or even five years, that you kind of accumulate legacy. And maybe in this podcast, you talked about it. Maybe I've read it somewhere, but legacy is not naturally a bad thing. Legacy means you have something working, right? It's true. But this thing that is working is no longer performant, is no longer maintainable. The tools that you use can no longer work with it. If you want to upgrade one tool, you're kind of stuck in this nightmarish scenario where you're like, oh, I need to upgrade my ESLint. It's not connected to my code. I want to upgrade it, but the new version requires a new version of Node.js. Oh, okay. So I need to upgrade Node.js. Oh, okay. Then I need to upgrade all my other packages, because they also need to be compatible with the new version. ([Time 0:10:33](https://share.snipd.com/snip/05fddd0b-4de2-4d9b-b9e0-f52ad30d170f))
- **Isolate Tools Into Their Own Runtime Packages**
- Put each developer tool in its own package with its own runtime and dependencies so upgrades happen in isolation.
- Use a whitelist of supported runtimes to limit maintenance overhead while enabling concurrent Node versions.
Transcript:
Anton Stoychev
We talked about the typical scenario where all of your bag of dependencies, you can put any language here, by the way, like Laravel, like with all the dependencies, Python with all the Dependencies, but here the open source that I'm talking about, breakproof.dev, which is just a GitHub repository and it should redirect there, basically offers the following concept Okay, in the JavaScript world, in the JavaScript world of dependencies, right, what we are used to is this bag of dependencies, not modules. Okay? But what if we can isolate each tool with its own dependencies and its own Node.js version separate from everything else, but still be able to work together. Okay, so let's say TypeScript or Jest has its own list of Jest plugins, Jest configuration, right? And specific Node version. And I can only upgrade the node version for Jest, and I can only upgrade the Jest dependencies, and this does not affect the node version for any of me, any of the other dependencies that I have in my project, no matter whatever they are. How does that sound?
Steve Edwards
I already have a few questions running through my head here about how you would do that. But I'm curious. Well, I mean, just obviously, does that mean you have multiple node versions running concurrently somehow? Or how do you, you know, you're saying, okay, I can upgrade this dependency from this version of node to 24. Maybe this one still has to be on 22, for instance, how you rectify that. But I'm assuming you'll get to that.
Anton Stoychev
Absolutely. But I think this is useful. Like if this is your first question, super valid question, by the way, and there is an answer as you guessed. Yes, this means multiple Node.js versions, right? And maybe immediately you have an inner fear of like, oh my God, like I have to maintain multiple versions of Node.js, right? But maybe you can actually limit that. Like you don't need to have all the versions of Node.js. You don't need to have 14, 16, 18, 20, 22, 24. You can have a whitelist. You can have, okay, I support this, 20, 28, that's it. Three whitelisted ones. And this is where we come into the repository. Why is this open source like a repository? This is just a template for a repository. ([Time 0:19:30](https://share.snipd.com/snip/a36d0707-83ea-48ad-acc0-2fe74b31a694))
- **Call Shared Tool Packages From Apps**
- PNPM can filter and run scripts from a specific package so apps can call a centralized tool package without installing it locally.
- That lets apps reuse one shared config while keeping tool dependencies and runtimes separate.
Transcript:
Anton Stoychev
You open the terminal, say PMPM, and you can filter packages. So you can select one package and run a script from its package JSON or run a dependency that is installed in this package. So I can say PMPM filter my Jest package, Jest. That will run Jest from my package that has all my Jest stuff. This means this allows us basically to code Jest from these two other packages without installing Jest. My package A with my view application doesn't have Jest installed, nothing. It doesn't even know about Jest. But it can code Jest that is installed from the other package. Now, probably the question here is like, okay, okay. Why are we complicating things? Why are we making this separation? And now this is the key here. This is because PMPM, after we talked with Zoltan, did one feature, which is, I think, for me, it changed the game in terms of a monorepo. They introduced the concept of runtime per package. And in the latest version, by the way, a few versions back, 10 something, I think it's now, they even allowed different than Node.js runtimes. So you can say my package A will have Node.js 14. It's a legacy one. Let's say Angular 11. But my package B has all the latest React 19, like running some CLI that only runs on the newest Node.js. And I can even have like a concept experimental app that runs on Bun or other runtime and still live in the same repo, and I can still manage all of their dependencies through the same PMPM CLI. Right? So back to the original example. I know, very lengthy. I'm going to pause in a minute, I promise. Back in the original example, I have my Vue application, and I have my Jest-related stuff in a separate package, right, in the monorepo.
Steve Edwards
When you say separate package, is that a separate, you mean like a separate directory?
Anton Stoychev
Yes, separate directory. Sorry. Yes. Let's simplify it. Yes, two directories. One is my Vue application with all of its dependencies, and one is my Jest stuff, a separate directory with all of its Jest-related dependencies and plugins, right? So PMPM with this feature, what allowed us is the Jest-related stuff to be running at node 24 while your application can run at newer or older node, like the application meaning the build Of the application, right? So this means that I can run Jest. I can say from my application, I can say, hey, can you code Jest from the other package? And what this will do is code the other node.js that is associated with the Jest from my application and can run on my source code. This way I can use the specific node that I know it works with Jest from my application that I know that it works with a different node or the same node, but let's put it in a different node. ([Time 0:31:12](https://share.snipd.com/snip/20065841-a5b8-4fb2-9e5f-8fbabd5050fa))
- **Let PNPM Manage Per-Package Runtimes**
- Let pnpm manage per-package runtimes so it auto-installs and invokes the correct Node or other runtime for each package.
- Rely on pnpm’s runtime feature instead of manual global nvm juggling for reproducible behavior.
Transcript:
Anton Stoychev
And now, this is the other fantastic thing. PMPM is not a dependency that lives under Node. It is written in JavaScript, but it's bundled as a standalone CLI. So it can work without a specific... You don't have to have Node to be able to run PNPM. This is a huge difference from NPM. NPM is always bundled with a specific version of Node.js. Right. Okay, cool. So you have PMPM and it's a standalone thing. And the cool new feature is, like addition to PMPM, is that it can also manage Node.js versions. So it has like a NVM features. So it combines NPM and NVM together in one tool. And I'm saying combine because PMPM, in my mind, is a superset of NPM, which means it has all of the functionalities of NPM, to be honest, even the same commands.
Speaker 3
Did you know using your browser in incognito mode doesn't actually protect your privacy? Take back your privacy with IPVanish VPN. Just one tap and all your data, passwords, communications, browsing history, and more will be instantly protected. IPVanish makes you virtually invisible online. Use IPVanish on all your devices, anytime you go online at home and especially on public Wi-Fi. Get IPVanish now for 70% off a yearly plan with this exclusive offer at ipvanish.com/audio.
Anton Stoychev
And under the hood, basically it's kind of the same results, but also can manage Node.js versions. And having that new feature that PMPM introduced after we talked with Zutan that allowed each package to define it, PMPM reads a specific property in your package JSON file. You have two directories and each have package.json file, right? And each of them can specify a property that I think is called runtime environment. And PMPM knows about this property, reads it, and checks if there is an installed Node.js with this version. If it doesn't have, it installs it for you. Automatically, you don't have to do anything. So even if you try to run something from a package that you don't have the Node.js version for, it will install it for you. Of course, you can pre-install it, but it will do it for you if you haven't done this step. I hope that this gives a better idea of like PMPM does it for you, basically. In a short answer, PMPM does it for you. ([Time 0:36:34](https://share.snipd.com/snip/4474c214-5f15-4b6d-b6c4-a31abf3ebd1d))
- **Use The Breakproof Template To Bootstrap Repos**
- Start from the Breakproof base monorepo template and import existing projects instead of retrofitting their repos.
- Use the included infra packages for ESLint, Jest, CI and editor setup to avoid repetitive configuration work.
Transcript:
Anton Stoychev
Otherwise, it wouldn't be a base monorepo, right? It would be just a readme file. You can do that with PMPM. Do it yourself. Good luck. Bye. What it comes in is with already some tools installed as isolated packages, right? They're not installed for your package. You can create your own package with there's already tools in it. Most of them, they're in a directory called infra. If you go back to our original conversation, there's categories of packages. There's something about the build. There's something about code checks. There's something about testing. So these are kind of a tooling that you usually have in an application. So this repo comes with some of those packages that are isolated with their own dependency, in their own packages, and their own Node.js version already there. So what you are able to do is have your project already run ESLint, already run Jest, already configured with some base sensible configuration it's not opinionated and you can change It but it's centralized and it's already working so this concept that we introduced in the beginning it's it's a starter it's a starter that you you can use so they are already working A cool part is usually in as we talked about scale of companies right and scale of projects. When you have 10 projects, you kind of want to reuse and not repeat every single time and have one project with slightly different test configuration from the other, because you end Up with this book chasing where the result and the causation usually is like, ah, it was fixed, but not in this other package, but in the other one, right? So you want to have something centralized as a base sensible configuration. And the repository comes with base sensible configuration for Jest, base sensible configuration for ESLIN, and different kind of tools that are not very known, but kind of useful, Like finding unused dependencies, finding dependencies that are not installed, but you kind of use them. All these kind of code checks, they have their own package, their own Node.js and base configuration that your own package can just reference, right? And of course, if you have your Vue application, let's reuse the example, and you want to extend, you want to change some ESLint plugin, sure, you can do that. Because it only provides you with a base config. It just extends the base config, and you just apply it with more configuration. You overwrite something. You disable a plugin, enable a new one. This is just a base for you to play with and already isolated ESLint. ([Time 0:49:43](https://share.snipd.com/snip/17148aa8-843c-4a67-b1bd-6ae5b7de4603))
- **Pull Base Upgrades Without Breaking Apps**
- Pulling upstream changes to the base repo can safely upgrade isolated tools in your clone without breaking application packages.
- Isolation enables incremental adoption of newer tool versions without immediate large refactors.
Transcript:
Anton Stoychev
Because it's isolated. It doesn't affect your package. It only upgrades ESLint. It only upgrades Node.js maybe for the ESLint. But it's very isolated. So now, one of a sudden, suddenly, just doing Git pull from break proof or whatever, right? Your Git clone second origin, let's pull it. You now suddenly have upgraded YesLint. And that's it. You continue your day without major, like, oh, let's figure out configuration now, et cetera, et cetera, et cetera. So you suddenly have a newer version, like even major version of ESLIM. We just want Git pool. ([Time 0:53:30](https://share.snipd.com/snip/0542e091-b239-4677-a84b-00c9ea56189a))
- **Use Error Snapshots To Gradually Enforce Checks**
- Snapshot current lint/test errors per file so CI can ignore the existing debt while enforcing no new regressions.
- Gradually tighten checks by decreasing allowed counts so teams fix issues incrementally instead of all at once.
Transcript:
Anton Stoychev
Like you upgrade YSLint and all the config, and suddenly in your editor, everything will be read. You will not be able to commit. Your PRs will just light up with all the errors that are coming in. And this is, I think, where the interaction with Dan happened on Blue Sky. He was talking about having a project with JavaScript or an older version of TypeScript and wanting to go to newer version or more stricter or even just introduce TypeScript. Okay. And this is kind of the same scenario of like changing the YSLIN config because something in the code checks become different and all of a sudden all your code screams at you and says like, It's red. So how do you jump over this hurdle? How do you go from previously it was working, now with a little change, it's now suddenly not working. And Dan actually talked two times on this podcast from the two years that I've listened to you prolifically about this topic. He mentioned several times like, oh man, this is a nightmare. And yes, it is a nightmare because you don't have many options. You have like, okay, maybe only introduced for the TypeScript files and we don't check the JavaScript files or we only introduce a few checks, but not all the checks that we want, right? Because in our ideal world, we have like a list of checks that we all agree on that are good and this is how it should be. And we turn them on. But in this scenario, everything goes right. Okay. So what am I talking about? What is the solution? It was proposed to me. It wasn't original. As I said, even this repository, it's kind of all going around PMPM with some more stuff just around it. Right. So it was proposed to me in a previous company I worked with. It was the idea of having an old project running with the strictest possible config for ESLINK, whatever you like, all the plugins that you want. However, running a snapshot of all the errors, aka remembering in my file called Anton.ts, I have five errors of this type. I have seven errors of this different type. I have 20 errors of undefined. I have 20 more for console.log that I don't want to have in my file, right? And remembering that for each file that you have in the repository and just having this kind of a mini registry. Imagine it like a JSON file of key value. File name, type of error, how many? File name, type of error, how many? And this is an invaluable resource when upgrading and going stricter, going to a better rule. Why? Because it lets you see the problems. It lets all of your developers see, aha, my editor is saying this is a problem, so I can focus on it or I can at least understand there's a better way. While your CI checks at the point of upgrade, at the point you make it stricter, still pass. Why? Because you run ESLint, but you also tell ESLint, hey, ignore all of these number of errors in these files. So if you have five undefined, like let's say errors in this file, then you do a change. If there's still five or less than five, it's okay. If there's more than five, then you scream and you fail the commit and you fail the PR. So it's an approach that lets you gradually kind of increase the checks, but still remember the status quo, like not forcing you to change all of your code today. ([Time 0:58:22](https://share.snipd.com/snip/63c66f28-2cea-43e8-b554-229f156970ba))
- **Pomegranate Tool Turned Into A Joyful Kitchen Hack**
- Anton bought a little pomegranate-peeling tool that delighted his father and made the messy task fun.
- He recommends the gadget as a small kitchen lifehack he enjoys using.
Transcript:
Anton Stoychev
Trying to find a translation for it in English. Give me a sec. But I bought, like, it's a kitchen thing. It's a kitchen thing. And I amazed my father with it. Give me a second. Give me a second. Give me a second. Oh, man. Okay i'm gonna jeopardy theme in here what is that the time limit oh no no pomegranate pomegranate pomegranate pomegranate pomegranate pomegranate okay i'm sorry i mean pronunciation Shit i know um so pomegranate is kind of a famously messy to open it and clean it. And I love it. It's a fruit. Yeah, it's a fruit. It's like a red fruit. It's prolific in, let's say, Greece, which is like a neighboring country here in Bulgaria. And it is also part of the south part is there. And my dad, as a person coming from there, loves this. Loves this. I mean, when I was growing up, it was just pomegranate, pomegranate, pomegranate, like any time is the season. And of course, it's like a special type of like opening it and getting the seeds out, et cetera. And recently, I just, I don't know, browse online, this kind of a shopping, anti-depressing, running away from reality moment. So I ended up at a kitchen tool that you kind of put the pomegranate on, and it comes with a little hammer. Like, it's super cute. It comes with a little hammer, and it removes the mess from getting the seeds out, and it keeps the pieces that hold the seeds out of the seeds. It's amazing. Like, I know maybe I'm not doing a good service and job describing it. It's a pomegranate cleaning tool or whatever. And I love it. I, only the fact that it amazed my dad when I showed how this works was worth it. So highly, highly recommend that. ([Time 1:12:11](https://share.snipd.com/snip/34571aff-8212-4e78-92bd-805e51e273e8))