Enforcing Architecture Rules with ESLint
Lesson Description
The "Enforcing Architecture Rules with ESLint" Lesson is part of the full, Enterprise UI Development: Microfrontends, Testing, & Code Quality course featured in this preview video. Here's what you'd learn in this lesson:
Steve discusses the value of custom ESLint rules, especially for large-scale refactors and migrations in codebases. While enforcing strict code style can cause friction, custom rules can help identify deprecated code, enforce import boundaries, and manage dependencies across monorepos or microfrontends.
Transcript from the "Enforcing Architecture Rules with ESLint" Lesson
[00:00:00]
>> Steve Kinney: All right, so this one, this one's fun, and I've alluded to it several times. Like the first time we did this workshop, you know, I was like, hey, let's look at like what would be involved in building like custom ESLint rules, right? And, you know, generally speaking, first of all, if like an ESLint rule exists, um, then like, yes, obviously use it, right? But like, I think that there are interesting things that you can do with ESLint when it comes down to like large scale refactors and migrations, right?
[00:00:42]
We always think about ESLint rules of like, how am I going to enforce standards across the codebase, um, one, for anyone who's like either like, you know, an engineering manager or a principal engineer, hypothetically in this room. I think you've all have understood at some point that you had to let go, right? Like, and that, like, not all code is going to be written exactly the way you want. It hurts, right?
[00:01:07]
Like the last codebase that I worked on, like I was initial commit and then I hired, you know, seven people, and some people write functions differently than I do. I like arrow functions. My buddy Alex likes function declarations. Some places in the code have function declarations, some places in the code have arrows. It's not actually hurting anyone, right? And like in some cases, if you're holding his ESLint rules to make sure everyone writes functions the way that I want them to be written.
[00:01:39]
Like, let Prettier do what it does and then just maybe you let go with the other stuff. Where I think that like custom ESLint rules are, um, and we'll talk about two things, ESLint rules that are good for monorepos and micro frontends, and we're going to talk about custom ESLint rules, but to be clear, like, but the ESLint rules that are good for, uh, monorepos and micro frontends, I don't feel like I have to do a sales pitch.
[00:02:01]
You know what I mean? Uh, that seems easy. I'm going to like start with the sales pitch for why you might want to consider a custom rule, right? Which is, we think about ESLint rules like, and if they don't do the thing that I want, it's going to break the build, and then they'll have to make everything the way that I. You know, and then everyone hates you, and then someone turns off the rule, or they start sliding in that ESLint ignore next line, you know, now your codebase is littered with those.
[00:02:29]
I'm not here to tell you you need to do that, like that's a personal journey. Where I think it is really interesting, which is like, um, I was like talking to this company like six months ago and they were doing a big refactor. I think a lot of their code previously, it was all like was like Dart and Flutter, and they were trying to like migrate it to like React, and TypeScript, and they also had all this other stuff, and they're like, how do you do a refactor, and I'm like, one is like, if you don't know what needs to get refactored, it's really hard, right?
[00:03:05]
So like, a pattern, and I'm just kind of doing the sales pitch and we'll get into the like specifics of it in a second, a pattern of like, let's say, um, you know, we know that we're behind on our design system and we need to upgrade, and we don't have time for it yet, right? One thing that could be super useful is find out whatever the deprecated library is, or the deprecated code is, or the deprecated like prop, uh, on a design system component is starting out with a custom rule that just does a warning, right?
[00:03:39]
And now that warning, you can run it across a monorepo easily, it's multiple repos, you run across all of them and like, I don't, dump it into a spreadsheet, I don't know, like whatever. Um, but now, you have a sense of like, we now know, here are all the files across the codebase that we know that we need to slowly migrate, right? And it gives you like ways to find those patterns, and also, yeah, like you can say like, for the places that we know, we're going to set it to a warning, and we'll also set it to an error, right, for new files, right, and new lines of code or something along those lines, right?
[00:04:17]
So now, as you're trying to figure out how to get a bunch of different teams up to speed on the latest version of the design system, or, you know, deprecating in a certain library so you can swap it in for the latest and greatest one, right? You can actually have like, effectively something that's analyzing the codebase on a regular basis and giving you a report on where you stand. Is it getting better?
[00:04:40]
Is it getting worse? Right? Or you can say like, we're going to set it as a warning now, and six months from now that's going to an error, right? And you can do stuff along those lines. And like I said before, most of us have at least welcomed one or two little robot friends onto our teams, uh, who are incredibly creative with how they will like navigate around my best wishes. Right, and so having something that immediately like, um, allows you to kind of like provide that feedback and like when you're writing custom rules, you can also like write a custom error message, uh, and stuff along those lines, uh, it becomes incredibly helpful, so you can hopefully like have your tooling enforce some of the practices, right, because you can tell people on your team that we're deprecating something, we need to get aligned with the design system, and like, managers can hold people accountable.
[00:05:39]
It's a lot harder with some of these little, uh, our little friends, um, but it makes it a lot easier, works with humans too, right, but if you get carried away with humans, with the ESLint rules, they will revolt. The LLMs will tend to just go, alright, yeah, yeah, of course it's a great idea. It's such a good idea. Yes, I will use that file. And so I actually like at one point, like I was like I should cool it with the custom ESLint rules, and I have now gone back to my previous stance.
[00:06:16]
I matured and loosened up a little bit, and then I've like matured even farther to become like my original state. Um, but some of the, uh, the kind of ESLint packages and or rules that you might, um, come to love and respect are, for instance, one called like import boundaries. Remember we talked about before about those phantom imports where you just kind of like navigated yourself out of one directory and navigated yourself into another one, right?
[00:06:44]
Um, import boundaries, uh, allow you to, like, say, if the file lives in this section, it can't be, it has to, you know, it has to pull it in from the monorepo package, or it has to pull it in from that other library. It has, it can do like import at Pulse, which is our demo app, or at your company slash whatever, but it can't go navigating 17 folders over. And if you really like path aliases, this will work for that as well.
[00:07:10]
Um, but it can kind of just like make sure because sometimes like, no, very few people are doing stuff like, either it's an LLM or like very few people are doing stuff like that on purpose. You've probably, uh, if you use VS Code, you've done that thing where you start typing in the name of a function, unless you hit tab and auto import it. And like, sometimes it uses a nice path alias, and sometimes it's like dot slash slash slash lib slash, do you know what I mean?
[00:07:42]
And so sometimes it's, sometimes it's a, it's an AI. Sometimes it was like, you were in the middle of like a core deep thought about the algorithm that you were trying to implement and did not see that it did the wildest thing to import that thing on your behalf, right? And so again, having those checks in there are super useful. Uh, another one that I have used a lot is like banned dependencies, right?
[00:08:09]
Like, I feel bad that I called that moment, but I definitely did it on purpose. Um, but like you say like, no, you cannot import that file. And there's two places where this is useful, right? Like one is if you are trying to deprecate a library, like you can theoretically like, say like, hey, in, you know, apps slash legacy, we don't apply this rule, but in apps slash new hotness, right, we do apply this rule, right?
[00:08:45]
Um, and then you can kind of like make sure that, you know, doing the refactor you're seeking to do is not necessarily a moving target, because again, most tech debt is not malice aforethought, right? Most tech debt that you accrue is either rational decisions at the time where at least if you have to like ignore that rule, you thought about it and you have a place you can go find it, uh, or a lot of times it's an accident, like, somebody didn't know, someone didn't get the memo, the newest person in your team was not there for the thing that scarred all of you, that you'll never forget, and you don't have great onboarding docs, and now they made a boo boo.
[00:09:26]
The no relative imports, you can stop like the, you know, directory, so on and so forth. And like, I know, so like, yeah, having like the custom rules to make sure that like, oh, like those, you know, a lot for me was like working on this Chrome extension, right, where fundamental, like different modules would not work in different parts of the app. Like the service worker stuff couldn't work on the DOM.
[00:09:48]
Anything that was using DOM stuff could not work on the service worker part. You know, there was a server that couldn't have either one of those things, and like, for my own sanity, especially cause like when you're refactoring and moving stuff around and like pulling stuff apart like it, even if you didn't use, if you're not using any like agent coding tools. These are all still practices that were in the last version of this kind of workshop, because like they apply to humans and now they more so apply to your short-term memory friend.
[00:10:26]
Right, um, as we kind of go through. Uh, couple of questions. Um, first question is, um, does, um, you recommend, for example, also enforcing ESLint at the CI/CD level not only on each developer, uh, you know, machine, and the other one is, uh, what are your views on Biome if you have used it, uh, yeah, so Biome's cool and I would use Biome. Uh, Biome's Svelt support, I think is not totally there yet.
[00:11:02]
Last time I started a project, I like, I aspired to use Biome, right, and I didn't like, uh, there's another, uh, library too that's worth checking out, uh, in the great, uh, so we, uh, let's do biome JS. Uh, just, you know, as Rodriguez mentioning, Biome, which is, uh, a project that used to be called Rome. That used to be maintained by the team that made Babel. And then it wasn't, and now it's called Biome, so like if you've heard, you're like, if you've heard about like Rome or about like, it's the same idea there as well, and if I'm not mistaken, cause again, I haven't used it as much as I should because I think for the thing I needed it for, it wasn't totally there yet.
[00:11:47]
It pulls in, yeah, it does, it pulls in like stuff like a linter, stuff like a formatter, uh, and so on and so forth. So it seeks to be, instead of like installing like 17 different tools, you can install Biome. The other one that looks, uh, super interesting that you might check out, um, is, uh. Like, I understand that's supposed to be helpful. I'll, I'll add that Brian Holt uses Biome in our uh build a full stack app with Next.js.
[00:12:18]
Yeah, yeah, like it's a minimal configuration, but he uses it in there if you want to see it in a project. Yeah, I like Biome seems super cool. I aspire to use it. There was some reason last time I started a project that I couldn't use it yet, uh, but it seems cool. The other, the another really interesting one is Oxlint. Uh, things I've never had to say out loud until right now, and it is, um, in the great paradigm that we're going through of rewrite everything in Rust.
[00:12:58]
It's like, you know, uh, where's the, where's the real, uh, you know, 50 to 100 times faster, right? And so like, for some of this performance stuff I'm saying for some of these tools, like, yeah, do the right practices, but also, you can be sloppy if you just use a tool that's 100 times faster than the other one, and so, and I use this one on a project that I'm working on right now, but it also didn't support everything, so now I have both ESLint and Oxlint, but like these are things to definitely keep an eye on and like.
[00:13:22]
Some of this is self-inflicted for me because I'm using stuff like Svelt where if I was using React, like everything supports React, you know, like, you can, you know, Bun can compile JSX out of the box, right, with no even transpiler, you know, like, um, so yeah, there's a lot of different options.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops