← Back to Blog
Building in Public

From Architecture Drift to Working AI

christian crumlish•
A robot reassembling a simple toy, returning an overcomplicated system to something that actually works.
“Get back on track!”

From Architecture Drift to Working AI

June 15, 2025

Well, I thought I was just going to connect some dots. Finish PM-008, get GitHub issue analysis working end-to-end, maybe record a demo for the team. You know, a nice straightforward Friday afternoon of development.

Three hours later I’m knee-deep in what you might call “architectural archaeology” — digging through layers of implementation decisions to figure out why we had three different Task classes that couldn’t talk to each other.

(By the way, every time I say “we realized” or “we discovered” in this post, it really means Claude proposed something and I tried to keep up with the implications while juggling Docker conflicts.)

The goal was simple enough

PM-008 was supposed to be our first complete AI workflow: GitHub issue analysis from start to finish. The intent classification worked fine, the GitHub integration existed, but that crucial middle layer — workflow execution — was completely broken. Tasks got created but never executed, workflows existed in theory but couldn’t persist, and error messages were about as helpful as a snooty barista.

What looked like a simple “connect the dots” problem turned out to be fundamental architectural inconsistencies threatening the entire system’s integrity.

Meanwhile, back in environment hell

Before we could even start debugging, I had to deal with the practical reality of developing across multiple machines without losing momentum to environment setup. Docker conflicts, PostgreSQL connection failures, and the classic “worked yesterday, broken today” syndrome that every developer knows by heart.

Mind you, this isn’t just about Docker containers — it’s about session startup protocols that account for port conflicts, service dependencies, and the fact that Homebrew PostgreSQL likes to claim port 5432 before Docker gets a chance. (If you’ve ever wondered why your local development setup works perfectly until you restart your laptop, this is why.)

The seemingly mundane work of getting consistent environments reinforced something important: architecture decisions must account for operational reality, not just logical design.

Catching ourselves freelancing

The breakthrough moment came when I pointed out to Claude that at some point we’d started “freelancing” — making implementation decisions without consulting our own design specifications. The symptom was simple enough: 'Workflow' object has no attribute 'get_next_task'. The root cause was architectural drift.

We had inadvertently created three different Task classes:

  • services.domain.models.Task (domain business logic)
  • services.orchestration.tasks.Task (orchestration-specific)
  • services.database.models.Task (persistence layer)

Each served a purpose, but they weren’t aligned. The workflow factory created domain Tasks, the engine expected orchestration Tasks, and the database used its own Task model. No wonder task execution was failing.

(This is what happens when you’re building a PM assistant without following your own PM methodology — you get to debug your architectural decisions in real time.)

Domain-first design saves the day

When we actually returned to our technical specifications — imagine that, reading our own docs — the guidance was clear: domain objects should drive technical decisions, not the other way around. PM concepts like workflows and tasks should be first-class entities with business logic, not just data structures.

Tech geezer flashback: I’m pretty sure I first heard about DDD from Kent Beck, in the previous, um, millennium?

Instead of patching individual symptoms, we methodically aligned the implementation with the original design. Enhanced the domain Task class with proper types and business methods, fixed the workflow factory to create domain objects with correct relationships, completely rewrote the orchestration engine to use domain-first patterns.

The key was working through each layer systematically, checking our specs before making assumptions, and applying the repository pattern to cleanly separate domain logic from persistence concerns.

When persistence actually works

Getting database persistence working revealed another architectural win. Rather than tightly coupling workflow execution to database models, we implemented clean domain-to-database conversion through repositories. Domain workflows with business logic methods persist to database models optimized for storage, with clear boundaries between layers.

This pattern proved its worth immediately: when we needed to add workflow state tracking, the changes were isolated to the repository layer. The domain logic stayed clean, and the database schema could evolve independently.

Thinking about when I was directing product on the last iteration of AOL Instant Messenger (AIM), and the spaghetti that had accumulated there. We had more QA engineers that regular engineers! The hours we could have saved with that sort of architectural hygiene… but you can’t retrofit that sort of thing. If you don’t get it right from the start, it’s just about impossible.

Real AI in action

After fixing the architectural foundation, PM-008 came alive. The system now extracts GitHub URLs from natural language requests, fetches real issues via API, applies 85 documents of PM knowledge for context-aware analysis, and generates professional improvement suggestions with confidence scoring.

Testing with “Disabling the gutter icon by default”, the system correctly identified the issue’s vagueness, suggested specific improvements, and provided actionable feedback — all while applying PM best practices from the knowledge base.

Nevertheless, we persisted

This session reinforced why persistence matters in both technical and personal contexts.

Technical persistence: Database state that survives restarts, workflow history that enables learning, error recovery that maintains system integrity. Without proper persistence, AI systems can’t build on previous interactions or learn from corrections.

Development persistence: Staying with architectural problems until they’re properly solved, rather than patching symptoms. The temptation was to work around the Task class inconsistencies with type casting and special cases. Instead, we invested in fixing the root cause.

Product work often offers the temptation to ship quick fixes vs. investing in foundational improvements but that way leads to darkness.

Demo-safe development

With PM-008 functional, we established what I’m calling “demo-safe development” — stable branches that always work for stakeholder presentations, while enabling continued development on feature branches. The demo-stable-pm-008 branch represents a known-good state that teams can rely on. If I break things while building out PM-009, I can still revert to the demo branch and show what was working last time things were working.

This recognizes that AI systems development is inherently experimental. Features may work intermittently, new capabilities might break existing ones, and architectural changes can have unexpected ripple effects. Having a stable baseline enables confident iteration.

What I’m learning about building AI tools for PMs

Domain-first design pays off: When building tools for specific disciplines, start with domain concepts rather than technical abstractions. The domain provides natural boundaries that prevent architectural drift.

Early architecture investment accelerates development: Time spent on proper design patterns — repository layers, domain models, event sourcing — speeds up development rather than slowing it down. When problems arise, you fix root causes rather than patching symptoms.

Environment consistency enables flow: AI development requires rapid iteration cycles. Anything that breaks the develop-test-iterate loop multiplies development time exponentially.

Real integration validates design: Mock implementations can hide architectural problems that only surface when connecting to real external systems.

What came next

PM-008 proved the architectural foundation can support sophisticated AI workflows. With GitHub issue analysis working end-to-end, we were ready to expand to additional PM tasks: automated reporting, multi-repository support, and eventually proactive insights that leverage the learning mechanisms we’ve built.

But first: demos. Having a working system changes everything about stakeholder conversations.

← Back to BlogAlso available on Medium