Desarrollo5 min

[Day 2] When Your 'Clever' Architecture Becomes Your Worst Enemy

A 'simple' versioning bug turned into an existential crisis about software architecture. This is what building in public really looks like.

L

Luis

Founder & DevOps Engineer

The Bug That Broke My Brain

It's 3:47 AM in Bogotá. I should be sleeping, but instead I'm staring at a bug that's making me question every architectural decision I've made in the last 7 months.

Yesterday, I launched InfraUX with zero users and infinite optimism. Today, I discovered why we might stay at zero users for a while.

Today's Metrics Dashboard

<MetricsCard date="2025-09-30" previousDate="2025-09-28" showChange={true}

  • Users: 0 → 0 (still zero 😅)
  • Revenue: $0 → $0 (consistency!)
  • Features Shipped: 0
  • Bugs Found: 3 → 7 (+4)
  • Bugs Fixed: 1 → 1 (0 today)
  • Coffee Consumed: 11 ☕
  • Days Without Breaking Prod: 2 (no prod to break!) </MetricsCard>

Previously on InfraUX...

Yesterday I promised to show you the actual product, bugs and all. Well, be careful what you wish for. I found a bug so perfectly representative of startup life that I had to share it.

But first, let me show you what we're actually building. Here's the InfraUX Infrastructure Designer in all its 80%-complete glory:

InfraUX Infrastructure Designer Interface

Yes, it's real. Yes, it works (mostly). And yes, I discovered our versioning system is having an existential crisis.

The Innocent Beginning

Here's how it started. A simple requirement:

  • Users can version individual resources (like a single EC2 instance)
  • Users can version entire infrastructure diagrams
  • These should be independent

Sounds straightforward, right? Past Luis thought so too.

The "Clever" Solution

Six months ago, I had a brilliant idea. Why create two separate versioning services when I could create one SUPER SMART service that handles both cases?

1// Past Luis: "I'm a genius!" 2class UnifiedVersionService { 3 async createVersion(entityId: string, entityType: 'resource' | 'diagram') { 4 // One service to rule them all 5 // What could possibly go wrong? 6 } 7}

Narrator: Everything went wrong.

The Bug That Revealed Everything

Today, while testing (yes, I test my own product because I have no users), I discovered this beauty:

1// What I expected 2const nodeVersion = await versionService.createVersion(nodeId, 'resource'); 3// Result: Clean version of just the node 4 5// What actually happened 6const nodeVersion = await versionService.createVersion(nodeId, 'resource'); 7// Result: Node version contaminated with diagram state 8// Plus bonus race conditions 9// And a sprinkle of undefined behavior

When you version a single resource, it's dragging along the entire diagram's context. It's like trying to save a single Photoshop layer but accidentally creating a copy of the entire 2GB file.

Here's what the versioning modal looks like when you try to version a single node:

Versioning Modal Bug

Notice how it says "Version this resource" but then shows the entire diagram context? That's our problem.

The Architectural Spelunking

I spent 6 hours today tracing through the code. Here's what I found:

The Coupling Nightmare

1// Found in 47 different files 2import { versionService } from '@/services/versionService'; 3 4// Each one assuming different things about how versioning works 5// Some expecting resource versions 6// Some expecting diagram versions 7// Some expecting both??? 8// One literally had a comment: "TODO: Figure out what this does"

The State Contamination

1# In the backend (Python/FastAPI) 2def create_resource_version(self, resource_id: str): 3 # This function somehow knows about diagram state 4 # Because past Luis thought "context is good" 5 diagram_context = self._get_diagram_context(resource_id) # WHY??? 6 7 # Now every resource version includes diagram metadata 8 # Making each version 10x larger than needed 9 # And creating circular dependencies

The Database Horror

1-- What I found in the versions table 2{ 3 "id": "v_123", 4 "entity_id": "node_456", -- This is a single EC2 instance 5 "entity_type": "resource", 6 "data": { 7 "node": { ... }, -- 1KB of actual node data 8 "diagram": { ... }, -- 100KB of diagram data (WHY IS THIS HERE?) 9 "global_state": { ... }, -- 50KB of ??? (I don't even know) 10 "random_metadata": { ... } -- Past Luis was very thorough 11 } 12}

And here's what the version history looks like in the UI:

Version History Contamination

Each "node version" is 150KB instead of 1KB. That's a 150x bloat factor. Nice job, Past Luis.

The Three Paths Forward

After consuming dangerous amounts of coffee, I see three options:

Option 1: The Clean Separation

1// Two services, clear boundaries 2class ResourceVersionService { 3 // Only handles individual resources 4} 5 6class DiagramVersionService { 7 // Only handles full diagrams 8}

Pros:

  • Clean architecture
  • Single Responsibility Principle
  • Future Luis will thank Current Luis

Cons:

  • 2-3 days of refactoring
  • 40+ files to update
  • 0 users will become 0 users for 3 more days

Option 2: The Band-Aid

1// Keep the mess, add more flags 2class UnifiedVersionService { 3 async createVersion( 4 entityId: string, 5 entityType: 'resource' | 'diagram', 6 options: { 7 includeContext?: boolean, 8 isolateState?: boolean, 9 pleaseDontBreak?: boolean // Getting desperate 10 } 11 ) { 12 // 47 if statements later... 13 } 14}

Pros:

  • Quick to implement
  • Minimal refactoring
  • Can ship other features sooner

Cons:

  • Technical debt compounds daily
  • Future Luis will hire a hitman for Current Luis
  • The code will become sentient and seek revenge

Option 3: The Over-Engineering Special

Implement a Git-style versioning system:

  • Working copies
  • Staging area
  • Commits with proper isolation
  • Cherry-picking between versions

Pros:

  • Elegant solution
  • Infinitely scalable
  • I'll feel smart

Cons:

  • 2 weeks of work minimum
  • Massive overkill for 0 users
  • My therapist says I need to stop over-engineering

The Lessons (So Far)

1. Premature Abstraction is the Root of All Evil

I tried to predict future needs instead of solving current problems. Classic mistake. The "unified" service that handles everything handles nothing well.

2. Your Clever Code is Tomorrow's WTF

Every time I think I'm being clever, I'm usually just creating future pain. That "elegant" abstraction? It's tomorrow's 3 AM debugging session.

3. Simple > Smart

Two simple services would have been better than one "smart" service. Boring code is maintainable code.

4. Zero Users = Maximum Freedom

The silver lining? With 0 users, I can refactor without fear. No migrations, no backwards compatibility, no angry customers. Just me, my code, and my regrets.

The Decision

After writing this post (rubber duck debugging via blog), I'm going with Option 1: The Clean Separation.

Why? Because if I'm going to have 0 users, I might as well have 0 users with clean architecture.

Starting tomorrow, I'm:

  1. Creating separate ResourceVersionService and DiagramVersionService
  2. Refactoring all 47 files (I counted)
  3. Adding actual tests (revolutionary, I know)
  4. Documenting the decision (future Luis deserves context)

What's Next

Tomorrow's post will either be:

  • "How I Fixed the Versioning System" (optimistic)
  • "Why I'm Now Building a Coffee Review App Instead" (realistic)
  • "Day 3: Still Refactoring, Send Help" (probable)

Community Questions

I genuinely want to know:

For the architects out there: How do you decide when to refactor vs. when to pile on the technical debt? When is "good enough" actually good enough?

For the builders: What's the worst architectural decision you've made that seemed brilliant at the time?

For everyone: Am I overthinking this? Should I just ship the band-aid and move on?

Join the Journey

Want to watch someone refactor their way to glory (or madness)? Follow along:

Today's Question: What's the most embarrassing bug you've found in your own code months later?


Building InfraUX in public, one refactor at a time. Tomorrow: Either victory or a career change.

#building-in-public#architecture#technical-debt#versioning#devops
[Day 2] When Your 'Clever' Architecture Becomes Your Worst Enemy | InfraUX Blog | InfraUX - Cloud Infrastructure Platform