DEV Community

ynwd
ynwd

Posted on

I Got Tired of Fighting AI-Generated Spaghetti Code, So I Built a Framework That Tames It

If you've used GitHub Copilot for more than a week, you know the pain.

You ask it to add a new feature, and suddenly files are everywhere. A handler ends up in the wrong folder. It imports stuff from modules it shouldn't touch. It writes frontend code that looks nothing like your existing patterns.

And every single time, you spend 20 minutes fixing what the AI broke.

I got tired of that. So I built something about it.

Quick heads up: this is the initial version. I'm still iterating, things will change, and I'll keep the article updated as the project evolves. If something looks different by the time you read this — that's probably why. Cool? Cool.

What Even Is This?

Flow is a modular monolith. Here's what that means in practice:

One feature = one folder. The Go backend (handler, service, repository, model) AND the React frontend (components, API client, tests, esbuild config) for a single feature all live in modules/<feature>/. Together. No splitting across 7 directories.

This is the opposite of the typical structure where you have backend/handlers/, backend/services/, frontend/components/, frontend/api/ — and finding all the pieces of one feature feels like a treasure hunt.

With colocation, you open one folder and everything about that feature is right there. And more importantly — Copilot can see the full picture in one place, so it stops guessing where things should go.

The "Just Tell the AI What to Do" Bet

Here's the idea: what if instead of fighting Copilot, we designed our project structure so the AI can't mess up?

What if we wrote instruction files that Copilot reads automatically — telling it exactly where to put files, how to name things, which patterns to follow?

That's what I did with Flow. It's a Go + React/TypeScript modular monolith. But the real magic isn't the tech stack. It's the AI infrastructure wrapped around it.

The Problem With Most Repos

Most codebases are a mess for AI. Here's what happens:

  1. You say "add a user profile module"
  2. Copilot generates some code
  3. The backend goes in handlers/ and the frontend goes in components/ — now your feature is split across the universe
  4. It copies patterns from some random npm package instead of your codebase's conventions
  5. You sigh, delete everything, and do it manually

Sound familiar?

Three Things That Actually Work

1. Tell the AI Your Rules (It Will Listen)

VS Code lets you put a file called .github/copilot-instructions.md in your repo. Copilot reads this file every time you chat.

I filled it with stuff like:

- All code for one feature MUST live in modules/<feature>/
- One module MUST NOT import another module's package directly
- React components ONLY go in root components/ if used by 2+ modules
Enter fullscreen mode Exit fullscreen mode

That's it. Just plain instructions in markdown. And Copilot actually follows them.

2. Give It Scripts, Not Hallucinations

Instead of letting the AI guess how to scaffold a new module, I gave it a shell script and told it: "When someone says 'create a module', run THIS script."

The script generates 15+ files — handler, service, repository, model, route registration, React component, API client, tests. All of them follow the exact same pattern every single time.

Same for database migrations and shared components. I wrote 3 domain-specific skills that the AI triggers automatically when it sees keywords like "add table" or "move to shared".

3. Two AI Agents With Different Jobs

I defined two custom agent modes:

  • @implementer — has full access to read, write, and run terminal commands. This is your coding buddy.
  • @reviewer — read-only. It can look at your code and tell you what's wrong, but can't touch anything.

When I finish implementing something, I just type @reviewer review my changes and it checks module boundaries, import rules, and coding conventions. No PR review needed for small stuff.

Does It Actually Work?

Yeah, surprisingly well.

The other day I typed @implementer add a blog module with CRUD endpoints. It ran the scaffold script, registered the module, built both the Go backend and React frontend, and ran the tests. Took about 30 seconds.

No file was in the wrong place. No cross-module imports. The frontend called the API through the api.ts file like it was supposed to.

The AI didn't magically get smarter. It just had clear boundaries and couldn't go off track.

What's in the Box

If you wanna try it:

git clone https://github.com/ynwd/flow
cd flow
npm install
npm run build:css
go run .
Enter fullscreen mode Exit fullscreen mode

Then open VS Code, hit Cmd+I, and tell it to create a module. See what happens.

The repo has:

  • Go backend with proper error wrapping, DI, and table-driven tests
  • React 18 + TypeScript frontend bundled with esbuild (no webpack, no vite for prod)
  • Tailwind CSS v4
  • Module self-registration pattern (no monolith route file to edit)
  • .github/copilot-instructions.md with all the rules
  • 3 custom agents (implementer, reviewer, ask)
  • 3 reusable prompt templates
  • 3 domain skills for scaffolding, migrations, and shared components

The Honest Take

This doesn't replace understanding your codebase. You still need to know what you're building.

But it removes the friction of AI coding. The part where you fix the AI's mistakes instead of writing the actual feature. That's what this solves.

If you've been on the fence about using Copilot for bigger projects, or you tried it and got frustrated — give this pattern a shot. The framework itself is whatever. But the idea of structuring your repo for AI? That's gonna stick.


Built this over a few weekends. Star the repo if you find it useful, or fork it and make it your own. Happy shipping.

Top comments (0)