I’ve been shipping code for 20 years. I remember when “IntelliSense” was considered a crutch, and I’ve survived enough “Next Big Thing” frameworks to be thoroughly cynical. My stack has always been C#, C++, and Python—languages where I know where the bodies are buried and how to dig them up.

When the AI coding hype train left the station, I stayed on the platform. To me, LLMs were just glorified autocomplete tools for junior devs who didn’t want to read documentation.

Then, our project, edme.pro, hit a critical pivot. We needed to ship DreamPath, a complex, adaptive learning engine, and we faced a binary choice: burn months hiring a traditional engineering team, or bet the house on the AI hype to see if we could build it with tools alone.

I decided to run the ultimate stress test. I would build the entire production backend in Go (Golang) language I had never used in production—and I swore to write zero lines of code by hand.

My role shifted from Tech Lead to Architect. The AI became my Junior Developer partner that types at the speed of light but lies with sociopathic confidence.

Here is what happened when I traded my IDE for a prompt window.


The Setup: A Clean Slate and a Dirty Experiment#

The requirements for DreamPath were not trivial. This wasn’t a “To-Do List” app or a Hello World prototype. The Product Requirements Document (PRD) called for:

  • Adaptive Learning: A system based on the Dreyfus model of skill acquisition.
  • Recursive Taxonomies: A graph-based skill tree that evolves based on user progress.
  • RAG & Vector Search: An AI mentor that generates curriculum on the fly using retrieval-augmented generation.
  • Production Standards: Clean Architecture, strictly typed interfaces, JWT authentication, and asynchronous job queues.

The challenge was specific: Go is a brutalist language. It doesn’t have the magic of Python or the syntactic sugar of C#. It demands explicit error handling, strict typing, and a very specific philosophy on dependency management. If the AI tried to “Python-ize” the Go code, the compiler would reject it instantly.

I expected disaster. I prepared myself to spend weeks fighting the AI over folder structures, circular dependencies, and the dreaded if err != nil.


Phase 1: The Honeymoon (The “Magic” of Scaffolding)#

I admit it: The first 48 hours were intoxicating.

I fed the LLM (Google Jules) the PRD and asked for a project skeleton. Usually, I spend the first two days of a new project arguing with myself about folder structure, setting up the linter, and configuring the Docker container.

PR #1 (Implement Job Role Management API) changed my mind immediately. In seconds, the AI scaffolded the entire backend:

  • Domain Models: Structs defined.
  • Repository Interfaces: Strictly typed interfaces for data access.
  • Service Layer: Business logic separated from the HTTP transport.
  • HTTP Handlers: A fully functioning chi router setup.

It didn’t just write code; it wrote idiomatic Clean Architecture. It separated internal/domain from internal/service and internal/repository exactly as requested.

The Linting Miracle#

The moment that truly shook me happened on PR #9. We had generated a massive openapi.yaml specification file to define our API contract. As is typical with auto-generation, it was messy.

I pasted the file into the context window and said, “Lint this using Spectral. Fix every error.”

It found and fixed over 500 linting errors in one commit. Five. Hundred. It corrected schema types, fixed indentation, added missing descriptions, and normalized operation IDs. If I had to fix 500 whitespace and schema issues in YAML manually, I would have questioned my career choices. The AI did it in 30 seconds.

For a moment, I thought, “Maybe I’m obsolete. Maybe this is just easy now.”


Phase 2: The Reality Check (The Valley of Despair)#

Then, the honeymoon ended. The complexity ramped up, the features got harder, and the AI hit the wall. This is where the difference between “coding” (typing syntax) and “engineering” (solving problems) became painfully obvious.

As we moved past CRUD and into the core logic, the AI exposed its true nature: it is a brilliant mimic that doesn’t understand consequences.

The “Go” Struggles: Byte vs. Rune#

Since I didn’t know Go, I relied on the AI to handle language nuances. That was a mistake.

In PR #152, we implemented Server-Sent Events (SSE) to stream the AI mentor’s responses to the student in real-time. The AI wrote a chunking algorithm to split long strings into manageable packets. To my C++ eyes, it looked standard.

But Go strings are UTF-8 byte slices. The AI was slicing the string by byte index, not rune.

The Bad Code:

// AI generated slice logic
chunk := response[start:end] // Slicing bytes

When the AI mentor generated a multi-byte character—like an emoji or a complex Cyrillic character—and that character straddled a chunk boundary, the stream corrupted. The frontend received garbage data.

The Fix: I had to intervene, dive into the Go documentation, and force the AI to cast the string to []rune before slicing. A human junior developer might make this mistake, but they would likely catch it during a unit test. The AI was supremely confident that its code was correct because “it compiled.”

The Architecture spaghetti: Circular Dependencies#

Clean Architecture relies on strict dependency rules (Inner layers cannot depend on outer layers). The AI, trying to be helpful, decided to connect everything to everything.

In PR #79, we hit a wall. The service package needed to schedule background jobs, so it imported the async package. But the async package needed to update job status, so it imported the service package.

Go compiler: import cycle not allowed

The AI panicked. It tried to fix the error by creating more interfaces in new packages, essentially creating a “dependency interfaces” soup. It was trying to engineer its way out of a hole by digging deeper.

I had to stop the generation. I grabbed a pen and paper, drew the dependency graph, and strictly instructed the AI with the new dependencies structure.

The AI is great at writing files. It is terrible at seeing the holistic graph of the application.

The Hallucination: Internal Monologue Leaking#

The scariest moment came in PR #97. We implemented a “Chain-of-Thought” logging feature where we wanted to save the AI’s internal reasoning to the database for auditing.

The AI model, confused by the prompt instructions, decided to return the internal reasoning inside the public API response to the frontend.

The Response:

{
  "response": "Here is your lesson plan...",
  "thinking_process": "<thinking>Student is struggling with loops...</thinking>"
}

The frontend crashed because the XML tags inside the JSON broke the schema validation. It wasn’t a syntax error; it was a logic error. The AI didn’t understand the boundary between “System Instructions” and “User Output.”


Phase 3: The Epiphany (The Shift from Coder to Architect)#

Around PR #100, my workflow changed completely. I stopped treating the AI like a code generator and started treating it like a subordinate developer who needs strict, verifiable instructions.

I realized that “Prompt Engineer” is a myth. The real role is AI-Augmented Architect.

Verification is the New Programming#

I stopped reading code to see what it did, and started reading code to see where it would break. My job became 90% code review and 10% prompt writing.

  • Idempotency (PR #211): The AI kept creating duplicate learning activities when the user refreshed the page. It didn’t understand that state changes need to be idempotent. I had to explicitly prompt: “Ensure this endpoint is idempotent. Check if a task is already generating. If so, return 202 Accepted, not 201 Created.”
  • Concurrency (PR #247): We hit race conditions in the job queue where two workers grabbed the same job. I had to explicitly tell the AI to use SELECT ... FOR UPDATE SKIP LOCKED in Postgres. The AI knew the syntax—it had trained on it—but it didn’t know when to apply it until I, the Architect, demanded it.

English is the Compiler#

I learned that if my instructions (prompts) were vague, the code was buggy. Writing a prompt became exactly like writing a technical specification.

“I no longer ask for ‘code.’ I ask for ‘plans.’ I make the AI write the implementation plan first. If the plan is wrong, I reject it. I’m reviewing logic, not syntax.” — Entry from Phase 3 Logs

If I asked, “Fix the bug,” the AI would often just change random lines. If I asked, “The foreign key constraint is failing because the deletion isn’t cascading. Implement a transaction that deletes the child rows first,” the code came back perfect.


Phase 4: The Final Product#

So, did we succeed?

We shipped DreamPath. It works. It has a comprehensive admin dashboard, a student portal, complex graph algorithms for skill trees, and a 90% test coverage rate across the service layer.

The Stats:

  • Duration: 150 Days
  • Pull Requests: 276
  • Manual Lines of Code: 0
  • Lines of Code Reviewed: ~45,000

The AI handled the boilerplate, the linting, the DTO conversions, and the repetitive unit tests. It allowed me to focus purely on Architecture, Data Consistency, and Security. I learned Go not by writing it, but by reviewing thousands of lines of it.


Conclusion: The Verdict#

Did the AI build DreamPath? Yes. Did it do it alone? Absolutely not.

If I had let the LLM drive, DreamPath would be a spaghetti-code mess of circular dependencies, deadlocked transactions, and corrupted text streams. It would “look” like code, but it wouldn’t function as a system.

We decided not to hire the traditional team, and that bet paid off. But it paid off because we didn’t fire the architect.

The Verdict: LLMs won’t replace developers. Developers with strong architectural fundamentals who use LLMs will replace those who don’t.

The future of software engineering isn’t “No Code.” It’s “High-Level Supervision.” We are moving from laying bricks to managing the blueprints. And frankly, after 20 years of typing public static void main, I’m okay with that.