Emilio Carrión
Why do we write tests? (The CTO who didn't get it)
Software that doesn't solve the problem it was created for is worthless. Discover why tests are the only guarantee of value.
We often debate about architectures, design patterns, system design... And those are incredibly important topics, but they're not the important part of software.
Because why do we create software in the first place? What do we get paid for?
The answer is simple: to solve problems. With software we create value by solving different problems within a business. Maybe we generate more revenue, maybe we reduce the cost of doing things... But at the end of the day, we create software to make an impact.
And keeping this in mind is the most important thing. Software that doesn't solve the problem it was created for is worthless, no matter how elegant its architecture is.
The CTO who didn't get tests
And that's precisely why it surprises me so much when I encounter teams that completely ignore this.
Throughout my career I've met many types of developers and many types of companies. I've met great tinkerers and great professionals. And in that diverse ecosystem, I've found more often than I'd like those strange beings who don't write tests.
A colleague told me that one day his previous CTO walked up to him with a puzzled look: "Tests? What for, right?". My colleague didn't last much longer at that company.
WHAT DO YOU MEAN "WHAT FOR"?? To guarantee the ONLY reason we write software: to guarantee that it solves the problem it was created to solve.
We're not even getting into the discussion about defects (or bugs), whether we have more or fewer edge cases that could cause problems. Simply the fact of validating that our software actually works as we expect it to and delivers the value it's supposed to deliver is already one of the most important things in building software.
Without tests, you can't say with certainty that your code does what it should. Period.
Manual tests: expensive and unreliable
Digging a bit deeper into my colleague's case, I got him to admit that they did run tests. But they were the kind we call expensive. They basically had several people manually testing the application to make sure everything worked correctly. Tests, yes, but the most expensive kind possible.
At that point the discussion shifts to cost management. If a person has to manually test every version of our software before we ship it, those tests will be very expensive. And if they're that expensive, they'll end up being done rarely and poorly.
But watch out: tests are only cheap if the code is easy to test. If your architecture forces you to fight with a thousand mocks, the problem is the design. To write code that's testable by default, I recommend applying these 9 object-oriented design principles:
The investment that's always worth it
Think of it this way: every automated test you write is like hiring a robot that will work for you for free for the entire life of the project. Every time you make a change, every time you deploy, that army of silent robots will verify you haven't broken anything.
Do you invest time upfront? Yes. Is it worth it? Absolutely.
The alternative is constantly praying that the change you made at 3 PM doesn't break something critical in production at 9 PM. And trust me, that robot you wrote once is much cheaper than waking up at 2 AM to fix a production bug.
"But I don't have time to write tests"
I've heard that exact phrase 47 times in my career. You know how many times those same teams had time to fix production bugs at 2 AM? All 47.
The reality is you don't have time NOT to write tests. Every minute you invest in automated tests saves you hours of debugging, crisis meetings, and emergency patches.
Enjoying what you read?
Join other engineers who receive reflections on career, leadership, and technology every week.
The conclusion is simple
Software creates value, it solves problems. And being able to guarantee with certainty that it actually does so is one of the most (if not the most) important things in our craft. Test, and don't do it manually.
Because your code without tests is like a pilot flying without instruments. You might reach your destination... or you might not. Do you really want to take that chance?
Next Step: The TACTICAL Testing GUIDE
Alright, this post focuses on the "why." Why tests are non-negotiable to guarantee that our software delivers value.
But once we're convinced of the why (and that doing it manually is a bad idea), the next logical question is... how?
Are all tests the same? Does it cost the same to write a test that validates a complete login flow as one that tests a simple calculation function?
To answer this, I've recorded my latest video. It's the tactical guide you need to understand the cost-benefit differences between the various types of tests.
In this video we dive into the code and analyze the three main levels of the testing pyramid, looking at their pros and cons:
- Unit Tests: The fastest and "cheapest" to create and run. They're perfect for testing pure business logic (like a function that calculates a price) without external dependencies.
- Integration Tests: One step up in cost. We use them when we need to verify how multiple components interact with each other, or with an external dependency like a database.
- End-to-End (E2E) Tests: The "expensive but expensive" ones. They simulate a complete user flow in the real application (like using Playwright to test a login). They're hard to maintain, but vital for your most critical business flows.
The goal is that, as developers, we can make informed decisions about which type of test to use in each situation, always balancing the cost of creating it with the value it provides.
Question for you: What percentage of your code is covered by automated tests right now? Whatever the answer, what are you going to test this week?
This content was first sent to my newsletter
Every week I send exclusive reflections, resources, and deep analysis on software engineering, technical leadership, and career development. Don't miss the next one.
Join over 5,000 engineers who already receive exclusive content every week
Related articles
Your AI Agent Doesn't Need to Think Better. It Needs to Know When It Screwed Up.
Teams getting real value from agents don't have magical models. They have verification loops that catch failures fast and force correction with external signals.
Invisible Heuristics: What Seniors Know but Can't Explain
Senior engineers resolve incidents faster, but they can't explain how they do it. Gary Klein discovered the same thing with firefighters in 1984: tacit knowledge is built through experience and can't be easily articulated. This matters now more than ever.
The Code Nobody Understands Is Already in Production
AI writes code faster than ever. But there's a trade-off almost nobody talks about: we're exchanging development speed for operational opacity. And that exchange isn't free.
