Why TDD matters for product managers, product owners, and stakeholders

Co-authored by the badass Diana Villanueva

Test Driven Development (TDD) is a software development technique practiced by a portion of the developer community. In this article we are going to focus on the benefits TDD provides for product managers, product owners, and stakeholders. If you’re managing a project that is written without TDD, you’ll be missing out on a lot of the benefits we discuss below. And if you’re unfamiliar with TDD and managing a project that is developed without TDD, you’ll be able to notice some of the common problems we’ll highlight below, but you wouldn’t be able to suggest TDD as a solution. So it’s important that you learn how TDD can benefit your project and why your development team should be using it.

What does TDD look like?

Imagine you want to deliver a new feature that allows existing users to login. In a typical development approach, developers generate the login functionality without any automated tests and report the feature as done. With a TDD approach, developers write a test first. The test would say something like: “When the user exists and they log in with the correct username and password, they’re taken to their dashboard.” This test would immediately fail because the login functionality has not been built yet. Then, the developer would build the functionality that satisfies the test and the test would pass.

What are the benefits?

Before we jump into TDD, we’ll talk about the benefits of automated tests. What’s the difference? If the developer used the traditional method for the login feature, then wrote a test to check that it worked, that would be an automated test without TDD. It’s only when you write your automated test before the feature that you’re practicing TDD.

Automatic Tests help with:

Regression and Bugs

Imagine you spend the whole day doing chores around the house and at the end of the day you realize you don’t remember where you left your keys, but know you had them at the beginning of the day. Now, you have to think back through the whole day and look everywhere you’ve been. Now imagine a different scenario where you check for your keys every five minutes. In this scenario, when you lose your keys you only have to think back the last five minutes. You’re going to find them much faster.

Automated tests work the same way. If you discover a bug in your production environment, it’s much easier to find and fix it if you had a way of knowing that it worked five minutes ago: “Whatever we did in the last minutes caused the problem.” Automated test are fast and repeatable. You can build an automated test suite that tests your entire app every five minutes. Automated tests on new features can catch issues before they’re released to production. If a problem does reach production, automated tests reduce the surface area of the problem space. The more automated tests you have, the less untested code there is to comb through.

To summarize, automated tests can quickly and frequently check that features work. The more frequently you check, the sooner you find and fix problems. The faster you fix problems, the more your developers can focus on building new features that deliver new user value.

Confidence to change code and deploy

You want to focus on writing stories that deliver user value, regardless of what part of the application it touches. With the traditional method of development, a lot of developer behavior is motivated by fear. If a new feature requires too much change, there’s concern it will break existing features. There may be nasty parts of the code base that nobody is willing to touch because if it breaks, it would take a while to notice or have devastating effects. When fear interferes with quality, features will take longer to finish. Developers will think of creative ways to avoid interacting with the scary part of the code. These creative ways can’t take a direct path through the bad code so they’re harder to understand by others. This is why bad code tends to cause a downward spiral of quality unless addressed. The best way to address the problem is to surround it with enough automated tests that developers feel confident it can be changed without breaking any functionality.

If you’ve ever been in this situation, you may have felt pushback and pressure to prioritize different features by developers. But if there was a trusted test suite in place, this would not be a major concern. The automated tests would tell you and the developer that the existing features still work.

This also gives you an alternative to the dreaded “rewrite”. Rewrites are expensive and difficult to pull off. There’s no guarantee the rewrite will turn out to be better quality. You don’t need to rewrite the app if you can confidently change it to whatever you want.

In addition, when you trust your automated test suite, you have a lot of confidence that your features work because they’ve all been tested. This confidence allows you to deploy to production frequently without stress. No more all nighters.


Here is a problem we often encounter: You have documentation that says when you put in a coupon, you get a discount. If the application code changes this behavior, the documentation will have incorrect information unless someone actively combs through it to keep it up to date. Keeping the documentation in sync with reality is expensive and not a huge value add relative to new features. In this context, it can be pragmatic to keep the documentation out of date. But, when someone reads it later they’ll be misled by its information. This makes people question its accuracy and when people are skeptical of the documentation, they tend to not read it. In the end, a lot of the effort to write it in the first place is wasted.

Let’s compare this to automated tests: If the coupon code stops working, the tests fail. Automated tests tell developers when they’re incorrect, traditional documentation does not. Automated tests act as a form of developer documentation that are always up to date. This helps velocity stay consistent even when new team members join the team.

TDD is automated tests on steroids

With TDD, you’ll get all the benefits mentioned above, plus these additional ones:

Better test coverage

When developers write automated tests after the feature works, it’s easy for them to forget to test some functionality because they have to think back and go through every possible path. When forgotten paths are discovered later, developers lose confidence in their entire test suite. They think, “What else did I miss?” This puts the team in a “worst of both worlds” situation where they put effort into automated tests but don’t trust them so they spend effort on manual testing, too. It’s a skill to write testable code and the most straightforward way to write a feature isn’t usually testable. Therefore when developers write the feature first, the code must often be modified so automated tests can be written against it. This creates a “measure once, cut twice” situation where the developer builds the feature, tries to test it, but then realizes they have to rewrite the feature in order to do so. Then time constraints cause developers to skip a lot of tests they should be backfilling.

When developers practice TDD, they write the test first, then write the minimal amount of code to make it pass. It bypasses all these issues. They’re sure that all their code is tested: It wouldn’t be there unless there was a test saying it needed to be. When you don’t practice TDD, you have to manually check the application to get the same confidence in it.

Deliver user value with simple solutions.

By writing a test first, developers constrain themselves to write the minimal amount of code to make the test pass. This focus prevents the common problem of over-engineering: Writing overly complex solutions when a simple one would suffice. Simple, minimalistic solutions maintain high quality. They’re easier to understand and there’s less to read through. This means they’re easier to maintain and change than complex, ornate solutions.

Easier for other apps to integrate with yours.

When other systems work with yours, the developers of the other teams will appreciate convenient APIs. This will make your product more appealing and people will be more willing to leverage it. Test Driven code is usually more pleasant to use. That’s because when the test is written, the developer has to think, “I wish I had some code that I could call like this, even though I don’t.” That’s exactly how anyone integrating with your APIs are going to think. In other words, TDD code is written the way people want to use it. Using the traditional or the test-after approach doesn’t usually work this way. Instead, the focus is on how to make the feature the easiest to implement, and that’s not always the most convenient way to use it. As a result, the design of the code becomes an afterthought.

PM and Dev agreement on Done.

TDD encourages and documents conversations about the definition of “done”. If the feature says, “when the user enters the correct coupon code, the discount is %15”, the developer can write a test that says exactly that. TDD makes this test the first step for the developer so the conversation has to happen early. With traditional processes, there’s a tendency for these conversations to happen after the feature is already written. It’s very expensive to realize the devs and PM are out of sync at this point because the feature may have to be rewritten and retested.

Hopefully these examples informed you of the value of TDD for your product. TDD is not only a developer tool, it is a methodology that helps provide better test coverage which in return increases confidence in your product and deployments. It focuses developers on the minimum amount of code required to deliver user value. It ensures a consistent velocity over time by eliminating unnecessary feature rewrites and reducing time on bugs.

Will you write less tests with Integrated or Unit tests?

In this article I’m going to talk about Unit/Solitary/Mockist/London Style tests and compare them to Integrated/Social/Classicist/Detroit Style tests. This won’t be a thorough review. I’m actually going to focus on one specific argument: If you choose one over the other, will you write more or less tests? There are many other important aspects worth considering (the speed of your tests, the confidence they give you, the way they impact your code design, etc.), but I’m just going to focus on the number of tests you have to write.

If you’re unfamiliar with these concepts, the terminology in Martin Fowler’s Unit Test article can be a good primer. Here’s an image for reference: Solitary vs Sociable

The reason I’m writing this article is because I saw a video a few years ago called “Integrated Tests Are A Scam”.

J.B. Rainsberger – Integrated Tests Are A Scam from devtraining on Vimeo.

The main theme of this video is that integrated (AKA Sociable) tests are generally inferior to mockist/unit/Solitary tests and one of J.B. Rainsberger’s arguments is that it doesn’t make sense to use integrated tests when you think about things mathematically. Here’s some code to explain the argument:

If we wanted to completely test all the paths, how many tests would we have to write? Well if we’re writing mockist style unit tests, we’d have to write 6:

  • When testing A, we’d mock out B and write a test for when maybeEven is even and another test for when maybeEven is odd.
  • When testing B, we’d mock out C and write a test for when maybeFavoriteNumber is your favorite number and another test for when maybeFavoriteNumber isn’t.
  • When testing C, we’d write a test for when maybeFactorial is a factorial and another test for when maybeFactorial isn’t.

But, if we were writing integrated tests, we’d have to write 8 tests. Each of these would test A‘s execute method and the input would have to satisfy all these conditions:

  • even? == true, favoriteNumber? == true, factorial? == true
  • even? == true, favoriteNumber? == false, factorial? == false
  • even? == true, favoriteNumber? == false, factorial? == true
  • even? == true, favoriteNumber? == true, factorial? == false
  • even? == false, favoriteNumber? == false, factorial? == true
  • even? == false, favoriteNumber? == false, factorial? == false
  • even? == false, favoriteNumber? == true, factorial? == true
  • even? == false, favoriteNumber? == true, factorial? == false

The more classes and paths you have, the more integrated tests you have to write relative to the unit tests. This argument never felt right to me but it took over a year for me to figure out why. The scenario assumes that both branch paths lead into the same class and method. That’s not always true. What if your code looks like this?

In this scenario, each branch leads to a different class instead of the same class. How many unit tests will we have to write to test all paths? Just like before, you’d have to write 6 tests:

  • When testing X, we’d mock out Y/Z and write a test for when maybeEven is even and another test for when maybeEven is odd.
  • When testing Y, we’d write a test for when maybeFavoriteNumber is your favorite number and another test for when maybeFavoriteNumber isn’t.
  • When testing Z, we’d write a test for when maybeFactorial is a factorial and another test for when maybeFactorial isn’t.

But, how many integrated tests would we have to write?

  • even? == true, favoriteNumber? == true
  • even? == true, favoriteNumber? == false
  • even? == false, factorial? == true
  • even? == false, factorial? == false

Now we only have to write 4 integrated tests. If you look at the first two, factorial? is irrelevant because execution doesn’t go into Z when the number is even . If you look at the last two, favoriteNumber? is irrelevant because execution doesn’t go into Y if the number is odd.

Back to the original question: Will you write less tests with Integrated or Unit tests? It depends on the shape of your code. If your conditional branches converge into the same class and methods, you’ll write less tests with unit tests. If your conditional branches diverge into different classes and methods, you’ll write less tests with integrated tests.

6 reasons you should NOT rotate developers onto new projects frequently

I believe that there are a lot of good reasons to rotate the developers at your company onto other projects more frequently than you already are. What I mean by “rotate” is that a team member is effectively moved from one project to another. This is usually in the form of a trade. A developer from one project switches places with the developer of another project. They’re still around to help anyone working on the old project, but it is no longer their primary roll.

Just how frequently am I suggesting that developers should rotate onto new projects? In my opinion, a developer should be on a new project every 6 to 12 months. But this is something your company should experiment with to find an ideal length of time.

I wrote about the benefits in my previous article. I don’t want to act like this is all advantages so in this article I’ll be pointing out the downsides:

Developers may not see the ramifications of their choices

I’ve worked with people who I would say rotate onto new projects too frequently. As a result, they never were able to see the ramifications of their choices. They may have pushed hard for a new pattern of testing that did not work out well in the end. If they stayed on the project the whole time, they would feel the pain of choices and learn from their mistakes. If rotated too frequently, they may never see the problems that were caused.

This is why I suggest a rotation time that is long enough for people to see the ramifications of their choices but short enough so that developers can learn new things and share context before it’s too late.

Frequent rotation can make people stop caring

Even if developers are rotating from team to team, that developer should feel a sense of ownership of the project while they’re working on it (even better, the whole team should feel ownership). If you rotate developers too frequently, they may not feel responsible for the project they’re working on. They may feel like guests in the code base instead of owners.

This problem can be exacerbated if a deadline is given and the developer knows they are rolling off long before that deadline.

I would say this is a good reason to plan a rotating schedule around events instead of instituting a schedule around time.

The rotation may come at a bad time

There’s rarely a perfect time to rotate developers on and off a project. It may be disruptive to the team or the individual. This is why it’s important to share context properly before anyone is rotated on or off a project. Without this upfront effort, the rotation is going to cost a lot more than it needs to.

It takes time to share context

When a developer moves to a new project they now have to become familiar with the code base. This costs time and therefore money. The rest of the team also has to slow down to communicate concepts to the new developer that they are already familiar with. I’ve written about ways to reduce this cost, but it can’t be eliminated.

This is another reason you should avoid rotating too frequently. But, there is a time where the cost of sharing context is worth the risk of a small bus factor number.

Developers may like “owning” a project

A lot of developers consider their project their baby and don’t like the idea of anyone but themselves working on it. When one is in this state of mind, they are not going to appreciate being rotated off their project. Therefore, rotating onto new projects regularly will demoralize these developers.

Rotating onto a low quality project is not fun

If nobody rotated to different projects, each project could stay as its own island of quality. Some would be high quality and some would be low. But if you were working on a high quality project and you rotate onto a low quality project, this can feel like a downgrade. Of course, others may see it as an opportunity to improve the quality of a new code base, but most developers don’t consider that to be a pleasurable experience.

If the rest of the team has a low bar for quality, it can feel like one against all if the new developer tries to improve the code base and nobody else sees a problem with it.

The best way I’ve seen to deal with this problem takes a few steps. First, you must identify a project as low quality and another project as high quality. Then, half of the low quality team should be rotated with high quality developers. This way, it’s not a one against all situation anymore but there should be enough people left on the low quality project to share context. I suggest this because it is really difficult for one person to turn the tide against a whole team.

Although I feel like most companies should rotate their developers onto new teams every 6-12 months, I do acknowledge that this comes at a cost. With these tips, you can avoid the pitfalls of rotating too frequently, at the wrong time, and rotating between projects of different quality.

8 reasons you should rotate developers onto new projects regularly

I believe that there are a lot of good reasons to rotate the developers at your company onto other projects more frequently than you already are. What I mean by “rotate” is that a team member is effectively moved from one project to another. This is usually in the form of a trade. A developer from one project switches places with the developer of another project. They’re still around to help anyone working on the old project, but it is no longer their primary roll.

Just how frequently am I suggesting that developers should rotate onto new projects? In my opinion, a developer should be on a new project every 6 to 12 months. But this is something your company should experiment with to find an ideal length of time.

I don’t want to act like rotating developers is all advantages. But I shall discuss the disadvantages in a future article. Here’s why you should rotate your developers onto different projects:

Rotating developers helps them understand the big picture

Lets go extreme and imagine that one of your developers worked on every single project at your company. They would be intimately aware of how all the pieces fit together. They’d understand why a request to another team is reasonable or unreasonable given their constraints. They would have insight into the best place a feature to be implemented and be able to break down high level requirements into low level stories that are distributed to the rest of the teams. And, the chances that all the pieces would fit together in the end would be improved.

Consider that not all projects are user facing. Some projects are merely APIs for other projects. The people stuck on these API projects have a difficult time seeing how the features they are writing are providing value to the end users. Rotating these developers onto user facing projects will give them empathy for the customers and help them make decisions on their behalf. This decision making advantage will carry oven even if they are rotated back onto an API project. If a developer from a user facing project rotates onto an API project, they can bring a lot insight to the rest of the team for the same reason.

Rotating helps with a shared context

There’s a company maturity level required to even be able to rotate a developer from one project to another. Unless there’s a good support structure (documentation, onboarding, pair programming, etc.), a rotation will not go well. This support structure requires a way to share context. Without this – worst case scenario – it will be as if both developers quit. This would occur if you only had one person teams and after rotation, the two people never communicated with each other. Without being able to share context about where they left off, the ramp up time for being effective on their new projects will be astronomical.

The reality is that any developer is free to quit any time they want. So avoiding rotation does not avoid this concern, it just stops you from dealing with it until it’s too late. Better to deal with this risk head on today than at the worst possible moment tomorrow.

Rotating increases collaboration

With a large enough company it can become possible for projects to depend on each other with limited collaboration. This is a problem and here are a few symptoms of it:

  1. Work that is frequently blocked waiting for other teams
  2. Components that don’t fit together (e.g., an API nobody uses)
  3. Features that nobody uses

Frequent rotation will help alleviate these problems. It will also give developers direct contacts with people in other projects and that can pay off in unexpected ways.

Rotating empowers anyone to fix issues on any project

Once a developer has worked on a project, they will feel comfortable fixing issues on that project. When a company does not rotate developers this is not the case. When a project has production issues the developers of other projects wait for the developers of that project to fix it. This doesn’t have to be the case. With sufficient trust and collaboration, any developer could feel empowered to fix any production issue regardless of the project.

Rotating can reduce employee churn rate

A lot of developers start to look for a new job when they feel their current job has stagnated. It’s no fun to know everything you’re going to do day in and day out for years to come. Another reason developers quit is that they want to work with new people or learn a new technology. Rotating gives a developer all of those opportunities with very little downsides (relative to having an employee quit).

Some projects are less fun than others. If a developer is stuck on a bad project and it feels permanent, they’re likely to look for a new job. But if a new project is 6 months away, the bad project will be temporary and an employee is more likely to stick it out.

Rotating is an opportunity to learn

Having a developer stuck on a project for years is like having a developer live on a little knowledge island. They are learning things that would be valuable to share with others. Others are learning things that should be shared with them. When the organization structures these communication boundaries so developers can’t share experiences, everyone is left to their own devices to learn.

This is inefficient because some islands that others have already learned. Some islands may never learn a lesson that they should.

Rotating gives developers an opportunity to improve code quality

I’m working on a project right now that has had over 4 people rotate on. Each time a new person rolls on, they point out a low hanging fruit for improving the quality and stability of the code base in their first week. Why does this keep happening? Because a pair of fresh eyes can easily see the eyesores that the rest of the team has learned to adapt to over time.

When developers know they will be rotating onto new projects and responsible for sharing context before they leave, they’re going to be encouraged to write maintainable code. It’s far too common for a developer to silo knowledge so that nobody can understand it but them. Frequent rotation reduces this risk by addressing it earlier.

Rotating can reduce ego

Pride can be a good thing when it improves code quality. But ego can get in the way of code quality and cause developers to silo the knowledge of their project. Frequent rotation reduces this problem to a 6 month period because that’s the longest that an individual can “own” their code. I’ve discussed in other articles why code ownership is a bad thing.

I will say that there are more effective ways to deal with this issue (pair programming comes to mind).

Hopefully these advantages have convinced you that your company should be rotating developers onto new projects more frequently. Feel free to reach out to me if you have any questions or concerns.

I’m OK with some DRY violations. Here’s why

If you haven’t heard of DRY, it stands for, “Don’t Repeat Yourself” and it says “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.”

I first learned about this principle in The Pragmatic Programmer and immediately connected with it. I think the reason why is that it was so easy to apply compared to all the other software engineering principles. Most principles are very abstract but this one is easy to follow. When you see two methods that are similar, combine the methods into one and both places should use the single method.

The benefit of fixing DRY violations is that if the code is copied and pasted in two places and then you discover a bug in one place, you may forget to fix it in the other place. You spent all the time fixing it the first time and now you’re going to spend all this time fixing it the second time. Bugs are very expensive and this could have been prevented the second time if you DRY’d up the code. Even if you realize you need to fix the bug in two methods upfront, you still have to put in the effort to fix it twice.

Also, we spend much more time reading code than writing it. If there is duplicate code, there is even more code to read to understand the code base.

But, I rarely find two identical methods. Usually it’s more that two methods are a close match instead of an exact match. To merge the two into one, I have to add a flag on the outside to say conditionally skip a part of the similarities. Or, I have to extract a part of one of the methods and that new extraction is now a match with another method.

Don’t get me wrong. This often has a lot of benefits. But, these little compromises make the code slightly more complicated. It’s even worse when the functionality of the DRY’d method now needs to diverge in different ways depending on who’s calling it. Sometimes this works out, but other times you wish you were working with two different methods instead of one shared one. In my experience, it takes a long time to realize that a merged method should really be two separate methods. And until you figure this out, the code tends to get much more complex than it even would have if you left the methods as duplicates.

Lets summarize so far: DRY violations are bad because they increase the size of your code base and can be a cause of bugs. But when you fix these DRY violations your code can also become more complicated and therefore difficult to read.

When I’m practicing TDD (Test Driven Development), DRY violations are low on my list of things to deal with. When you’re practicing TDD by the book, these types of bugs are usually caught by a failing acceptance test. An acceptance test tells me if the feature works or fails. That gives me the luxury to keep the DRY violations around for longer than I’d feel comfortable with in an untested code base even if the bug is duplicated in two methods.

TDD also tends to increase code quality much more than fixing DRY violations does. It’s a false dichotomy, but if I had to choose TDD over fixing DRY violations, I’d choose TDD. This is why I’m comfortable leaving DRY violations around unless a test is driving me towards fixing them.

That’s why there’s a guideline we go by where I work. We avoid fixing questionable DRY violations until there are 3 duplicates. With a well tested code base, the advantages of fixing DRY violations don’t necessarily outweigh the disadvantages. That’s why we often wait and see.