Don't Make Me Think - Maxi Ferreira
Welcome to the learning driven development podcast. I'm Josh Cirre, and here's where I get the incredible opportunity to read, learn, and share with you articles, blogs, documentation, stories, or other forms of written content that exists in the tech space. Today, we are reading an article called Don't Make Me Think by Maxie Ferrera. That's at charca, CHARCA, on twitter slash x and this blog post can be found at frontend@scale.com. So frontend@scale dotcom.
Josh:So this is don't make me think by Maxie Ferreira. At the end of this episode, I'll share my observations and thoughts about this article and just overall what I learned and what I want to apply after reading this. Don't make me think. Cognitive load, cohesion, simplicity, micro front ends, naming things, and how selfishness is the key to good component design published by Maxey on October 22, 2023. Back on issue number 1 of the newsletter, We Were So Young, we talked about the causes and symptoms of complexity and their importance to the overall health of your code base.
Josh:1 of the symptoms we discussed is cognitive load, which is the amount of information you need to keep in your head at a time to make a change. My advice for reducing cognitive load in your code base was to keep your modules cohesive, which sounds great and all, but, also, what does it even mean? I owe you a more detailed explanation. So today's issue is all about cognitive load and what cohesion has to do with it. In the famous words of every PM you ever worked with, let's double click on that.
Josh:Cohesion and cognitive load. Let's start with an example. Imagine a file uploader component that is responsible for, surprisingly, uploading files, but also validating file types and checking user permissions. Some users can't upload files at all. Others can only upload certain file types, and so on.
Josh:Now imagine we need to change the validation rules to add a new allowed file type. There is no sign of any validation logic in the file uploader component, so we put our detective hat on and start looking for clues to find where we should make our change. In our example code base, there's an API service that handles network requests, including requests to the file uploading endpoint. This sounds like an appropriate place to put file validation, so we follow that path. Then we discover that permissions and validation are handled by different modules that call each other as part of some complicated logic, so we need to follow those paths as well.
Josh:After a while, we find all the different places we need to change to add our new validation rule, and we're able to finish our task. But we paid a high cost for it. The change took longer than it should have, and we ended up feeling exhausted from having to put together this puzzle in our heads. This is what high levels of cognitive load feel like. Jumping from module to module until we finally find the place or more likely places where we need to make our change.
Josh:At each step, we have to make sense of the module we're currently looking at, load all of its logic in our heads, and figure out where we should go next. There are many ways to reduce cognitive load, but 1 that would work particularly well for this example is to declare the file type and permissions relationship closer to the component that cares about them. In practice, this could look like a simple map. For example, const validation rules as an array with an extension and allowed 2 parameters. Data structure representing our validation and permissions rules could look like this.
Josh:This doesn't simplify the relationship between our services in any way, but it does wonders for reducing cognitive load. But we might still have some complicated logic between our different services, but now we don't need to concern ourselves with it. We can now declare our updated rules without having to load a bunch of information in our heads. For example, changing the validation rules is much easier now. We don't need to think about the complicated logic powering this feature.
Josh:Our updated design is simpler because it's more cohesive. Things that belong together are together. By bringing the file type validation and permissions closer to the file uploader component, we're bringing the file uploading concerns of our application closer together, which makes them easier to think about. We've also made our design more declarative. The file validation and permissions relationship was previously represented as a bunch of complicated logic.
Josh:Now, we've replaced that relationship with a simple data structure, which is both easier to change and easier to understand than any action or operation. Making things cohesive isn't as always as simple as in this example, so here are a few other things you can do to reduce cognitive load. Use descriptive names. Understanding what something is or does by simply looking at its name is 1 of the best ways to reduce cognitive load. Naming things is hard, but it's worth spending a bit of extra time to come up with a good 1.
Josh:Write good comments. Don't write comments that repeat the code, but use them to explain relationships and decisions you've made. When naming isn't enough, use comments to describe complicated logic in simple terms. Document high level decisions. If what you need to explain doesn't fit in a code comment or if it spans multiple modules, it's a good idea to spin up a quick document explaining the tricky parts of the implementation.
Josh:Reducing cognitive load is an effective way to simplify your code base, but more importantly, it can be a significant quality of life improvement for the people who work in it. It takes time and discipline, but it's definitely worth the investment. Don't make the users of your code base think too much. Remember, this includes both your team and your future self, and they will certainly thank you for it. 1 of the things that I really enjoyed about this particular article is that it was titled don't make me think because I think that is exactly what you need to do in order to make components and a codebase and architecture of an application even better.
Josh:If you think less about how it all works, if you think a little bit harder and even less about what the actual program and architecture and specific components do within that application, then you just make things easier. It's a simple concept of overthinking. If you don't actually overthink how a component works or overthink if this component is ever getting too large or ever overthink what this component actually does and how we should name specific pieces of that component, that's overthinking and in fact making you think harder in the future. So the easiest way to not think hard in the future is to don't overthink it now. So don't think things through too much in the now when it comes to building out an application.
Josh:A lot of times when I'm building out products or features within a product, 1 of the things I love to do is start to extract that into a different component or a different naming architecture once it gets to a point where if I'm jumping back into it, I don't know where I'm at. So don't think too hard about it so that you don't have to think too hard in the future.