The frontend tooling guide to improve Developer Experience12 min read
Let's talk about how we can use frontend tooling to improve the developer experience in our team and how these tools impact the reliability of shipping value into production safer and faster.
Have you ever been on a situation where your team had to quickly fix a bug on production or struggled to deploy a new feature reliably?
Keep reading then! All those problems usually indicate that you don't have the proper tools. In this article I'll give you the tools your team needs to create a high-velocity development environment for frontend applications.
Table of Contents
- What is Developer Experience ?
- The impact of having a great DX
- A tool for every problem
- Integrating tools on the development workflow
- Wrapping up
What is Developer Experience ?
Developer Experience in this context, is everything that happens in between programming and the outcome of the code.
Starting from the first line of code that is written on the editor and ending when the code is shipped and available to our users. Is the literal feeling a developer has when coding.
Think about all those little things that help you to code better without having to put any attention on them.
A few examples and common use cases of DX would be:
- An engineer writes code that it's not typed and a tool warns about it.
- An application that is hot reloaded every time the code changes.
- An automation that deploys your changes into production.
The impact of having a great DX
Having a great DX has a significant influence in your development speed. How? It's simple, as soon as you rely on automated proceses and tools, the people on your team will start to feel more confident.
Trust, empowers developers to be more productive and agile 🧠, forgetting about the fear of change. So you will be able to bring value much more faster to your users.
Automation is the key to build a high-velocity development team.
Think of it as giving superpowers 🦸♂️ to people. With the proper tools your team will be able to:
- Deliver value to production fast ⚡️
- Anticipate and fix bugs 🐛
- Optimise the development time of engineers ⌚️
- Build consistent and high quality codebases 🥇
- Leave fear of change behind 😍
Sounds awesome right? Let's dive into it! 🛠
A tool for every problem
Before starting to analyze what we are going to use, is important to understand that the election of a tool depends totally on the problem that needs to be addressed.
As Abraham Maslow said, on the law of the instrument:
"I suppose it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail."
The following quote 👆 is highly relevant on the programming context. Many times as software engineers we tend to use the same known tools for any new project, no matter what the constraints and requirements are.
This is a comfort zone bias. We decide to not change or even consider a new tool to avoid risk.
The problem is that we're limiting our knowledge and the ability to discover more efficient instruments, because we can't compare them against anything.
Let's start exploring the tools that will help us to build an awesome DX on our team. We will start from the ones that require less effort and provide more impact.
I've made up a little graphic to represent the impact every tool is going to have on the DX of your team against the effort it requires to be used.
Linters and formatters
Linters and formatters are essential to maintain a consistent codebase. When it comes to writing code every team member should follow the same style, rules and conventions. Consistency is key in a project.
You don't want to have different code styles depending on who's writing the code. It's better to agree on a style to avoid confusion. Think about onboarding a new team member, it's will be much easier if the coding convention is well defined with clear rules.
What's the difference then between a linter and a formatter?
Most of the time, you can use both of them for the same purpose, which is to find code errors, bugs and automatically format the code based on the given style guide. However there's a subtle difference:
- Linters: Find and catch unexpected coding errors.
- Formatters: Format and structure the code with the given style.
This is the list of linters and formatters I use that I would recommend:
- Prettier: An opinionated code formatter. Works with
.cssfiles too! 🎉
💡 Protip: Run linters and formatters on a
pre-commit git hook using Husky + lint-staged to make sure that the code is readable and formated before the code review process 😍
Since we started to use static type checkers on the team, we began to code and refactor a lot faster while feeling much more safer.
This is the list of static type checkers I use that I would recommend:
💡 Protip: You can also run
flow type checks on a
pre-commit hook basis, as suggested on linters and formatters.
Performance and accessibility audits
Performance and accessibility are two important values we should be accountable for as Software Engineers. Although this does not have a big impact on DX, the outcome is part of our programming process, so we should measure and improve our a11y and performance constantly.
These are the most relevant values for your users. Keep in mind, that whenever a user enters your site or application, the first thing they will feel is the overall performance of the site, how fast it loads, how fast is to navigate through, the way it works and integrates with screen readers etc.
So for me, makes a lot of sense using a tool to audit and monitor performance and a11y to ensure they met the standard requirements.
This is the list of tools I use for auditing performance and accessibility I would recommend:
- Lighthouse-CI: Automate running Lighthouse for every commit, viewing the changes, and preventing regressions.
💡 Protip: It's a great idea to include this audit as a step of your Pull-Request checks. We will introduce this concept on the next section.
The aim for unit tests is to ensure that individual and isolated pieces of your codebase work properly. Also they act as a layer of protection against the passage of time. As long as the test exists the behaviour of those units will be guaranteed.
So then it's impossible break those individual pieces by accident on a refactor or when introducing some changes.
Unit tests should be fast to run. Some examples and use cases where I use unit tests:
- Components: Ensure that isolated components are working as expected on their own.
- Utility functions: We usually build a lot of helpers and wrappers to create abstractions in our projects. To ensure they work as expected with all the uses cases, you should be unit testing them.
- Sagas: Audit the steps and behaviour of your asynchronous flows in your application.
This is the list of tools I use for unit testing I would recommend:
- Jest-Extended: Additional Jest matchers
Integration testing is about checking the behaviour of unit pieces grouped together. In my opinion, this is where most of your focus should be when writing tests.
Why? Considering the time and effort it takes to write tests compared to the level of confidence they give back to you, integration tests are merely the best.
Unit tests are great but they give you low confidence results, think about the umbrella example 🌂
Some examples and use cases where I use integration tests:
- User interactions: Simulate user interactions through events as if you were a user. For example, on an e-commerce site add a product to the cart and assert that an API request has been made and the product items count has been increased.
- Forms: Fill forms in different ways, simulate a form submit and validate the data, make the form fail and assert the errors.
- Navigation: When the user clicks on something, are we navigating to the expected screen?
This is the list of tools I use for integration testing I would recommend:
- Testing-Library: Simple and complete testing utilities that encourage good testing practices.
End to end testing
When it comes the time to guarantee the behaviour of key user flows on a product, for example on an E-commerce would be; an add to cart or a checkout process. E2E testing is crucial. Because you're testing on a real browser and a real device against external APIs, as if you were an genuine user.
Finally! With E2E your team would be able to sleep properly 😴, with the safety that nothing is going to brake suddenly due to a weird bug or something that hasn't been tested properly 🎉.
Be aware that since those type of tests run on real devices, you should only test the most important things for what you're building, specifically user flows and paths. Building an E2E could be a little bit time consuming at first, but the effort pays off. Luckily at the moment there are a lot of tools that makes this process a lot easier!
This is the list of tools I use for E2E I would recommend:
- Cypress: Fast, easy and reliable testing for anything that runs in a browser.
- Detox: Gray box end-to-end testing and automation framework for mobile apps.
Integrating tools on the development workflow
Now that we understand what is each tool for, I'm sure you asked yourself; How I'm going to integrate those tools on my team development workflow?
Automate all the things with a continuous integration and a continuous delivery pipeline.
As we've seen previously, we can simply run some tools automatically using git hooks and that's totally fine, but this approach has a few problems:
- Certain tools can't run on a git hook, because they could be really slow.
- Something that runs on a local machine could be skipped.
A pipeline is a way to unify all your tools into a process that does not relies on any local machine and not depends on human interaction.
This process runs on every Pull-Request and your main branch, in order to ensure any change that wants to be deployed into production, meets the quality standards and works as expected.
Based on the tools we've seen here's a representation of how our pipeline would be:
Note that tools that run and fail faster are the first ones that run on our pipeline.
💡 Protip: When building a web project, I use something called deployment previews. This is probably one of my favorite features of the whole pipeline. You can get a standalone deployment with a unique URL for each PR.
This allows you to test your changes on a safe and isolated environment. Previews speed up code reviews and help you to validate implementations and features. There are a couple of platforms that provide this feature such as Netlify and Vercel. If you're not using them you can try to build something on your own!
Here's a list of platforms you can use to build your pipelines:
Right now I'm using GitHub Actions to build my pipelines 😍. If you want to see an implementation like the one we described, you can take a look at my website pipeline. Find the implementation on the following link.
You can see that whenever I open a Pull-Request, there's a list of checks that need to be ✅ before being able to merge the changes to master:
The checks ensure that the quality standards are passed and everything works perfect 🚀.
This article is a brain dump of the concepts and knowledge I use daily on personal and professional projects, to ship software.
I know it's a lot of information to understand but I can ensure you that if you implement a similar workflow and you put attention into Developer Experience it will make a huge difference on your team.
Enjoyed the article? 😍