A hands-on experience report on a structured, repeatable, AI-assisted development approach
We built a full-stack web application — .NET backend, Vue 3 frontend, PostgreSQL database, hosted on Azure. The platform handles complex business flows: payments, electronic invoicing, AI-based candidate scoring, and automated multilingual translations.
From the start, we made one defining choice: not to treat GitHub Copilot as a code completion assistant, but as a development agent driven by precise, versioned instructions. That choice fundamentally changed how we work.
Two AI tools, two distinct roles
Before getting into the methodology, it's worth explaining something that took shape naturally over the course of the project: we use two AI tools, and they serve completely different purposes.
Claude is our thinking partner. Every feature starts here — before a single line of code is written. We use Claude to:
- run the backlog refinement session for each feature, going through a structured template that covers scope, data model, business rules, dependencies, tests, and acceptance criteria
- design the UI mockup to validate the visual approach before writing any prompt
- challenge architectural decisions — when something feels wrong, Claude is where we reason it out
- author and optimize the Copilot prompts themselves, including trimming token count, structuring the file reads, and enforcing the output format
- maintain the memory of conventions, hard-won lessons, and invariants across sessions, since Claude has no persistent memory by default
Claude never touches the codebase directly. It works at the level of intent, design, and strategy.
GitHub Copilot Agent is our execution engine. It operates inside VS Code, has direct access to the filesystem via MCP, and generates code. But it only kicks in once Claude has done its job — once the feature is refined, the mockup validated, and the prompt written and reviewed.
Copilot reads exactly the files it's told to read, generates exactly what the prompt asks for, runs the tests, and stops. No initiative, no suggestions, no commentary beyond the constrained output format.
The boundary is clear: Claude thinks, Copilot executes.
This separation matters more than it might seem. Mixing the two roles — using Copilot for architectural decisions, or using Claude to generate code directly — produces inconsistent results and breaks the discipline that makes the methodology work.
Core principle: one prompt, one scope
The most important rule in our organization: a Copilot Agent prompt covers exactly one functional scope and one technical layer — backend or frontend, never both at once.
This discipline isn't cosmetic — it prevents context interference, keeps prompts small, and produces predictable, reviewable output.
Each prompt is a *.prompt.md file, versioned in Git. It is never pasted into chat — it's always triggered via the ▶ Run button in VS Code. This distinction matters: it forces us to treat the prompt as a versioned development artifact, not an improvised question.
The anatomy of a prompt
Each *.prompt.md file follows a strict four-layer structure:
1. Tool declaration — MCP servers are declared in the YAML frontmatter, not in the prompt body. filesystem is always present, scoped to targeted source roots — never the repo root. Copilot explores files dynamically rather than relying on hardcoded paths. Other servers (postgres for migrations, sequential-thinking for complex architecture) are added only when genuinely needed.
2. Targeted file reads — files to read are listed explicitly at the top of the prompt. No global scan, no heuristics. The agent knows exactly what to inspect.
3. Delta-only instructions — we never ask Copilot to rewrite an entire class or component. We specify only what needs to be added. This reduces prompt size by 50–60% and eliminates the risk of the agent inadvertently "optimizing" something it wasn't meant to touch.
4. Constrained output format — at the end of every prompt, the response format is enforced: a short summary per step, a list of files created or modified, no implementation justifications, zero next-step suggestions, zero questions, zero proposed improvements, and a hard stop after tests.
That last point is critical. A verbose agent that proposes improvements after every run introduces cognitive noise and pushes toward scope creep.
Versioned UI reference files
To ensure visual consistency over time, we maintain one UI reference file per application module. These files describe the design system to apply: allowed components, color palette, typography, interaction patterns.
Every frontend prompt includes a non-negotiable instruction: use this reference file as a hard constraint — generate no component without consulting it first.
Without this rule, the agent produces functionally correct but visually inconsistent code from one session to the next. These reference files are the long-term memory of the design system — they compensate for the agent's complete lack of context between sessions.
From Figma design to code: the Figma MCP server
One concrete example of how we pushed this methodology further: our UI designs were created in Figma. Rather than manually translating design specifications into component descriptions for the prompts — a tedious and error-prone process — we connected the Figma MCP server directly to Copilot.
This means Copilot could read the Figma file directly during prompt execution: component dimensions, colors, spacing, typography, layer structure — everything the agent needed to produce accurate frontend code was pulled straight from the source of truth, not transcribed by hand.
The Figma MCP server was added to the prompt frontmatter only when a new screen or component was being implemented for the first time. It was never included by default — following the same discipline applied to all other MCP servers: declare only what is genuinely needed for that specific prompt.
The result: a much tighter loop between design and implementation, with no information lost in translation.
Structured refinement before code
Before any prompt is authored, every feature goes through a structured refinement phase — and this is where Claude does most of its work. A template versioned in Git guides this process. It covers: scope, dependencies, data model, business rules, frontend components, tests, acceptance criteria, documentation, and the architectural decision record.
This template serves a dual purpose: it forces careful thinking before development begins, and it feeds directly into writing the prompt. A well-refined feature becomes a prompt in minutes.
The workflow is sequential and non-negotiable:
- Refinement in Claude — scope is defined, mockup is designed, invariants are identified
-
Prompt authoring in Claude — the
.prompt.mdfile is written and reviewed - Execution in Copilot — the agent reads, generates, tests, and stops
- Documentation update — mandatory section in every prompt; docs are part of done
Business invariants: what AI must never reinvent
One of the most important lessons from this project: some business rules are too critical to leave to AI inference. We grouped them in a shared invariants file, systematically included in every relevant prompt.
These rules cover three domains:
- Security — how user identity is extracted and verified, what constitutes an unauthorized access
- Data integrity — which fields are calculated where, which values are persisted rather than recomputed, and why
- Legal and regulatory constraints — tax rules, invoicing obligations, regulatory boundaries
These rules are non-negotiable. Writing them down explicitly prevents an "innocent" prompt from silently generating an implementation that bypasses a critical constraint. This file is authored and maintained in Claude, then referenced in Copilot prompts.
Documentation as code
Every prompt includes a mandatory documentation update section. Depending on what was produced, the agent updates the corresponding reference files in the project's technical documentation: new endpoints, database migrations, background jobs, secrets — all updated in the same commit.
Documentation is not a separate step. It is part of the definition of done for every prompt.
The documentation is automatically published to GitHub Pages on every merge, via a dedicated workflow. There is no manual publishing step — if the docs were updated in the prompt, they are live as soon as the pipeline completes. This makes the documentation as much a part of the delivery pipeline as the application itself.
What this approach actually changed
The quality of AI output is directly proportional to the quality of the instructions. That's not a revelation — but applying it rigorously on a project of this complexity made it viscerally real.
A few concrete takeaways after several months:
- The boundary between Claude and Copilot is the foundation. Blurring it — asking the execution agent to make design decisions, or skipping the refinement phase — consistently produced worse results and more rework.
- Shared conventions are worth more than they look. A rule written once and well-formulated prevents dozens of manual corrections over months.
- Agent verbosity is a risk. Without explicit output format constraints, the agent proposes, comments, suggests — and buries the signal in noise.
- Delta-only reduces regressions. Less unnecessary context means fewer opportunities for the AI to "improve" something it wasn't asked to touch.
- Reference files compensate for the absence of memory. Between sessions, the agent starts from zero. These files are the pragmatic answer to that structural limitation.
- Structured refinement pays off at execution time. Time invested upfront in the template is recovered many times over in reduced back-and-forth during generation.
Conclusion
This methodology isn't universal — it was shaped by a project with a clear architecture and strong business constraints. But the underlying principles transfer to any web application of moderate-to-high complexity: discipline the agent, version the instructions, protect the critical invariants, and treat documentation as code.
Most importantly: know which AI tool is doing which job. Claude thinks. Copilot executes. Keeping those roles distinct is what makes the whole system reliable.
AI doesn't replace architectural rigor. It amplifies it — in one direction or the other.


Top comments (0)