Check out a free preview of the full Enterprise UI Development: Testing & Code Quality course

The "Wrapping Up" Lesson is part of the full, Enterprise UI Development: Testing & Code Quality course featured in this preview video. Here's what you'd learn in this lesson:

Steve wraps up the course by sharing some of the design patterns that result in cleaner, more testable code.


Transcript from the "Wrapping Up" Lesson

>> And with that, those are kind of like some of the high level strategies. I will take just one or two more minutes to talk about some patterns because I promised. So some of the patterns I think are good. Like I said, Prefer Dependency Injection. Now, the default arguments exists in JavaScript, let's say hypothetically your function calls fetch.

And I do this actually, the fetch one is interesting, because I accidentally discovered why I should do this in fetch. Is that the fetch that exists in the browser and the fetch exists and most server side rendered frameworks are different fetches, right? Window.fetch does not exist in node 18, whatever year it was, fetch did not exist in node, right?

So I'd have to do something like yo, if we're in the browser, and it's available, yeah, fetch. Otherwise, if we're on the server side rendered piece, I will pass you in a fetch, right? And I accidentally discovered this is a really powerful pattern. Cuz now it's not like, fetch was available.

For any of the things that might otherwise just be imported and then just somewhere deep in the closure scope of my function. I will have the last argument will be just like a whole bunch of fetch or maybe anything else that I would normally just import and include.

I will still import them, and there will be the default argument, but there's the ability to pass in a different functionality if you want. First, that was originally for server-side rendering. Then I found out that was great for testing. Then I found out, I need that function but it made some assumptions.

But I've already made the API flexible because I can pass in some other string formatter here instead of whatever I initially intended, right? As the callback function, and I can reuse this someplace else and get all of the goodness for free without having to copy and paste or rework the function.

So anywhere where it's the thing that think might want to be pluggable. Even if it's a default argument and you almost always use the default argument, give yourself the ability to pass in a different functionality in there, right? If it's something that's traversing a tree object, right? I had one they use that it was just a breadth-first search and depth-first search.

And it was effectively a cheap version of the ESLint thing we looked at, because I needed it for something. But then I instead of like traversing the thing and calling a function, I did that but I had you could pass in a function, right? All of a sudden, what do you know six months later I need to do something similar.

Use the same thing, I fixed a bug and one I fixed two bugs. Some of this is used the tests to find the bugs, using these tools to analyze your code, find all the bugs. But then having the ability that everything kinda passes through as few places as possible, and you've made them super flexible, is incredibly powerful for making large scale changes.

And then you have the infrastructure to know everything works and you don't have to change like six test files cuz that tree traversal one that does a thing is different than that one, walk the tree, whatever. But thinking about how many options that you might need and making them default arguments you don't have to pass a thousand things every time.

But if you do need to override one of those things, giving yourself the ability early on. So this is what that might look like. This is not doing it, right? Important things, use them in that closure scope. You want to use a different hook for grabbing something cuz especially with UI components.

So it was an accordion list, and it was fetching directly from the API. Pass in thing you should fetch from, even if it's like passing a URL that you think is never gonna change, right? Let it be the default argument, life goes on, it doesn't really matter. But then later if you wanted to, it has default arguments, right?

When I use it, I'm not calling it with any arguments because by default, those are the defaults. If I need to override one, and using an object here is usually a lot better, but it's hard to fake that. Cuz then you don't have to be in any particular order cuz if you need to fake the fourth thing, then you have to do like undefined, undefined, undefined thing versus an object and the spread operator.

All this stuff was harder before modern JavaScript. But then just giving yourself the ability to pass stuff in, make everything a prop with default arguments that you think you might need to swap out later, and then you just can, right? Even if you're gonna keep open and close as state, right?

You could pass into like, what the initial like what you're gonna pass that you state hook as the initial argument is. And then all of a sudden, one day the designer is like, I want this particular one to be opened by default. Cool, you already gave yourself the ability to pass in as a prop, right?

And then I said before when we saw what the [INAUDIBLE], these are all things I've said various places. Which is like having, hooks are great cuz they're wonderful, and I talk a lot of smack about hooks because it's fun. But I've written, do you know how many years it took me with that Redux to map dispatch to props and map states to propts before I actually knew what that was doing?

Right, it's all that stuff is squirrelly, but this idea that you would have just the view layer component that doesn't have any state management whatsoever and gets everything in props. And a component above it that does all the hooks and prompts and passes it in means that you can grab that other one totally separate provider, and your test pass stuff in it.

And again, of the sudden you find out that you need roughly the same UI for something, completely different, with different state, you just hug in a different state management container. And then by default you export that one, that's everything. So as you use this component, you just use it like you've jammed everything together.

But you can export that other ones if you just needed the view, you can easily pull it out. The ability is how much is your stuff glued together versus, yeah, pretend it's glued together, but it's more snapped in there. And then you can kind of pull that stuff out and be really happy and stuff along those lines.

And so there are some patterns that kind of once you have some of the rules, once you've got the framework that you can begin to do so you can change your mind. Cuz like most of the tech debt that I've accrued is not because I'm bad at this also because I'm bad at this.

But most of the tech debt that I've accrued is like the ask or the assumptions and what we thought we knew about the world at the time were totally true and there were good choices. There was this book Thinking In Bets, which is like, you can make a good choice that has a bad outcome, a good choice it has a good outcome.

A bad choice it still has a good outcome because luck, or bad choice has a bad outcome, like a lot of the tech debt that we have in our code bases is good choices at the time. Whoever made that choice in 2017, with the information they had, made a good choice.

In 2023, everyone hates that part of the code base, right? And so it's about how much ability to pull stuff apart, swap stuff out, and giving yourself that framework. And then if you don't have it, you inherit a code base. Smother in an automated playwright tests with some screenshots that catch the div and start fixing it and pull it apart.

But these patterns I've now for some of these that we've talked about today. It's one of the things if you have like one, like either code base that you've either built from scratch with some of these patterns. And there's one more that we'll talk about. Or refactor things, you do it once you don't know if you're lucky.

Or like this is a pattern that works. You do it two or three times, and there might be other patterns, these are the ones that have worked for me, right? The last one and we talked about this is it is very tempting to use a use effect or what have you in your component.

And then massage the data in our view layer component, all over your code base everywhere and format stuff. And then all of a sudden the backend engineer changes an API or we choose to change an API and you've gotta chase down all of those. One of the things I usually do with, we saw request for API.

I'll also have whatever the nouns in my thing are is like if it was a to do list, I would have a function called get all to dos and get to do that takes an argument. Maybe that calls requests from API under the hood, and then it calls fetch.

But it gives me if all the times I'm fetching a thing are going through the same function. And it's always the same bug by the way. It's some back end engineer because it's Go it's Golang, it's Golang. They do something, and instead of an empty array when there's nothing, it's now undefined or null.

And then I was calling map on it all throughout my code base on all these different views. Now, I got to chase down all of them, I learned the hard way. No, no, we format the data in one place right after we get the API. And when they change that empty array into a null, I fix it in one place and everyone gets it for free.

And just kinda like building up these abstractions of how little change for changes outside of your control can you deal with, right? And then we've got the test, we've got the build processes, we've got all the stuff that supports that. But it's about one, you can't do that until you have those processes in place.

And then once you have those processes in place, these things will make sure that it will kinda keep you honest and keep you working on everything. And that that is my spiel. Thank you.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now