July 9th, 2025
0 reactions

How the .NET MAUI Team uses GitHub Copilot for Productivity

Jonathan Peppers
Principal Software Engineer

Around the time of Microsoft Build 2025, the new GitHub Copilot Coding Agent was unveiled and made widely available across GitHub. With AI evolving at a dizzying pace in recent months, we imagined a “dream scenario”:

  • A GitHub issue is filed.
  • The problem looks to have a straightforward solution.
  • A team member assigns the issue to Copilot, giving it a few tips.
  • Copilot generates a pull request with the fix (and tests).
  • CI runs, tests pass, and the pull request is merged!

This would dramatically reduce time and effort, freeing human developers to focus on higher-value, complex problems–while Copilot takes care of the repetitive work.

But would this dream be a reality? In this post, we aim to share a balanced perspective: highlighting both where Copilot has been genuinely helpful, and when it completely missed the mark.

In many ways, it already is. The .NET MAUI team has been actively using Copilot to boost our productivity, and we’re excited to share some practical tips for getting the most out of it.

At the time of writing, Copilot has risen to be the #64 contributor all time in dotnet/maui, which we expect to grow in the coming months:

GitHub Contribution Graph of Copilot

Step 1: .github/copilot-instructions.md

To start with the basics, we provide GitHub Copilot with some general context and guidance for our repository by including a copilot-instructions.md file. By convention this file should be kept in the .github folder at the root of your repository or workspace. This works both locally when using Copilot in Visual Studio or VS Code and when using the GitHub Copilot Coding Agent on GitHub.

The types of useful instructions we keep in this file include:

  • Project Context: What is this repo? Provide an overview of the project and relevant background information.
  • Repository Structure: Describe the structure of the repository, what types of files go where, how to write tests, etc.
  • Coding Standards: If you are using non-default code formatting, give specific examples of how you want your code to look.

A great way to start is to simply ask the Copilot Coding Agent to generate this file for you.

An example GitHub issue would be:

Go through this repo, review structure of project, source code, etc.

Additional docs to review about the product: https://learn.microsoft.com/dotnet/maui

Update `.github/copilot-instructions.md` to make Copilot more helpful going forward.

Include a note to update `copilot-instructions.md` with new instructions as the project evolves.

See: https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot 

Assign to Copilot and see what it comes up with. You can review and remove any instructions that are not relevant.

See examples of copilot-instructions.md files on GitHub at:

Step 2: Firewall Rules

When the GitHub Copilot Coding Agent completes your first assigned issue, you may notice a warning on the pull request description, such as:

GitHub Firewall Warning

This warning is actually a key security feature:

  • Imagine you’re working on a “top secret” private repository.

  • Copilot decides to make a web request to a public website to retrieve some data.

  • Copilot could have inadvertently leaked your private code to the public website!

While this is less of a concern in public repositories, this could still happen with GitHub secrets or other credentials.

If you want Copilot to be able to make web requests, you can review each firewall warning and configure rules to allow specific domains.

  1. Go to the Settings > Environments > copilot page on your GitHub repository.

  2. Scroll to the bottom, Environment variables section.

  3. Add a new COPILOT_AGENT_FIREWALL_ALLOW_LIST variable with a comma-separated list of domains you want to allow.

Some useful domains to allow include:

  • learn.microsoft.com – for Microsoft and .NET documentation.
  • nuget.org – to add new NuGet packages.
  • developer.apple.com – for iOS and Apple-specific documentation.
  • developer.android.com – for Android-specific documentation.

Note

$COPILOT_AGENT_FIREWALL_ALLOW_LIST should not have a trailing comma. See copilot-coding-agent/user-feedback#41 for more details.

Step 3: .github/workflows/copilot-setup-steps.yml

Building upon GitHub (a platform), the Copilot Coding Agent literally runs within GitHub actions. You have complete control over the environment in which it runs, giving you the ability to run steps before the firewall restrictions are applied:

  • Download and install additional tools or dependencies.

  • Restore NuGet packages or other steps that require network access.

  • Do an inital (working) build of the project.

This way Copilot has a working source tree, can make changes, build again (incrementally), run tests, etc.

As with the previous step, you can file a GitHub issue and let Copilot generate the copilot-setup-steps.yml file. Here’s an example:

# Setup copilot development environment 

Setup a `.github/workflows/copilot-setup-steps.yml` file, such as:

name: "Copilot Setup Steps"
on: workflow_dispatch
jobs:
  copilot-setup-steps:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      with:
        submodules: recursive
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '9.x'
    # TODO: Build the project

See `.github/DEVELOPMENT.md` for more details on how to build this project.

See: https://docs.github.com/en/copilot/customizing-copilot/customizing-the-development-environment-for-copilot-coding-agent

Note that Copilot currently only supports ubuntu-latest as its runtime OS. If your project needs Windows or macOS builds, Copilot may be somewhat restricted in what it can do. See (or upvote!) copilot-coding-agent/user-feedback#40 for more details.

One important detail: make sure that copilot-setup-steps.yml is configured to always complete, even if your build fails. This is done using continue-on-error: true.

- name: Run dotnet build
  id: copilot-build
  continue-on-error: true
  run: dotnet build -bl
- name: Upload logs
  uses: actions/upload-artifact@v4
  if: steps.copilot-build.outcome == 'failure'
  with:
    name: copilot-artifacts
    path: '**/*.binlog'

Copilot (or a human) might push a commit that breaks the build. If you leave a comment like @copilot fix error XYZ, it needs to be able get past its setup steps and actually fix the problem. Having a copy of the failed logs can also be useful for humans troubleshooting in the future.

Step 4: Setup MCP Servers (Optional)

A Model Context Protocol (MCP) server provides “tools” that Copilot can draw from to accomplish specific goals. In our experience, the Microsoft Learn Docs MCP Server is one of the most powerful tools available. This gives Copilot important context on many topics before modifying your code.

To set this up, go to your repository Settings > Copilot > Coding Agent > MCP Configuration:

{
  "mcpServers": {
    "microsoft-docs-mcp": {
      "type": "http",
      "url": "https://learn.microsoft.com/api/mcp",
      "tools": [ "*" ]
    }
  }
}

It is also a good idea to add the following guidance at the top of copilot-instructions.md:

## Development Guidelines

**Always search Microsoft documentation (MS Learn) when working with .NET, Windows, or Microsoft features, or APIs.** Use the `microsoft_docs_search` tool to find the most current information about capabilities, best practices, and implementation patterns before making changes.

Security Note

Just like the firewall allow list, the MCP server configuration is a potential security risk. Be sure to review any MCP server’s source code and/or documentation to decide if it is safe to use.

To setup GitHub Copilot Coding Agent end-to-end:

GitHub Copilot Copilot Coding Agent workflow

Note that much of this is optional, but Copilot can complete tasks faster and more reliably with a complete setup.

Storytime: AI’s Little White Lies

As a fun experiment, I used the Copilot Coding Agent to create a new .NET MAUI app called CatSwipe. It displays cat images from thecatapi.com, and lets you swipe left/no or right/yes (similar to a popular a dating app).

The trouble began, when I asked Copilot to take a screenshot of the running Android app. I was hoping it would do something like:

  1. Boot emulator: emulator -avd Pixel_7_API_35
  2. Install and run the app.
  3. adb shell screencap /sdcard/screenshot.png
  4. adb pull /sdcard/screenshot.png

Unfortunately, due to a configuration issue, it couldn’t start the Android emulator, and things quickly went off the rails! It instead decided to:

  1. Write a C# console app.
  2. Use System.Drawing to generate an image of an Android emulator and what it imagined the app would look like.
  3. Check in the screenshot to the repository, as if it was a real screenshot!

This experience led us to come up with strategies for keeping Copilot on track:

  • When assigning issues, always add links to documentation. Write the issue in a way that a junior engineer (human) could pick up the task.

  • Be terse and direct but not necessarily rude. Mention how you’d like something done and don’t expect Copilot to discover novel solutions on its own.

  • Anticipate what might go wrong for Copilot. Tell it to “give up” if it cannot complete a task and report the error message.

  • Write scripts for common tasks and put examples in copilot-instructions.md that Copilot can use as a reference.

When Copilot does the wrong thing, it likely needs more context or more instructions. Think of this as “debugging” your copilot-instructions.md file. Keep this in mind as you review pull requests from Copilot, ask it to update copilot-instructions.md during code review.

Conclusion

The GitHub Copilot Coding Agent is already showing strong potential in our day-to-day development. We’ve found it particularly effective for handling well-scoped, low-risk tasks. By leveraging Copilot for these “easy” issues, we save valuable engineering time and keep our team focused on more complex, high-impact work.

Copilot has been most successful in the dotnet/android repository, where we’ve specifically assigned it simpler refactoring-related tasks. In the past 28 days we’ve seen the following results with pull requests:

Author Count Merge % P50 time to merge
@copilot 17 82.4% 10:15:34
All others 49 87.8% 11:36:35

P50 is 50th percentile, or what you might think of as the most common time a PR could be merged. In dotnet/android, Copilot has been pretty successful compared to all other PRs.

In dotnet/maui, we’ve been more optimistic: assigning PRs we knew might be too tough for Copilot to complete:

Author Count Merge % P50 time to merge
@copilot 54 16.7% 10:15:22
All others 255 52.9% 14:36:47

Take these numbers with a grain of salt, as we have certainly been focusing a decent amount of time on Copilot. We could easily be giving Copilot PRs more attention as a result! Over the next several months, we should have a better picture of Copilot Coding Agent’s full impact on the product.

A few things that GitHub Copilot Coding Agent doesn’t do yet:

  • Comment @copilot do this on an existing PR opened by a human: This would be beneficial to fix tiny “nitpicks” instead of waiting for one of our contributors (potentially a customer) to make the change.

  • Support running on Windows or macOS: This is unfortunately a big need for .NET MAUI, as the product targets Windows and iOS. Ideally, we could detect the issue or pull request is specific to a platform, and programmatically choose which OS to run on.

As the tool improves, we expect to expand its role in our development process.

Author

Jonathan Peppers
Principal Software Engineer

I used to build apps in C#. Now build *tools* to build apps in C#. Engineer on the .NET Android / .NET MAUI team.

0 comments