Video Summary and Transcription
Today's Talk highlights the importance of software quality and its impact on businesses. It emphasizes the use of different tools and practices to improve software quality. The Talk covers topics such as testing with TypeScript and React Testing Library, accessibility, Cypress for end-to-end testing, writing better queries, monitoring performance, using feature flags with LaunchDarkly, and the value of Prettier. The key takeaway is that developing high-quality software with fast feedback loops and simplicity is crucial for success.
1. Shipping High Quality JavaScript Apps
Today, I want to talk about shipping high quality JavaScript apps with confidence. I'll start with a story of a company that went bankrupt in 45 minutes due to a failed deployment. This highlights the importance of software quality and its impact on businesses. There is no single solution to improve software quality, as stated in the paper 'No Silver Bullet'. It requires a combination of different tools and practices, represented by the testing spectrum.
Hello, everyone. My name is Tomasz Hokomer and today I would like to talk a bit about the way we can ship high quality JavaScript apps with confidence. But before we move along, I would like to start by telling you a quick story. This is a story that actually happened. You can see the link over here. I will be also showing the slides later.
And this is a story of how a single company with nearly $400 million in assets, went bankrupt completely in 45 minutes because of a single failed deployment. So the story goes like this. A global financial services firm was preparing for a major upgrade of one of their systems. So they wanted to replace an old system that hadn't been used in eight years by essentially using a variable. And don't ask me why they had that code in their codebase for eight years and why they decided to release a variable because this is another story entirely.
But the story goes like this. That they had eight servers. And so they only copied the new code to seven of them. So one server still had the very old code, which caused all sorts of problems. And the problems were so severe that they ended up basically trading eight million shares every single minute with no key switch, no documented procedures how to react. And it was an absolute mayhem. And in short, they've managed to lost $460 million in 45 minutes because of a single fake deployment.
And there is a why I'm telling you the story is not to make you scared of shipping software to production, is to convince you and to highlight the fact that quality matters. And it's not only that we care about the quality of our software because we want to be good engineers. Quality of your software can also make or break your business. And the question naturally becomes, how do we ship quality software? Is there any meaningful way, any single item that we can add to our backlog in order to increase the quality of our software? And not really. Lately I've been inspired by this paper titled No Silver Bullet, The Essence and Accident in Software Engineering. And this title is from the 1980s, in fact, is older than I am. But it speaks is completely true, even in 2021, because in the abstract to this paper, we read that there is no single development in either technology or management technique, which by itself promises even one order of magnitude improvement within a decade in productivity and other factors. And the same goes for testing. There's not a single way, there's not a single technique or tool or whatever we can add to our toolkit in order to improve the quality of our software significantly. It has to be a combination, a collection of different tools. That is why, when I think of testing, I like to think about this idea of a testing spectrum. So all of the different tools and practices and the ways we try to optimize the quality of our software lies on the spectrum.
2. Tools for Quality and Testing
There are tools and practices that help improve software quality. Testing is a combination of these tools and practices. Prettier, although not a typical testing tool, helps minimize editor cycles and allows for quicker iteration. TypeScript, with its static types, eliminates certain bugs and helps minimize editor cycles as well. It is recommended to use TypeScript in strict mode to improve code quality.
So there are tools and practices that are very close to developer and kind of far away from the user. And there are things that are very close to the experience that we are shipping to our users. And a good testing practice is basically a combination of all of those. Which, by the way, is roughly similar to the way we are handling the current global pandemic. There's not a single solution to the problem, but instead, by applying multiple solutions, we are able to somehow mitigate the spread of this problem.
And again, going back to the testing spectrum, we're going to start with the developer. So the first tool, which is might be a bit surprising, that I would like to mention is Prettier. Prettier is not typically thought of as a tool for testing, but here's the thing. If I have written a code that has some syntax error, Prettier won't do anything. Therefore, Prettier helps us minimize editor browser, editor cycles, because whenever I hit save and my code doesn't shift, doesn't move, I am not going to even open up my browser. Therefore, I'm able to iterate quickly. I have to open up my browser less often, and I'm able to write a better software because I'm able to iterate more quickly and more efficiently create my code.
When it comes to other tools that help us write better code, I have to talk a bit about TypeScript. I've been using TypeScript for a while now, and I strongly recommend using TypeScript for medium and large and huge projects. And the reason why TypeScript is useful when it comes to quality and testing is that static types eliminate a certain class of bugs. Most notably, they eliminate bugs like this, undefined is not a function. That is not a thing that you are very likely to see in production when you are using TypeScript. Of course you will have other bugs, but undefined is not a function is probably not going to be one of them. And major critique of TypeScript, and for the record, I completely get that, is when you are trying to transition from JavaScript words to TypeScript, seeing code like this with all of those type annotations is a bit scary to say the least. There is definitely a steep learning curve when it comes to TypeScript and I definitely get that. But again, TypeScript is great because it helps us also minimize those editor browser, editor cycles. Because if I am refactoring a large React component written in TypeScript, as long as TypeScript keeps complaining, I am not going to even open up my browser. I'm going to keep iterating on this code for as long as it takes in order for me to make TypeScript happy, basically. So I am able to iterate quickly and when I finally open up my browser in order to play with the UI I've created, I know that a certain class of bugs have been effectively eliminated. So I'm able to write better software and quicker. When it comes to TypeScript, I strongly, strongly recommend using it only in strict mode. In fact, strict mode is recommended by the TypeScript team but it's disabled by default, which kind of makes sense because the vast majority of projects are actually migrating from JavaScript to TypeScript, but still I strongly recommend turning it on. One thing that I would like to emphasize that type safety doesn't mean that your code is bug free. What it means that your code doesn't have any type issues and that's it. And that's, it's not only it but nevertheless you have to think about that you will also have some other bugs because types are not going to guard you about them when it comes to misunderstanding of your business requirements and so on.
3. Testing with TypeScript and React Testing Library
When it comes to TypeScript, it's somewhat easy for Greenfield projects but very difficult for legacy projects. Testing library helps us build more accessible apps and focuses on testing the experience of users. The queries exposed by testing libraries allow interaction with visible elements on the page. For example, getting elements by role, text, or alt text. React testing library is recommended and testingplayground.com is an excellent tool to understand query writing.
Because again, they only eliminate a certain class of bugs and definitely not all of them. There is no silver bullet. But when it comes to TypeScript, it's somewhat easy in a way, like easy-ish for Greenfield projects but it's very difficult for legacy projects. If you have a large JavaScript project that you are trying to migrate to TypeScript, it's not easy, it's not trivial, absolutely not. And if you would like to learn more about TypeScript and especially migrating from JavaScript to TypeScript, I strongly recommend checking out this book because some of its final chapters actually talk about migrating from JavaScript. I read it last year and I think it's great.
Let me move on. We are just getting started on our way, on our journey through the testing spectrum. I cannot talk about testing JavaScript application without talking about testing library. It's absolutely awesome and I'm a huge fan of it. And this quote that has been said by Ken C Dodds is basically all that you have to remember when it comes to testing and choosing your testing tool. The more your tests resemble the way your software is used, the more confidence they can give you. Keep your tests close to the experience of your users. Every single time you're trying to think about new testing solution, think whether it helps us emphasize the experience that we are shipping to our users.
There are two things that React testing library does amazingly well. First of all, testing implementation details is hard. I have been using it for quite some time now and I honestly don't remember how to get the internal state of a React component with React testing library. No idea because I never need to do that. And secondly, the API is designed in a way that it helps us build more accessible apps. So to show you an example, the queries that are exposed by testing libraries are basically, you can get elements by role, by text, by alt text. So you only, you are only able to interact with things that users are actually seeing on the page. So there's no implement, there's no testing implementation details. When it comes to React testing library or testing library in general, I strongly recommend playing with testingplayground.com because it is an excellent way to understand what kind of queries should you write. To give an example, imagine that you have this very small email for a label and an input. I would like to test that. So what I'm going to do is a bit of a naive implementation. I'm going to add a test ID and I'm going to get this element by test ID. If we enter that into the testing background, we're going to see that... Okay, so far, so good. It is going to work, like don't get me wrong, but you could upgrade it to get by role in order to write a better queries, in order to write a better query for this component.
4. Accessibility, Cypress, and Testing
When it comes to accessibility, adding a bit of additional markup can make a form more accessible. Everyone should be able to use a web app, so it's important to care about accessibility. I recommend checking out the course 'Develop Accessible Web Apps with React' by Aaron Doyle. Users care about the results and the user interface, which is where Cypress comes in. Cypress is my favorite tool for end-to-end testing, and I recommend using Cypress Testing Library for testing React components.
Okay, fair enough. So I'm going to get this element by role, by accessible role of a text box. Okay, so far, so good. This is awesome. But, and here's the key insight of this. That you could make the query a bit more specific by adding the name option, and this was required to add some markup though, because your element is not named properly.
So here, testing background is inviting us to write more accessible code, because if I add some... A bit of additional markup in order to make this form a bit more accessible, I am able to get this element, this input, by accessible row of text box and a name of email address. And right now I have effectively built an accessible form element. Very small, mind you, but still, it is accessible. And I am able to also test that. So I've managed to succeed with two goals, with, you know, with a single function, with a single test implementation.
Because here's the thing, everyone should be able to use a web app. Web is for everyone and your apps should also be for everyone. You shouldn't be limiting your user base by the fact that you forgot to care about accessibility. And this talk is not entirely about accessibility and I definitely don't claim to be an expert when it comes to accessibility, but I strongly recommend checking out this course on echo.io, develop accessible web apps with React by Aaron Doyle. And the best part that the course is actually free. So there's definitely no excuses for you not to check it out. I strongly recommend that.
Speaking of users, the thing is that users don't care about your code. They don't care if you're using React, Angular, jQuery or whatever. They care about the results it provides. They care about the UI, the ease of experience and so on. And that is why, this is where Cypress comes in to play. Cypress is straight up my favorite tool for end-to-end testing. I've been using Cypress for like three years now and I think it's absolutely amazing. I'm a huge fan of its UI, of its API and just watching those tests play themselves in the browser is just fun to me. I enjoy watching the results of my tests.
And when it comes to Cypress, I have two things that I would like to recommend. First of all, using Cypress Testing Library because if you are using like React Testing Library in order to test your React components, if you are using also Cypress Testing Library, you get two benefits.
5. Writing Better Queries and Cypress's Intercept API
First, writing better queries by getting elements by accessible roles. Shift from integration/unit tests to end-to-end tests with less cognitive shift. Cypress's Intercept API provides full control over HTTP requests, supporting REST and GraphQL. Assert and modify request bodies, test response contents. Easily combine with other Cypress features. Practices for post-release cycle: app speed is crucial, delays impact conversion rates, users leave slow-loading pages.
First of all, you will be writing better queries because again, you will be strongly encouraged to get elements by the accessible roles and so on. And if you cannot get an element by an accessible role, well, it's time to add one. And secondly, if you are going to shift from writing integration or unit tests to end-to-end tests, there will be much less cognitive shift because you will not have to think, okay, I'm writing a new unit test so the API looks like this. Now I'm writing an end-to-end test so there's a completely different API. All those things play very nicely together.
Secondly, Cypress has recently added a support for Intercept API and it's quickly becoming one of my favorites because Intercept allows you to have a full control over the behavior of your HTTP requests. And it's supporting both REST and GraphQL because, well, GraphQL is just REST under the hood. It's not REST, it's HTTP under the hood when you think about it. Everything is just HTTP requests. And there are a couple of things that Intercept API allows us to do. First of all, for instance, if we have a POST request we can assert that the body of this request is going to include some string. Secondly, if I am sending a POST request I can also modify this request before it gets sent to the server. So I have a complete control over the network, over stuff that is going to be sent over HTTP. Secondly, if I am going to send a request to the API I can also assert what is going to be included in the response of that request. So I can test that as well. And Intercept allows you to... Intercept can be easily combined with all of the other features and practices that Cypress provides. So for instance, in this example I'm able to create a custom command that allows me to stop feature flags in my project. So whenever I am going to call my feature flag service I would be able to get whatever feature flags I'm going to pass into this function. And this is a highly flexible API and you can do all kinds of things with Intercept API. I do strongly recommend checking it out.
We are getting close to the user now. It's quite close because Cypress is clicking on buttons writing text in input fields and so on. But users are not going to use our Cypress app Cypress test. They are going to use what we are going to release. So we have to talk about practices for post release cycle because well, our app has to be fast. Every 100 millisecond delay in website load time can hard conversion rates by several percent. This is absolutely significant for your team, startup business, and so on. And secondly, half of your user base will leave a page if it takes more than three seconds to load. But here's a problem though.
6. Monitoring Performance and Testing in Production
It's important to monitor the perceived performance of users using tools like Century, New Relic, or Datadog. These tools allow you to create dashboards to monitor frontend performance and JavaScript errors in production. To truly understand the user experience, it's recommended to use your own product, a practice known as duck fooding. By using your own software, you can empathize with the user's experience and make improvements. Testing in production is necessary to account for the differences between staging and production environments.
It's fast on my machine, right? I am personally using a MacBook Pro. Everything is fast on my machine. But this is definitely not the experience of the vast majority of your users.
And there are a couple of tools that help us when it comes to monitoring the perceived performance of our users. So there are tools like Century or New Relic or Datadog. And all of them, when it comes to JavaScript development, they do fairly similar things. So, for instance, Datadog allows you to create dashboards in order to monitor frontend performance. So you can create dashboards that monitor time to first byte, time to first contentful paint, initial loading time, and so on. You can measure all those sorts of metrics and understand what exactly is going on and what is the experience you are serving to your customers.
And, secondly, tools like Datadog, Sentry, and New Relic, they also allow you to monitor the JavaScript errors in production because even though you are using the bestest TypeScript typings, well, you are going to have probably some runtime issues in production. Stuff like this always happens, like in Safari 9 or whatever. And with tools like this, you will be able to understand what exactly is going on, being able to dig in and investigate how do I fix that.
The somehow ultimate form of this testing spectrum is not trying to emphasize with the user, it's to become the user. And in my opinion, the best way to do that is to use your own product. And that is called duck fooding in the community, in the tech industry. I don't like the term, but I'm going to roll with it because it's somehow well-known in the industry. So the idea is that this is a practice of an organization to use its own product. And this is a way for an organization to test its products in a real-world usage. And for me, this is hugely useful and hugely important because imagine that you are working on a food delivery platform. If you don't feel like using your own site that you've built in order to order a pizza because it's slow, it's clunky, like there's a huge form that you have to fill in every single time, why would you expect your users to use that? If you are using your own software, you can emphasize with the experience that you are serving to your users. If something is annoying to you, it may be annoying to others. So why not be empowered, take some initiative, and just fix that in order to ship better quality software?
When it comes to measuring the experience that we are serving to our users, we have to address the topic of testing in production and by testing in production, I don't mean like yellow driven development, but whatever we want in production. But we have to test in production in some regards because as Corey Quinn says on Twitter, I call my staging environment theory on account of how many things work in theory but not in production. And I want you to close your eyes and think about your staging environment. It probably is quite different from production. You have a different database. Your database is way smaller because you have like five test users. You could have an API in order to create a user or add, I don't know, $200 to their account. That is probably not something that you have in production. And staging is always different than production.
7. Using LaunchDarkly for Feature Flags
I strongly recommend using LaunchDarkly, a feature flag management tool. It allows you to define different feature flags in production and pre-production staging. You can enable or disable the feature flag, and the change is propagated quickly. It's great for testing new features in production and performing A-B testing. For more information, watch Talia Nasi's talk on testing in production.
And that is why I strongly recommend using stuff like LaunchDarkly. So LaunchDarkly is a feature flag management tool that does a couple of things and it does them really well. First of all, you can define different different feature flags in production and pre-production staging and so on. It's quite easy to use. And secondly, if you enable or disable the feature flag, this change is going to be propagated in a matter of, I don't know, seconds. It's very fast, it's very efficient. So for instance, you get to enable your shiny new feature in production on your test account. You can play around with it, see on production if everything is fine, if it's working against your production API. Next up, you can enable it for only for 0.01% of your userbase and then monitor quickly, what the heck is going on, whether I'm seeing any errors. Next, you can enable it for 50% of your userbase. And by the way, this is an excellent way of also doing A-B testing because you could enable a feature flag for only half of userbase and then ship it to everyone. If you would like to learn more about testing production, I strongly recommend this talk by Talia Nasi. It's available on YouTube and it goes into much detail or into how and why should you test in production.
8. Closing Thoughts on High-Quality Software
High quality in software stems from fast feedback loops. Minimizing the entire loop, not just the developer part, is crucial. Developing high-quality software is always faster than fixing production issues at 2 am. The best way to reduce bugs is to make software simpler.
I would like to close this talk by expressing this idea that in my opinion, high quality in software stems from fast feedback loops. Because if you imagine a loop of develop, shift, feedback, repeat, if you are able to minimize the time it takes to complete this entire loop, you are able to iterate much more quickly. You are able to ship much better software that is more responsive to ever changing user requirements, to ever changing market, and so on.
But the thing that I would like to emphasize here, this is about minimizing this entire loop, not only the developer part. It's not about shipping whatever to production, because then you're going to fail the feedback part. Because you will have production issues, you will have production incidents, and you will not be able to move on to this big new thing, big new feature, because you will be stuck fixing the stuff that you were supposed to ship a week ago.
In short, developing high-quality software is always faster than fixing production issues at 2 am. And I know that there were quite a lot of things in this talk, but I would like to leave you with one last final thought. This comes from a philosophy of software design by John Asterhaupt, who said that, overall, the best way to reduce bugs is to make software simpler. That is something to think about. If you find your code difficult to test, consider shipping less of it. Thank you so much for listening.
End-to-End Testing and Accessibility Resources
If you had to write one type of test, it would be end-to-end. Being your own user is important and beats any testing tool. A good resource to learn about roles and accessibility is the course on Ekher.io and Lindsay Kopacz's writings. Start with low-hanging fruit like using your website with a keyboard.
Hey, everyone. So, good, good. Do you agree? If you had to, only had the option to write one type of test, would it also be end-to-end? Absolutely. Because the idea is that if I had to add only one test, I would like to make the test resemble the experience that we are shipping to our users as closely as possible. And our users tend to use our UI, click on buttons, write text and inputs. And the more we can test that, the better. With that being said, if I were to get hired at a company and they would only get a single test per project, I would probably quit on the spot, but this is another topic I suppose.
Yeah, of course it's a hypothetical question, but it kind of feeds our knowledge of what people think and find the most important type of test to write. So that's good to know. By the way, I have to say, I love the term dogfooding or eating your own dog food. I really agree with that, and I used to work for a supermarket and like the home delivery part of that, and I was already a customer of that supermarket. And so I was really happy to be working on that and like kind of killing my own pain points, right? I worked there for a year and never got to kill any of my pain points because I'm not the project manager. But being your own user is really important. So that's a good point. And that beats in my opinion, any testing tool you can use.
We're now gonna go to the questions from our audience. And the first question is from Martin. And Martin wants to know, what's a good resource to learn about the roles and their accessibility? So there's plenty. So first of all, as I mentioned in the talk, there was this amazing course on Ekher.io on building accessible React applications. And even if you are not using React, I strongly recommend giving this course, just checking it out, because it talks about so many practices which are completely independent of of the framework. Secondly, I strongly recommend following and reading everything written by my friend, Lindsay Kopacz, who has also written a book about accessibility. And it's absolutely amazing. I've learned quite a lot from reading everything written by her and I strongly recommend that. And secondly, I think you know, accessibility is a very broad topic and it's very, I would say, it's ridiculous to be able to have an entire understanding of all of the different aspects of accessibility, because there's just so much of it. So what I would probably do and strongly recommend is to go for the low-hanging fruit first, as in try to use your website with a keyboard. And then if you are not able to, find out what you can add to your app in order to improve that. Because like reading the entire documentation, it's not probably going to be the most useful use of your time. But if you were to improve your own pain points, going back to dogfooding, then you will be able to improve the accessibility of your app. Yeah, really important point to also mention in a testing conference, if you ask me, that accessibility should always be top of mind, of course.
Next question is from Yanni Kolev.
The Value of Prettier and Default Configuration
The biggest value of prettier is not having to think about formatting options. It eliminates endless discussions and speeds up development. Using the default configuration is recommended.
What options do you find the most important and prettier? I think semicolons. As in, because prettier has some of the options, but for me the biggest value of prettier is not to think about the options, like the fact that I get to add prettier to a project and I no longer have a discussion when it comes to how do we format this type of function or how we, I don't know, whether we add comma at the end of this JSON object or not, like all those discussions are completely gone. So not only from the testing perspective, as I mentioned in the talk, it's a much faster feedback loop, but also from the experience of a developer who wants to shift stuff to production, not having those endless discussions in Kotlin feels like this is going to speed everything up. In my humble opinion, maybe this is a hot take, I don't know. I see no reason not to use prettier. Yeah. And you shouldn't configure anything. Just use the defaults and everything will fly. Man. This is actually I'm willing to die. Yeah.
Comments