When building physical structures, engineers often use tools such as CAD/CAM modelling software, Finite Element Analysis (FEA) simulations, and resource planning software to assist in designing and delivering a project. These tools help engineers to design and build projects faster.
Software development is no different. In software, the tools used go beyond just the code editor which developers write in. Software tools allows for checks and corrections on code quality and style. They can run tests, simulating interacting with the software just as a real user would, to ensure that features continue to work as expected. They can monitor the performance of the application after it is deployed to catch issues faster. These tools are automated and can be run frequently when developers are making changes to a codebase, allowing for faster feedback than manual quality assurance processes.
Any improvement must be seen as an investment. It will cost time and money, in developer hours to implement them and potentially ongoing subscription fees if you use commercial tools. As with any investment, you should be seeking a return. This return comes in the form of development time saved - your tools ensure a higher quality software codebase and therefore less time spent fixing regressions and working on new features.
The impact of investing in these tools can be exponential. The bigger your team is, the more impact tooling investment will have on development velocity. The higher the development velocity, the faster your team can build the product. Building product faster means winning more contracts or attaining users faster to bring in revenue. More revenue means the team can be scaled. Scaling the team means more investment in tooling can be made.
In this article, we'll focus specifically on frontend tooling, that is, tools which assist in developing user interfaces of applications which your end-users will directly interact with. Most of this tooling is focused on web-based frontends, but many of the tools and concepts also apply to native application interfaces.
What to invest in
So how can we prevent these issues?
In the scenario above, John's change would have caused a compiler error and the change would have been caught earlier. No time would have been spent on investigating the production incident.
Teams building software at scale are consistently implementing static type checking tooling. Stripe has developed Sorbet for Ruby. Dropbox runs type checking on their Python codebases. The investment is also worth making for smaller scale teams too, as type checking libraries such as TypeScript and Sorbet are open source and easier to integrate in your project. Larger technology companies like Microsoft and Stripe have done the hard work of authoring and maintaining the type checking libraries.
Code linting and formatting
Linting tools enforce consistent code quality and style. A common frontend development example which linting solves is as follows:
- These extra log statements are not picked up during a code review and are then merged in to the production codebase
- When debugging code in production or building new features, these log statements make it more difficult for other developers to work with the application as the log is polluted with additional unneeded statements
A linter such as ESLint could be run configured with a rule which flags console.log statements to automatically catch this issue during development and code review (here is the specific ESLint no-console rule), preventing the production codebase quality from decreasing due to this.
Furthermore, code formatters like Prettier enforce a consistent code format automatically between team members, ensuring that minor formatting issues don't waste time during code reviews.
Linting won't solve all code quality issues and we always recommend a manual code review process, but it can catch certain kinds of errors such as the above scenario and overall speed up the code review process.
User interfaces can be decomposed into components. A screen on your application might consist of a navigation bar, some tables to display information, and some buttons. Components could be more complex too; a form with a dynamic set of fields, or a 3D interactive graph. The different views in your application can be thought of as simply a combination of components.
Component-based design means developing these components in a way that they can be reused throughout your application. Implementing this increases development velocity as new screens are faster to build - developers will combine components from an existing library together rather than building from scratch or reimplementing.
Using a web framework such as React allows for component-based design to be achieved.
An advantage of using component-based design is that developers can work on components independently to building out screens of your application. A component workbench tool such as Storybook is fantastic to enable this. Storybook allows components to be developed in isolation, reducing the time it takes to get feedback on edits.
A component workbench also allows for more robust UI components to be developed, as it is easier to simulate different UI states while the component is isolated from the rest of the application.
At Exponent an additional particular benefit we have found of using component workbenches is that it prevents developers from recreating components which already exist in the application, as it is easy to see which components have already been built by other developers on the team.
When opting for component-based design, your team can utilise component testing to improve the quality of your components. Writing unit tests for more complex components will ensure that code changes do not cause unexpected changes in component behaviour. We use React Testing Library to develop unit tests for more complex components. React Testing Library makes it easy to use and inspect components in a similar way to how users interact with your application, so your tests will be more closely aligned to how end-users use the components.
Visual regression testing
A "visual regression" occurs when a change is made to a codebase which changes how the application looks in an unexpected way. For example, a developer may make an edit to the menu font size of an application. Unbeknownst to the developer, modifying the font has caused a menu item on a particular screen to be pushed off the page, making it inaccessible to users.
Traditionally, software QA teams and testers would manually inspect an application after a change is made to minimise visual regressions. Modern web applications require testing on different browsers and viewports (desktop, and mobile, for example). The more complex your application is and the more screens it has, the more time consuming and expensive this process becomes.
Investing in automated tools to perform visual regression tests allows this issue to be mitigated and minimises the amount of manual QA required on changes.
Tools such as percy.io allow visual regressions to be tested and inspected quickly for each change, minimising the amount of manual inspection time required. These tools render screenshots of your application and detect changes in the resulting images. These changes can then be manually approved by your QA team.
Traditionally, after a change is made to a software codebase, a manual tester would run through a testing plan on the change to determine whether the software is still fully functional. This test plan might include clicking through several "user flows" on the app, simulating pathways which the user may take to ensure that core functionality is still working.
The problem with this testing strategy is that the amount of time required for manual testing increases the more complex that the software becomes. The more features that are added, the more user flows there are which must be tested. If many developers are making changes at once, these might be required to be consolidated all together into a large release to be tested at once manually. This reduces development velocity.
An alternative is to invest in automated tooling to run "end-to-end" tests, replacing manual testing in a codebase. End-to-end testing means interacting with the full application the same way that a manual tester would.
Advantages of automated end-to-end testing include:
- Tests run faster than manual testing
- Because tests run faster they can be run more frequently
- Once a test has been written it can be executed as part of a suite every single test run
- Tests can be run on different browsers and mobile or desktop screen sizes
- Tests can be run in development to give developers faster feedback on whether their changes have broken anything
At Exponent we have been using Cypress on most of our web application projects to replace manual testing. Playwright, an open source tool developed by Microsoft, is another interesting framework that we're looking at.
Error and performance monitoring
Once your application is live and your users are interacting with it, how do you know if something is broken? One way is to rely on your users, whom will send you feedback and let you know if there are issues which stop them using the application. This method is slow and time-consuming as it requires a back-and-forth conversation with a user to determine exactly what is broken and how they encountered the issue.
A faster way to get feedback in production is to capture errors and notify your team automatically, using a tool such as Sentry. These tools allow additional user context to be captured, such as the precise page that the user was on and even a printout of the code that was executing at the time.
Furthermore, instrumenting your frontend with performance monitoring tools allows your team to understand how fast or slow each part of your application is.
How much to invest
The amount of investment in tooling should of course depend on the size of the team and the project. Implementing too many tools with a small team will slow development velocity down rather than speed it up. Not only will your team need to learn how to use each tool, each tool does add a small ongoing maintenance burden to the project. For example, poor quality end-to-end tests will be flaky and break frequently. A developer will now need to spend extra time tweaking and fixing the tests when they are working on a new feature.
So, strike a balance and "right size" your tooling investment. At Exponent we generally have teams of 2-3 developers and a designer working on projects. Our frontend tooling stack is generally:
- Component based design (React)
- Static typing (TypeScript)
- Code linting and formatting (ESLint + Prettier)
- Component workbench (Storybook)
- End-to-end testing (Cypress)
- Error and performance monitoring (Sentry)
This is the maximum maintenance burden we're willing to take on for the maximum productivity increase. With our smaller project teams, we push for more end-to-end testing against real versions of our backend services, and focus less on testing individual components or visual regressions. With a larger team, especially if you are developing an internal component library to be shared throughout multiple applications, other tools such as component and visual testing are worthwhile investments.
How to invest
Aim to invest as early as possible with your tooling. On most of our projects at Exponent we integrate our basic tooling stack when we kick off a project and set up tools such as linters and testing to run automatically whenever a code change is made. The earlier you invest, the faster it takes to implement a given tool, as there is less migration or changes that need to be made on your existing codebase. That being said, balance this with the advice above on "right sizing" your tooling investment.
A strategy to continually improve your tooling is as follows:
- create a designated document or task list of tooling improvements to make
- prioritise these improvements based on effort, risk, and impact
- schedule and allocate small amounts of time regularly to improve tooling (the amount of time will depend on the size of your team and application complexity - for our smaller development teams we typically allocate a couple of hours per week)
- include documentation on how to run and use tools as part of the development documentation
Development Tooling can be complex and it's important to make informed decisions before investing time and money into tools. The landscape of frontend tooling in particular is rapidly changing, with new state of the art technologies being released every year. If you'd like to discuss how some of the technology mentioned in this article might benefit your team, get in touch with me to have a chat.
Main image credit: unsplash.com.