Module 4: Implementing

36. Guardrails and Constraints

Resources

Transcript

[00:02] The initial version of our project may have a very clean architecture and a well-defined design. But as the codebase continues to grow and as more people contribute to it, it is very likely that the codebase will grow messy and increase in complexity as well. So it’s important to spend some time defining some guardrails and constraints, especially at the beginning of the project if you can. This way we can protect it as it grows over time.

[00:32] I want to share in this video a few of my favorite tools that I’ve used in the past to apply these types of constraints to the codebase. The first one is called Dependency Cruiser. This is a library that works on every JavaScript or TypeScript project we can use to validate the dependencies of our codebase against a set of rules.

[00:55] For example, one of the most common rules that we may want to define for our application is that modules cannot or should not depend on each other. One of the reasons that we define modules in the first place is to keep things nice and separate. For example, I have here my restaurant module, my delivery module, server module, and I want those to be as independent as possible.

[01:19] Now, over time it’s possible, and it’s very likely that you will find that maybe one of your features will have a need for a component that is defined in a different module. So for example, let’s say this feature four here has a need for this component one, that is defined in a different module and we might just import it directly because there’s nothing really stopping us.

[01:40] Now, the problem with this is we’re making our search module dependent on a restaurant module almost in an implicit way. It’s very likely that this component will change over time. This component that only belongs to the restaurant module might break your search module unexpectedly. This is one of these surprises that we don’t like to have. We increase the possibilities of these scenarios happening when we break the encapsulation of these modules in this way.

[02:12] So we can use Dependency Cruiser, this library, to establish these rules and say, for example, that we don’t want a component in a module to depend on a component that is defined in a different module. Let me show you how that looks like.

[02:26] You can find this example in the constraints branch in the repo. This is essentially I just installed Dependency Cruiser library and I created this dependency-cruiser.js file which I just defined one rule. This is a forbidden rule about how modules do not depend on another module in a separate folder. So this is an error. I want this to break my build if I break this rule.

[02:59] Here you have these definitions which you can find more information about how to define this using regular expressions in the documentation of Dependency Cruiser. So let me show you how this works.

[03:09] If I run the NPM Run Check Constraints script here, it will check my constraints and it will say that no dependency violations were found because this is all good. I’m not breaking any rules. But now let’s see if I, for example, in this restaurant-carousel component that belongs to my delivery module, import this function. This is one of the functions that we defined in one of the previous videos, but this function belongs to the restaurant module, right? It’s not really a shared component or a shared function or anything like that. It belongs to an adjacent module.

[03:51] So I’m going to save that and I’m going to run the Dependency Cruiser check again, and this time it should fail and it will let me know what my violation is here. And if I scroll all the way up, I see that I have an error in this delivery module that is trying to import something from the restaurant module.

[04:09] This is just an example of one of the rules that you can define, but you can probably think of other rules as well. Maybe you don’t want teams to depend on each other’s work or something like that, and you can use Dependency Cruiser to define those rules.

[04:27] I want to quickly go over a few other tools that I think can be helpful. In some cases, Commonality is a newer tool that is used, I believe, mostly in mono repos, but that can provide some similar levels of constraints and checks for your mono repos. So you can help you organize your codebase and make sure that you don’t have any circular dependencies or anything like that. You can define ownerships of certain packages and you can tag them and so on.

[04:56] So if you’re using a mono repo, this is a very good tool to take a look at. It also provides some very nice visualization of your packages and all of your dependencies.

[05:08] If you care about performance, let’s say performance is one of the quality attributes that you’re trying to promote, there are a few things that you can put in place as well to protect your architecture when it comes to performance. One simple tool that I like to use is this one called Bundle Size. This is a very simple check of the size of your different bundles, and you simply define an object containing the types of file or the names of the files that you want to check, and what is the maximum size that you want to allow. If your files grow over that limit, the build will fail.

[05:49] So this tool will let you know there is a use case for this, even if you don’t really care too much about performance because you may want to check that your bundles don’t pass one megabyte or something very big like that, because you want to prevent accidentally installing a library or a package that is too heavy. This tool will help you catch those errors as well.

[06:09] If you have more strict performance requirements, something that you can look into is this thing called performance budgets. I will link to this article which has a lot of information about everything you need to know about how they work. But they essentially are a way to check your core web vitals and other metrics such as broadly bundle size or the size of your images and so on. They will give you alerts that will let you know if you are above a certain threshold that you can define as your budget.

[06:41] So if you have certain requirements that you have to meet in terms of performance, you don’t want the page to load in more than one second or something like that. You can establish one of these tools which will require a third-party service. In this case, we are looking at a Speed Curve article, but there are other tools that you can use as well. They can be very useful if you have these very strict performance requirements.

[07:08] The last tool that I want to call out is this VS Code extension called SonarLint. This isn’t related to architecture per se, it’s more related to design, but it can help you sort of manage the complexity of your code. One of the things that this extension checks is the cognitive complexity of your code. So if you have too many execution paths or if your code, you know, if your functions are too large, they will let you know what the problems are and what you can do to fix them.

[07:39] There is a very good talk that covers all different types of complexity in the code, including cognitive complexity, and it also has a little demo of SonarLint in practice. So if you want to take a look at how that looks, I will link that below and you will, of course, find links to all of these resources that I just shared in the description of this video.