This post summarizes my takeaways from Platform as a Reflection of Values, a very eye-opening talk that helped me understand why I would have the same disagreements over and over with some team members in the past.

Let's imagine a universe where you and I are on a small engineering team.

I care about testing a lot. I believe the following:

  • Automated tests prevent regressions in production.
  • Easy-to-test code is easy-to-debug code. After all, a debug session is often a prelude to a test-writing session!
  • Writing out tests is an excellent way to confirm you are implementing what you think you're implementing.

You care about simplicity a lot. You might believe the following:

  • Simpler codebases have less place for bugs to hide.
  • With less moving parts, it's easier to implement bug fixes once we know what a bug actually is.
  • Simpler designs are simpler to implement, and thus simpler to get out in front of users.

Both of us think tests are good! Both of us think that simple code is better than complicated code! To paraphrase Cantrill, nobody wakes up in the morning wanting their code to be slow, hard to read, hard to debug, brittle, and hard to test!

But while we both think that good things are good, our core values differ. What we spend our time thinking deeply about and pushing for differ. In practice this means that, when faced with choosing how to prioritize work, I won't choose what you would choose.

In this universe, you send in a bunch of code in a direct style. I have to go in and "fix" your code changes, adding dependency injection or splitting out I/O from logic to make it easier to test. In this process I introduce regressions (that weren't caught because rigorous tests weren't in place!)

In this same universe, you have gone from idea to code to "ready to deploy" more or less instantly. Your code doesn't have bugs. Not "I think there's no bugs" but there are no bugs. Yet my reply is to say "make this way less readable to prove the point." Insisting on introducing unit tests that "test the implementation". And at the end of all this some 5-line function is now a 25-line function with 2 levels of indirection hidden inside.

I write service layer upon service layer, and you have to piece it all together just to get something that goes from A-to-B in a straight line. Confusion about how the service layers interact lead to features being shipped completely off-spec, because the 10-line spec ends up needing to touch 5 different files*.

You write the same traversal logic in 6 different places, without realizing it's the same thing. I have to refactor it when there's a logic bug. I can introduce tests for the traversal, but have a hard time grasping whether these are the semantics you expected.


Each incident is resolvable. But the underlying cause of these incidents is the gap in values between us. We can tackle each issue as it comes up, but how many times do you want to have the same argument? Treating symptoms without getting at the cause can be frustrating, but it's even more frustrating if the causes are purely subjective!

A team has a set of values, and members of those teams have values. If everyone is in perfect alignment, you might argue that there are blind spots. But if people are highly performant along those axes, then the blind spots almost don't matter!

Meanwhile, if you have a team of 2 people, and they have a huge values gap, their job now becomes a tug-of-war, on top of the normal work of building things.

In practice, if you have a small team that hasn't torn itself apart, it's very likely that the members of that team have good values alignment.


In larger teams, it's unlikely there is super strong values alignment across the entire team. But you will likely have pockets of people who share values. The end result? Some parts of the team will contribute a lot to making your code base testable, other groups will be able to firefight effectively, others will be able to turn "feature idea" to code in record time. And nobody will feel alone, even if there are still going to be big fights from time to time.

And in these larger teams, you can build up structures and ways to deal with gaps in values. More formalized coordination work can mean that emotional discussions can turn into simply pointing at a spec, or a timeline, or some KPI, to make your point. Values formalization is a lot of work, but it means that you can deal with differing values a bit more comfortably.


In a smaller team, and in smaller companies, not being honest about one's values can be fatal. A small team that has survived likely has strong values alignment. So when a new person joins and doesn't align well, every misalignment is accentuated. It's no longer "one on one", it's "one vs four". There is a strong amount of rejection, and a smaller team is less likely to have at least some fellow travelers on some issues.

There will be strong pushback on things that the new person cares deeply about. Usually these discussions fall into arguments about prioritisation. "We can do that later" is a common refrain, but in these conversations "later" really means "never". At least not with the current team composition.

And the end result is a bunch of people frustrated, because nobody is wrong. They just don't have the same values.

People will talk about wanting people with different skillsets in their teams, to help improve the overall skills of the team. Skills and values are different, but generally people are good at things they care about. It's not clear to me that small teams can absorb the costs of differing values.


1: As someone who does care about testing, I do feel it worth pointing out that some people are really good at writing code that doesn't need deep testing. These people exist, though fortunately they are also willing to at least play ball with writing test code. Back