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

The "What is Enterprise UI Development" 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 discusses the core questions around enterprise UI development. How do you create maintainable code? What does it mean to be maintainable? Components of a well-architected UI application are also discussed in this lesson.


Transcript from the "What is Enterprise UI Development" Lesson

>> The kinda core questions that we are gonna look at is how do we create maintainable code bases? I think a lot about when I started at my current job, one of the super-duper senior distributed system engineers went like, for an architect, what is that, right? And it made me think a little bit about what does it mean for a front-end app to scale?

Because a database might have a million users, a hundred thousand users slamming at it, right? Your front-end application, generally speaking, they download the code into their one browser, and sure, it could be Internet Explorer or Safari, depending on the year. But generally speaking, the idea of a million people using it, it's not necessarily gonna have a larger effect than one or two, depending on other than the breadth of the different situations that they're using it in.

So the way that I kind of explained the plight of maintaining a large front-end code base is, I was like, you know how on the backend, you all like to break everything into very small microservices that don't know anything about anything and are totally isolated and encapsulated? He's like, yeah, yeah, yeah.

And I'm like, my job is to hide that from our customers, right? They should never know that you did that, right? And so the way I think that a lot of times a large front-end code base tends to fall apart is not necessarily that it goes down a database, right, or stops taking requests.

It's usually that it slowly erodes, and kind of in physics, everything tends towards entropy, which is chaos, right? It slowly erodes to the fact that what should have been a one-point ticket or a three-point ticket, right, a relatively easy thing to do, everyone' like, I don't know, that might take two weeks, right?

Cuz we don't know what will break. And every time you try to push something in production, someone's pager goes off, right? Insofar that we build these large, stateful applications, the way that they tend to break down on us is that they become so complex and so complicated and so chaotic that they become almost impossible to maintain.

And that's when people start whispering about the rewrite, and I've done the rewrite, the rewrite never goes well, right? [LAUGH] It's just a new opportunity to make the same mistakes and start the process over again, but you will end up back there as well. And that is kind of what we are seeking in our time together in this course, is to basically figure out, to the best of our ability, how do we kind of manage that chaos and keep it under control.

Like I said, even as I was putting all this together, and over the course of the last six months, even in my relatively, not particularly old, and it's open source code base, so I can't really hide anything from you. I have noticed, there are things where we need to focus on the testing over here.

We need to, after we get all the lint errors handled and all the accessibility errors handled, then that needs to actually break the build. But you can't break it in the very beginning because then you would never ship again, right? And so how do you kinda manage these things?

Cuz they exist in new code bases and they absolutely exist in big code bases. If they don't exist in your code base, either you are the greatest front-end engineer and you're probably alone. Or you work on the greatest team possible, or it is so new and so small that you just haven't gotten there yet, right?

And so that's great. Also, you should hang around, because maybe we can talk about how to make sure that you don't end up there, right? Because for all these things, each time that I've done this, it is, you're always trying to push back the ocean with a broom.

But these strategies over time start to add up and you do have a code base that you're relatively happy working in, right? So yeah, so as I kinda said, right, it's probably hard to define what it means to build a maintainable application, as we spend the next several hours together talking about how to build a maintainable application.

The best way to talk about it is to talk about what it's not, right? If you feel like you can't ship anything without being worried that something else is gonna pop off the other end and break, then your app is probably not maintainable, you literally can't maintain it.

You want to be able to change stuff and confidently know you didn't break something, right? It's one thing, step one, you would love to change something and have nothing break. Step two, or second best, is you'd at least like to know before your customers find out, right? I joke that you'll always have tests, right?

It's easy you have automated ones that run lightning fast. You're doing it yourself or your customers are doing it for you. Somebody is testing this software one way or the other. Maybe you wanna refactor without overwhelming fear. You know that you need to sit down and take a step back and think about this when a new version of whatever framework or even some library that you're using comes out and someone's like, should we upgrade?

And you're like, should we upgrade? Absolutely, can we upgrade? No. And over the fear around that, like, let's wait until Thanksgiving break or the end of December to upgrade. That's when you start to know that it is time to think about some of these things. Yeah, updating dependencies and knowing that everything works without having to go testing a very small to-do list app, of course, you can do that.

Even then, it can become tricky. Testing any kind of decently large application, that becomes like an all hands on deck, the team is kicking it for two weeks, which is not the best use of anyone's time. And the theme that we're gonna kinda deal with today is basically how do we automate this?

How do we basically have processes and infrastructure that is making sure that we can feel confident? And yes, we should always have the human element there, right? Code reviews are great. They don't always work at scale. There's always like, whenever there's a bug, the joke to git blame who the last person touch it was.

My thing is, well, who read the code review? Who approved it? And I am guilty of this, right? Sometimes you're tired, you don't have the same attention to detail, probably like the person who wrote it. Yeah, sometimes you just feel bad cuz you've been really nit-picky over things, right, and you get to the point where it's either it ships or it doesn't, right?

And so code reviews, obviously a thing, but then can we build the infrastructure around it so that we're talking about more forward-thinking and high-level things rather than you have an extra space after that opening bracket, right? Have something deal with that for you, right? That's not a good use of anyone's time.

Cool, and so again, this theme of automation. If I had to, and I did have to because I prepared for this, think about what are all the components of a well-architected, and that's a word that I don't love, but I stole from Amazon in terms of their cloud services.

What are the pieces of a decent front-end architecture that you need to have kinda in place to be able to consistently ship software? Testing, right, all sorts of different kinds of testing, some of these are more possible than others, just to kind of make sure that everything works, because like I said, either you do it or you automate it.

I think one of the nice things in the modern era is a lot of the tooling around this stuff is a lot better. I would argue that TypeScript, for instance, takes out an entire class of tests that I used to write, that I still occasionally write, cuz we trust that our APIs stay the same, but we don't really know that, right?

Just because your type doesn't have undefined or null, that doesn't mean that the API didn't give you that. Some of the worst bugs I've ever seen was a well-meaning backend engineer accidentally shipping a breaking change to the API, right? But generally speaking, if you think about what TypeScript is, right, and we're not gonna get all the way into the weeds on this.

But there's a certain spiritual aspect of it that is running through your entire code base, making sure that you pass any given function something that it expects, right, and yelling at you when you're unclear about that, right? That takes away the entire set of tests where it's like, you pass null into the function to see what happens, and then you pass all sorts of other bogus inputs, right?

That can still happen cuz it's JavaScript at the end of the day, but there's an entire set of tests that will help in that case, right? Linting, and linting is tricky because there's the stylistic ones, which I'm not super interested in. One of the things that I've had to learn to do over time is let go of my stylistic preferences.

And it's okay if someone uses a function declaration and somebody else uses a function expression, it's gonna be all right. That's not gonna be the thing that stops you from shipping stuff. That said, there are things in your linting that you might want to avoid unnecessarily confusing, like for loops or something along those lines.

A really great example that I think a lot about is, for instance, we have an abstraction for making API requests. And yeah, under the hood, it's just using fetch, sure, sure, sure. But it's also doing things like, well, if there is an error, right, go ahead and bubble that up to Sentry, which is what we use to track errors in production, right?

And then maybe it's got some retry logic. And generally speaking, it's also got types for the different endpoints that can hit so you don't mistype a URL. And it's got a whole bunch of features that we'll go ahead, and we have cursor-based pagination, so on and so forth, right?

You should use that instead of using fetch, right, if you're working in the application that I work on. However, there's not really a test that's gonna catch that, right? That becomes a static analysis, like, hey, I noticed you used fetch here, maybe you can opt out of it if there was a really good reason.

But let's automatically check, maybe you're brand new, maybe you're just switching in from another code base and you don't know everything, cuz yeah, we should document everything, except nobody reads it. And goes ahead and just kind of informs and helps guide the maintainability of code base. Cuz that's one of those things where a well-meaning mistake where somebody didn't know is all of a sudden, hey, when you're going to the post-mortem and you don't have any logs, because maybe the rejected promise didn't bubble up to your error reporting tools.

And formatting, I think that just set Prettier up and let it do its thing and never have these conversations again is a world that I like to live in. The kind of one of the major thing's testing is obviously a huge part of our time together, all sorts of testing, all the different flavors of testing.

That said, if you have to run NPM test, that's you're missing the point, right? So there should be some process that we'll go ahead and check the lint. There's some process that goes ahead and runs the tests for you on every push to a PR or something along those lines.

So we'll talk a little bit about how to set that up. And we're gonna use GitHub Actions, which is wildly powerful for a somewhat free-ish tool, free on an open source repo, relatively cheap on a private repo. You can do really cool things, like have it automatically open issues for you.

We have it set that when we bump the version on the open source UI, it will automatically open up a PR on our SaaS offering with a new version of that and run the tests there as well. A lot of this stuff will be bespoke to the unique needs that you have to solve for, but we'll kinda talk about the wide range of possibilities.

And my goal is we'll do the kind of obvious things that everyone should do, but we'll also kind of explore some of the interesting recipes and edge cases. And my goal is that a week from now or two weeks from now, you're walking down the street and you're like, I could take this tedious manual process and put together a GitHub Action workflow.

And if you use some other CI/CD tool, that too, whatever, that'll all work, but something to think about, what are all of the annoying things that you do, right? And when we get to the processes, I'll round back to deployment infrastructure in a second, the processes, which is like, okay, how do we design new features?

Do we have a process for writing a design document? Who reviews those, right? Those things are also really key to the maintainability of your code base as well. On those build processes and systems and stuff along those lines, as it stands right now, we do this thing where it's like, we're gonna release every Monday.

That's an hour meeting, with the goal to cut it down to 15 minutes, right? So every manual step, someone's taking a note of how can we automate this, right? And that's gonna be some of the stuff we look at today as well. And then we have that kinda structure.

Does our view layer sit there and process a bunch of data and make a bunch of API requests? Well, that's gonna be hard to test, right? And we'll kinda see that in some cases. What is our deployment infrastructure? That's gonna be unique to every company you work at, but we'll at least talk about it at a high level.

We talk a lot about technical debt. The other kinda thing that I wanna highlight is there's also this cognitive cost, right, to a complicated code base, right? If you know that all of your, if you follow whether it's a pattern like MVC, which is whatever, some pattern where you know, okay, all of the business logic is here.

All of the fetching and massaging the data is over here, all of the view layers over here, we have that kind of setup and you kinda know where to go looking for something, right? That makes you way more productive in a code base than everything kind of jammed together.

When we talk about testing, kind of one of the heuristics that I'm gonna kinda use, which is, if you have to mock and stub your own code, you've done something wrong, right? Third party things you don't control, sure, sure, sure. But if you are all of a sudden like, I don't have access to that hook from outside the component or the Redux store or what have you, it doesn't really matter, and you find yourself mocking things you own, right, that's a good sign you have not separated these concerns.

And as I thought about it more and more, there are kind of the three levels, and this pyramid is designed after Maslow's hierarchy of needs, which is first, have the systems in place, right? If you don't have the systems, then you can wax poetic from your ivory tower about the best way to structure an application, it doesn't really matter.

Also, you can talk about it over lunch, but unless you actually have a process for getting the team on board, and all those kinds of things, then it doesn't matter either, cuz it's just like your thoughts. And so those patterns, those kind of belief systems and how do we structure an application, sit on the foundation of do you have a system that keeps everyone honest about that?

And do you have a process for kind of collaboration? Otherwise those parts don't really matter. And so we will spend a lot of time focusing on the systems, because one that's the most objective, right, everyone's code base is gonna be a little bit different, but we will when possible kind of talk about the processes and the patterns.

With that, we will be writing some code and tweaking some stuff, right? I went through my own code base and tried to find, at one point, I was just gonna grab the entire open source code base, I realized that trying to grok that was a recipe of sadness.

And I learned some things, even trying to pull certain functions out about how I'm gonna break a lot of my own rules. So [LAUGH] I have a set of things to do for myself after putting this all together, but trying to take some of those patterns into small, grokable pieces.

So we will be kind of objectively, it's not gonna be hours upon hours of me waxing poetic for my ivory tower. We'll kinda look at some of these, particular the systems, with some small bite-sized little sample applications.

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