HaaS on SaaS

Jonathan Haas

I'm a product manager at Vanta with a passion for security and privacy. I write about SaaS, startups, and security.

The Perfection Paralysis: Why Moving Too Carefully Kills Startups

12/4/2024

How an obsession with perfect architecture and clean code can prevent startups from finding product-market fit

Written by: Jonathan Haas

A woman moving fast against a street background

The most valuable code I’ve ever written was messy, quick, and written in response to an immediate customer need. Not because it was technically elegant, but because it helped us understand what our customers actually needed. At ThreatKey, some of our most important product insights came from features we shipped in days, not the ones we spent months perfecting.

The Siren Song of Perfect Architecture

We’ve all been there. A customer presents a problem, and our engineering instincts kick in. We start thinking about the “right” way to build it - the scalable way, the maintainable way, the way that will handle every edge case. We sketch out architectures, debate interfaces, and plan for a future where this feature needs to support millions of users.

But there’s a fundamental flaw in this thinking. It assumes that we know enough about the problem space to design the perfect solution. In the startup world, this is rarely true.

The Real Cost of Perfectionism

When we prioritize architectural perfection over customer feedback, we’re not just slowing down development - we’re actively harming our ability to find product-market fit:

  1. Lost Learning Opportunities: Every week spent perfecting an architecture is a week without customer feedback
  2. Missed Market Windows: While we’re designing the perfect system, competitors are shipping and learning
  3. Wasted Engineering Effort: Perfect architecture for the wrong feature is worse than quick-and-dirty code for the right one
  4. Team Morale: Nothing kills motivation like spending months on a feature only to learn customers don’t want it

A Tale of Two Features at ThreatKey

I remember two distinct approaches we took to feature development:

  1. The Perfect Approach: We spent months building a beautiful, scalable system for handling complex alert routing logic. The architecture was clean, the code was testable, and the interfaces were elegant. After launch, we learned that customers mostly wanted simple “if this, then that” rules.

  2. The Quick Response: A customer mentioned they needed basic Slack notifications for critical alerts. We hacked together a simple integration in two days - literally hardcoding some webhook URLs. That “temporary” solution revealed exactly how customers wanted to interact with alerts, and the insights informed our entire notification strategy.

Breaking Free from Perfection

After many cycles of this, I’ve developed a new set of principles for startup development:

1. The Learning Speed Rule

Ask: “What’s the fastest way we can learn if this is valuable?” Sometimes, that means a manual process behind an API. Sometimes, it means hardcoded values. That’s okay.

2. The Rewrite Permission

Give yourself and your team permission to write code you’ll rewrite. Not because you’re lazy, but because you’re humble enough to admit you don’t have all the answers yet.

3. The Customer-Driven Abstraction

Instead of imagining future use cases, wait for customers to tell you what they need:

// Instead of building the perfect abstraction first:
interface NotificationRouter {
  route(alert: Alert): Promise<DeliveryResult[]>;
  // ... 20 more methods for every possible use case
}

// Start simple and evolve:
async function sendSlackNotification(alert: Alert, webhookUrl: string) {
  // 10 lines of code that solve the immediate need
}

Finding the Balance

The truth is, both the original article and this perspective have merit. The key is knowing when to apply each approach:

  1. Core Infrastructure: Yes, build it right. Your authentication system, data storage, and core security features need to be solid.
  2. Customer-Facing Features: Move fast, learn fast. You can always refactor once you know what customers actually need.
  3. New Markets: When exploring new territory, quick experiments beat perfect architecture every time.

A Startup-Appropriate Definition of Technical Excellence

In a startup context, technical excellence isn’t about writing perfect code - it’s about:

  • Writing code that’s easy to change when you learn new information
  • Building systems that help you learn faster
  • Knowing when to incur technical debt and when to pay it back
  • Keeping your architecture flexible enough to pivot

The Path Forward

The next time you’re tempted to spend weeks designing the perfect system, remember that in startups, perfect is the enemy of learning. The goal isn’t to build something perfect - it’s to build something that helps you understand what perfect would even mean for your customers.

Because in the end, the only way to build the right thing is to start by building something and putting it in front of customers. Everything else is just educated guessing.

The real skill in startup engineering isn’t writing perfect code - it’s knowing when good enough is better than perfect.