So, you've decided to try developing your Xperience by Kentico project using agentic software development practices. Congrats, this will be a new and exciting experience for you!
LLMs often struggle to follow your goals, codebase, and conventions consistently. Instead of repeating details in every chat, place them in a reference document for consistency and predictability.
GitHub Copilot solves this problem with custom instructions you can include as part of your repository and share with your team.
This post was written for Xperience by Kentico v30.8.0. Please consult the documentation for any feature or API changes when using a different version. Be sure to check the System Requirements in the product documentation.
Defining custom instructions for an Xperience repository
What are "custom instructions" for Copilot? The Copilot documentation describes it as:
[They] define common guidelines or rules for tasks like generating code, performing code reviews, or generating commit messages. Custom instructions describe the conditions in which the AI should perform operations (how a task should be done).
This is exactly what we need—custom instructions give the AI the right context and ensure it follows team conventions.
There are two types of instruction files and we'll be looking at the first in this post.
.github/copilot_instructions.md
- These instructions are included in every chat request and work across any editor that supports Copilot..github/instructions/<subject>.instructions.md
- These are applied based on glob (path) rules defined in each file or they can be specifically included in a chat. They are VS Code specific.
The benefit of having two types of files is all about managing context. If we put everything in copilot_instructions.md
then we risk overloading the AI context window with irrelevant information or including so much context that the agent loses focus on our priorities.
If we instead put everything into the <subject>.instructions.md
files we likely have repeated instructions, which is what we are trying to prevent in the first place.
What we need is a well-organized, balanced approach to providing context.
It's worth noting that GitHub Copilot coding agent will use the copilot_instructions.md
and <subject>.instructions.md
files included in your repository during PR reviews and task assignments, so your investment continues to pay off once you've pushed your code up to GitHub.
Generating instructions from existing conventions
We might already have some .md
files in our repository to document the project architecture and conventions for our team members. Could we copy and paste from those into an instruction file for Copilot?
Sure, but Copilot offers something even better - it can generate instructions just by analyzing the existing conventions already in the source code of our repository!
I tested this feature on the Kentico Community Portal and Copilot generated a file .github/copilot-instructions.md
. Interestingly, it also searched for instruction files used in other agentic editors.
---
applyTo: "**"
---
# Kentico Community Portal
This is a **Xperience by Kentico** community portal with
vertical slice architecture using MediatR for CQRS patterns.
## Architecture Overview
**Core Projects:**
- `Kentico.Community.Portal.Core` - Generated content types,
shared models and base operations
- `Kentico.Community.Portal.Web` - Main ASP.NET Core application
with feature-based structure
- `Kentico.Community.Portal.Admin` - Xperience admin customizations
with React client
**Key Patterns:**
- **Feature Folders**: All functionality organized in
`src/Web/Features/{Domain}/` (Blog, QAndA, Members, etc.)
- **CQRS with MediatR**: Commands/Queries in `Operations/` subfolders,
handler inherit from base command tools
- **Content Retrieval**: Always use `IContentRetriever` for content
queries, never direct database access
- **Page Templates**: Controllers, properties, and view models in
single files with assembly attributes
## Development Workflow
**Essential Commands:**
```powershell
# Build and start
.\scripts\Build-Solution.ps1
.\scripts\Restore-CI.ps1 # Sync database from CI repository
dotnet watch --project src/Kentico.Community.Portal.Web
# Frontend assets
npm start # In both /Web and /Admin/Client directories
# Testing
dotnet test -s .\test\basic.runsettings # Unit/integration only
dotnet test -s .\test\e2e.runsettings # All tests including E2E
```
**Code Generation (after content type changes):**
```powershell
.\scripts\Refresh-GeneratedCode.ps1 # Regenerate C# classes from Xperience
.\scripts\Store-CI.ps1 # Save changes to CI repository
```
## Key Conventions
**Content Operations:**
- Commands inherit from `WebPageCommandHandler<TCommand, TResult>` or
`DataItemCommandHandler<TCommand, TResult>`
- Queries use `IContentRetriever` with execution options for security and
caching
- All operations require explicit channel context (`IChannelContentQuery`
interface)
**Page Templates:**
- Controller, Properties, and ViewModel classes in single file under
`Features/{Domain}/`
- Use `[assembly: RegisterPageTemplate()]` and
`[assembly: RegisterWebPageRoute()]` attributes
- Always call `metaService.SetMeta(page)` for SEO and return
`new TemplateResult(viewModel)`
**Service Registration:**
- Primary constructors for dependency injection throughout
- MediatR behaviors for caching (`QueryCachingPipelineBehavior`) and logging
(`CommandHandlerLogDecorator`)
- Background services inherit from `ApplicationLifecycleBackgroundService`
**Frontend Architecture:**
- Vite.js for Web assets, Webpack for Admin React components
- HTMX for dynamic interactions, Alpine.js for simple client-side behavior
- Milkdown editor for markdown authoring in Q&A and admin forms
## Critical Patterns
- **Channel Context**: Operations from admin vs. website require explicit
channel name due to SaaS hosting model
- **Azure Storage**: Support requests use queue/blob storage with background
processing and dead-letter queues
- **Search**: Lucene.NET with faceted search on taxonomy tags, rebuilds required
after content model changes
- **Member System**: Custom badge assignment rules as background services,
avatar storage by member ID
Refer to `docs/Architecture-Decision-Record.md` for detailed context on major
architectural decisions.
This is a fantastic (albeit, not perfect) starting point! I can refine the file as I test, without struggling with a blank page.
If you don't have source code full of conventions yet - maybe you are starting your first Xperience by Kentico project - then follow these tips from the Copilot documentation when authoring your instructions.
Keep your instructions short and self-contained. Each instruction should be a single, simple statement. If you need to provide multiple pieces of information, use multiple instructions.
Don't refer to external resources in the instructions, such as specific coding standards.
Split instructions into multiple files. This approach is useful for organizing instructions by topic or type of task.
Make it easy to share custom instructions with your team or across projects by storing your instructions in instruction files. You can also version control the files to track changes over time.
Use the
applyTo
property in the instruction file header to automatically apply the instructions to specific files or folders.Reference custom instructions in your prompt files to keep your prompts clean and focused, and to avoid duplicating instructions for different tasks.
At the end of the response, the agent added the following notes.
The instructions are concise (~50 lines) while covering the essential knowledge that would take multiple file reads to understand. They focus on the patterns and workflows that differ from standard ASP.NET Core practices.
Questions for feedback:
- Are there any specific development pain points or patterns I should emphasize more?
- Should I include more detail about the admin customization patterns or background service implementations?
- Are there any critical integration points or debugging approaches that would help AI agents troubleshoot issues faster?
This will be relevant as we look to improve the instructions.
Clarifying with context
This instruction file contains a mix of information about the application's folder and file structure, architectural patterns, local development commands, reusable services, front-end libraries, and custom features.
There's not a lot here about Xperience by Kentico, some of the information isn't important enough to deserve space in this instruction file, and some of it isn't accurate.
Remember, the point of these instruction files is managing context, so let's try to keep everything relevant to things the agent will do most often.
Let's tune it up!
I gave Copilot the following prompt to adjust the instructions file.
I'd like to include more information about how Xperience by Kentico's features are used in this project and related conventions.
Look up the documentation for Xperience and use that to guide the instructions for how its features are implemented in this project.
Additionally, some of the instructions aren't relevant, like,
- All operations require explicit channel context (
IChannelContentQuery
interface)only queries used in non-web request scenarios require explicit channel context
- Channel Context: Operations from admin vs. website require explicit
channel name due to SaaS hosting modelThis is unrelated to SaaS. Explicit context is required when querying for web page content outside of Page Builder components (templates, sections, widgets)
Azure Storage: Support requests use queue/blob storage with background
processing and dead-letter queuesSearch: Lucene.NET with faceted search on taxonomy tags, rebuilds required
after content model changesMember System: Custom badge assignment rules as background services,
avatar storage by member IDThese are not significant enough features to include in the instructions
There is information in the /docs folder which might be more appropriate for an instructions file.
The agent responded with an updated copilot_instructions.md
featuring more details about Xperience and less about unimportant project details, and it used the Context7 MCP server to get more context from the Xperience documentation
The point here isn't the exact details of the request and response, but to showcase a few things.
LLMs know public technologies, not your project’s history, so they may misunderstand design choices.
Pointing the agent to Xperience docs and the Context7 MCP server adds important context beyond what code alone provides.
You will need to continue to adjust these instructions as you use them with the agent in your project.
Testing our instructions
Let's try and use the instructions to get the agent to build a new widget.
Build a new Page Builder Featured Question widget that allows the marketer to select a single Q&A page using the ContentItemSelector.
The widget should display the Q&A page title, taxonomy tags, and link to the page, all in a card layout.
The prompt was minimal, but that’s the point - custom instructions reduce how much project-specific context we need in each chat. Therefore, I'm starting out minimal and based on the results I'll decide where additional context goes - into my widget creation prompt or custom instructions files.
So, what was the result?
All files were generated in the right locations following existing naming conventions.
All modern C# best practices were followed.
A variation of the very custom component validation pattern I use was followed - not perfect but good enough.
The widget view model used constructor injection, something the Kentico Community Portal adopts but many projects skip for direct property assignment.
The Razor view used all the Bootstrap 4 and custom CSS classes used by other views.
Copilot did a pretty amazing job and followed many of the conventions of the project, however I think this could be attributed to strong consistency in the existing code examples and project structure.
What didn't go so well?
The agent generated a custom
IQuery
implementation to retrieve the Q&A web page content when it could have just used theIContentRetriever
. This is probably because it's not clear when the simplerIContentRetriever
approach is better.The widget property to select the web page is using the
WebPageSelectorComponent
instead of theContentItemSelectorComponent
, likely caused by old uses of the web page selector. This should be identified as an anti-pattern now.The widget property validation didn't use the
Result<T>
pattern I've adopted. This could be because the LLM doesn't include as much information about this pattern or library.
Prioritizing instructions over existing code context
This test reveals something interesting - the agent relies heavily on source code as its primary context! We have to do extra work to guide the agent away from the code it consumes.
If you have old code and patterns in your project that should eventually evolve to a better approach, potentially enabled by new Xperience features, this is the perfect type of instruction to add to our custom Copilot instructions. This will help you adopt the new approach with all new code.
Or, if you are using features and syntax specific to a platform (ex: Xperience) or library (ex: CSharpFunctionalExtensions), call that out in your instructions when your source code doesn't tell the whole story.
Let's improve the copilot_instructions.md
and try again. Of course, we'll use Copilot to make the changes.
Here's my prompt to update the instructions file.
let's update the copilot instructions with some key guidance. add this information to the relevant areas of the existing instructions and rewrite it to match the style of current instructions.
Always prefer using Xperience's IContentRetriever directly instead of creating new IQuery instances to wrap calls to it or the ContentItemQueryBuilder. The IContentRetriever automatically handles caching and incurs less technical debt. The ContentItemQueryBuilder is lower level, is typically not needed, and adds complexity.
Always prefer the ContentItemSelectorComponent over the WebPageSelectorComponent. Although the WebPageSelectorComponent isn't marked as obsolete in Xperience, it should be consider obsolete in this project.
When validating the properties of a widget, follow the Result<T> pattern found in other widgets. This uses the CSharpFunctionalExtensions library with the Result monad, and we want to keep its use consistent.
Next, I deleted the existing widget files and ran the exact time widget creation prompt again.
What went well this time?
The agent used the
ContentItemSelectorComponent
and even populated its properties exactly as I would have.It used the
IContentRetriever.RetrievePagesByGuids()
method - this is the best approach for this scenario.
What was still a challenege?
- The agent clearly doesn't understand the use of the
Validate()
method returning aResult<TValue, TError>
and it made two calls toValidate()
in the Widget'sInvokeAsync()
method.
I could continue to update the instructions for this widget scenario, but copilot_instructions.md
is intended for all agentic development, not just building widgets. This would be a great opportunity to create custom prompt files, but that will have to wait for a future blog post.
Either way, it's clear the context supplied in the custom instructions is having the desired effect and the agent is generating code more inline with our expectations, even when those don't match the existing codebase.
Where do we go from here?
This exercise - and custom Copilot instructions - revolves around managing the AI's context to produce better results.
There are other types of instructions we can provide that go beyond code generation and help close the agentic development loop with Git commit messages and pull request descriptions.
We should also create the <subject>.instructions.md
custom instructions that are applied to specific files using the applyTo
value. We can use this to organize instructions for generating documentation, C#, TypeScript, CSS, and even SQL code.
Or, by using the wildcard glob for applyTo
we can set up instructions about how the agent should craft Git commit messages, like in the Kentico Community Portal.
The same best practices apply here as well.
Limit the total context.
Keep instructions simple and focused.
Test your instructions on real scenarios and make improvements.
Explore other features like custom prompts, chat modes, or MCP servers to complement the instructions.
Need inspiration to get started? Check out the Awesome Copilot repository's list of sample instructions or read other developer's experiences with Copilot custom instructions.
Have you used Copilot custom instructions? Share your experiences in the discussion linked below.

Sean Wright
I'm Lead Product Evangelist at Kentico. I work in the Product Marketing department at Kentico along with Matej Stefanik, Miroslav Jirku, and James Turner. My responsibilities include helping partners, customers, and the entire Kentico community understand the strategy and value of Xperience by Kentico. I'm also responsible for Kentico's community programs.