zacowan
All Posts

How Testing Enhances Developer Experience

Dec 1, 2024

I think everyone can think of a time in their career in software that goes something like this:

  • Code a new feature
  • Manually validate that the new feature works
  • Write tests for the new feature to meet testing requirements
  • …repeat

This was certainly me for much of my early days in my full-time career - an immense focus on “move fast and break things”, and then sprinkling in some tests for good measure. Testing has often been an afterthought for me, something to do once I was confident that everything was going to work. And as I did the tests, there was no thought behind the testing - I just did them because I had to.

As I approach 2 years of working full-time on software, I’ve experienced a distinct shift in how I perceive software testing and its impact. Testing is no longer this meaningless requirement that might ensure that my code is correct. Instead, testing has grown into this amazing developer experience tool - one that tells my team how my code works, one that gives me the confidence to make changes, and one that helps automate tedious, manual tasks.

Testing to Document Code

Tests can serve as a living, executable form of documentation. While code comments or README files can explain how a function or feature is supposed to work, they often fall out of sync with the actual implementation over time. Tests, on the other hand, explicitly define the expected behavior of a system in code and, with the right checks in place, are required to be run on a regular basis to ensure they remain up-to-date.

For example, writing tests that cover edge cases or error handling can reveal how the code should behave in less common scenarios. This is invaluable for team members (or even your future self) who need to understand or build upon existing functionality. When you’re handed a complex codebase or revisiting your own work after months, well-written tests can often serve as the clearest guide to understanding exactly how the code works.

Testing as a Change-Management Confidence Booster

When making changes to a codebase — especially in large systems — there’s always a lingering question: What did I just break? This uncertainty can lead to hesitation or overly cautious updates. Comprehensive test coverage with a robust suite of unit and integration tests transforms this anxiety into confidence.

Tests give you immediate feedback about the impact of your changes. A suite of well-thought tests lets you actually “move fast and break things”, knowing that any unintentional side effects will be caught early. Good tests act as a safety net, allowing teams to embrace iteration without fear of introducing regressions.

However, be careful with the ways in which you write tests. Broad tests that validate multiple files might accidentally execute new functionality without requiring writing a new test, resulting in the new code passing test coverage requirements, even though the functionality added by that code has not been explicitly tested.

To avoid this, prioritize testing the smallest portion of a function as possible in each test. And when testing functions which invoke other functions that already have dedicated unit tests, ensure that you mock those functions to not validate their functionality again. Lastly, integration tests can oftentimes be the culprit of broad tests that result in large amounts of code coverage requirements being met. As a result, it can be helpful to isolate the code coverage requirements based on the type of tests being run. For example, unit tests might have a 100% code coverage requirement, while integration tests (which run separately using a separate configuration) will not have a code coverage requirement.

Testing as a Validation Task Automator

Manual testing is tedious, error-prone, and time-consuming. It’s also not scalable. Every time a feature is added or a bug is fixed, revalidating the application manually eats into time that could be spent developing new features or solving more complex problems.

Automated tests step in as a productivity multiplier. They free developers from the repetitive grind of manual validation, allowing them to focus on higher-value tasks. End-to-end tests, in particular, can simulate user behavior, ensuring that the entire system works as expected without needing human intervention. Automation isn’t just about saving time — it’s about enforcing consistency and thoroughness that manual testing alone can’t achieve.

One scenario in which automated end-to-end tests have been most helpful is validating the structure of human-generated data under a set of rules. For example, let’s say that we’ve got a hand-picked list of products that we are publishing in an article including their deals for Black Friday and Cyber Monday. In addition to this list, we’ve also got a set of principles that we’d like to adhere to when publishing this list:

  1. The products must have a minimum of a 10% discount if they are above $100, otherwise they must have a minimum of a 20% discount.
  2. We only publish products which we have reviewed before.
  3. We do not publish links to products for certain retailers.

For a sufficiently small list, these are relatively simple principles to adhere to. As the list grows over time though to hundreds of products after many years of Black Friday and Cyber Monday, this becomes an impossibly difficult task. A set of end-to-end tests which validated the hand-picked list based on these 3 rules would significantly simplify the previously manually process and ensures that these principles are always followed, no matter what.

Conclusion

I hope I’ve illustrated above why testing has many benefits beyond just “validating code works”.

I look to move really quickly, so a top-notch developer experience is something I’m always on the lookout for. I’ve always believed that good developer experience meant a solid CI/CD pipeline, fast development environments, solid dependency management, and monorepos for interconnected packages (can you tell I work in TypeScript a lot). Nowadays, I’d like to expand my personal definition to include robust testing, as testing has really shown itself to be a powerful addition to a good developer experience. I hope you do to.