{"id":57261,"date":"2025-07-09T10:15:00","date_gmt":"2025-07-09T17:15:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=57261"},"modified":"2025-07-09T10:15:00","modified_gmt":"2025-07-09T17:15:00","slug":"maui-team-copilot-tips","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/maui-team-copilot-tips\/","title":{"rendered":"How the .NET MAUI Team uses GitHub Copilot for Productivity"},"content":{"rendered":"<p>Around the time of Microsoft Build 2025, the new <a href=\"https:\/\/github.blog\/news-insights\/product-news\/github-copilot-meet-the-new-coding-agent\/\">GitHub Copilot\nCoding Agent<\/a> was unveiled and made widely available across\nGitHub. With AI evolving at a dizzying pace in recent months, we\nimagined a &#8220;dream scenario&#8221;:<\/p>\n<ul>\n<li>A GitHub issue is filed.<\/li>\n<li>The problem looks to have a straightforward solution.<\/li>\n<li>A team member assigns the issue to Copilot, giving it a few tips.<\/li>\n<li>Copilot generates a pull request with the fix (and tests).<\/li>\n<li>CI runs, tests pass, and the pull request is merged!<\/li>\n<\/ul>\n<p>This would dramatically reduce time and effort, freeing <em>human<\/em>\ndevelopers to focus on higher-value, complex problems&#8211;while Copilot\ntakes care of the repetitive work.<\/p>\n<p>But would this dream be a reality? In this post, we aim to share a\nbalanced perspective: highlighting both where Copilot has been\ngenuinely helpful, and when it completely missed the mark.<\/p>\n<p>In many ways, it already is. The .NET MAUI team has been actively\nusing Copilot to boost our productivity, and we&#8217;re excited to share\nsome practical tips for getting the most out of it.<\/p>\n<p>At the time of writing, Copilot has risen to be the #64 contributor all time in\ndotnet\/maui, which we expect to grow in the coming months:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/07\/copilot-contributions.png\" alt=\"GitHub Contribution Graph of Copilot\" \/><\/p>\n<h2>Step 1: <code>.github\/copilot-instructions.md<\/code><\/h2>\n<p>To start with the basics, we provide GitHub Copilot with some general context and guidance for our repository by including a <a href=\"https:\/\/docs.github.com\/en\/copilot\/customizing-copilot\/adding-repository-custom-instructions-for-github-copilot\"><code>copilot-instructions.md<\/code><\/a> file. By convention this file should be kept in the <code>.github<\/code> 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.<\/p>\n<p>The types of useful instructions we keep in this file include:<\/p>\n<ul>\n<li><strong>Project Context<\/strong>: What <em>is<\/em> this repo? Provide an overview of the\nproject and relevant background information.<\/li>\n<li><strong>Repository Structure<\/strong>: Describe the structure of the repository,\nwhat types of files go where, how to write tests, etc.<\/li>\n<li><strong>Coding Standards<\/strong>: If you are using non-default code formatting,\ngive specific examples of how you want your code to look.<\/li>\n<\/ul>\n<p>A great way to start is to simply ask the Copilot Coding Agent to\ngenerate this file for you.<\/p>\n<p>An example GitHub issue would be:<\/p>\n<pre><code class=\"language-markdown\">Go through this repo, review structure of project, source code, etc.\n\nAdditional docs to review about the product: https:\/\/learn.microsoft.com\/dotnet\/maui\n\nUpdate `.github\/copilot-instructions.md` to make Copilot more helpful going forward.\n\nInclude a note to update `copilot-instructions.md` with new instructions as the project evolves.\n\nSee: https:\/\/docs.github.com\/en\/copilot\/customizing-copilot\/adding-repository-custom-instructions-for-github-copilot <\/code><\/pre>\n<p>Assign to Copilot and see what it comes up with. You can review and\nremove any instructions that are not relevant.<\/p>\n<p>See examples of <code>copilot-instructions.md<\/code> files on GitHub at:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/maui\/blob\/main\/.github\/copilot-instructions.md\">dotnet\/maui<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/android\/blob\/main\/.github\/copilot-instructions.md\">dotnet\/android<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/macios\/blob\/main\/.github\/copilot-instructions.md\">dotnet\/macios<\/a><\/li>\n<\/ul>\n<h2>Step 2: Firewall Rules<\/h2>\n<p>When the GitHub Copilot Coding Agent completes your first assigned issue, you may notice a\nwarning on the pull request description, such as:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/07\/copilot-firewall.png\" alt=\"GitHub Firewall Warning\" \/><\/p>\n<p>This warning is actually a key security feature:<\/p>\n<ul>\n<li>\n<p>Imagine you&#8217;re working on a &#8220;top secret&#8221; private repository.<\/p>\n<\/li>\n<li>\n<p>Copilot decides to make a web request to a public website to\nretrieve some data.<\/p>\n<\/li>\n<li>\n<p>Copilot could have inadvertently leaked your private code to the\npublic website!<\/p>\n<\/li>\n<\/ul>\n<p>While this is less of a concern in public repositories, this could\nstill happen with GitHub secrets or other credentials.<\/p>\n<p>If you want Copilot to be able to make web requests, you can review\neach firewall warning and configure rules to allow specific domains.<\/p>\n<ol>\n<li>\n<p>Go to the <code>Settings &gt; Environments &gt; copilot<\/code> page on your GitHub repository.<\/p>\n<\/li>\n<li>\n<p>Scroll to the bottom, <code>Environment variables<\/code> section.<\/p>\n<\/li>\n<li>\n<p>Add a new <code>COPILOT_AGENT_FIREWALL_ALLOW_LIST<\/code> variable with a\ncomma-separated list of domains you want to allow.<\/p>\n<\/li>\n<\/ol>\n<p>Some useful domains to allow include:<\/p>\n<ul>\n<li><code>learn.microsoft.com<\/code> &#8211; for Microsoft and .NET documentation.<\/li>\n<li><code>nuget.org<\/code> &#8211; to add new NuGet packages.<\/li>\n<li><code>developer.apple.com<\/code> &#8211; for iOS and Apple-specific documentation.<\/li>\n<li><code>developer.android.com<\/code> &#8211; for Android-specific documentation.<\/li>\n<\/ul>\n<p><div class=\"alert alert-primary\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Note<\/strong><\/p><code>$COPILOT_AGENT_FIREWALL_ALLOW_LIST<\/code> should not have a trailing comma. See <a href=\"https:\/\/github.com\/copilot-coding-agent\/user-feedback\/issues\/41\">copilot-coding-agent\/user-feedback#41<\/a> for more details.<\/div><\/p>\n<h2>Step 3: <code>.github\/workflows\/copilot-setup-steps.yml<\/code><\/h2>\n<p>Building upon GitHub (a platform), the Copilot Coding Agent literally\nruns within GitHub actions. You have complete control over the\nenvironment in which it runs, giving you the ability to run steps\nbefore the firewall restrictions are applied:<\/p>\n<ul>\n<li>\n<p>Download and install additional tools or dependencies.<\/p>\n<\/li>\n<li>\n<p>Restore NuGet packages or other steps that require network access.<\/p>\n<\/li>\n<li>\n<p>Do an inital (working) build of the project.<\/p>\n<\/li>\n<\/ul>\n<p>This way Copilot has a working source tree, can make changes, build\nagain (incrementally), run tests, etc.<\/p>\n<p>As with the previous step, you can file a GitHub issue and let Copilot\ngenerate the <code>copilot-setup-steps.yml<\/code> file. Here&#8217;s an example:<\/p>\n<pre><code class=\"language-markdown\"># Setup copilot development environment \n\nSetup a `.github\/workflows\/copilot-setup-steps.yml` file, such as:\n\nname: \"Copilot Setup Steps\"\non: workflow_dispatch\njobs:\n  copilot-setup-steps:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Checkout repository\n      uses: actions\/checkout@v4\n      with:\n        submodules: recursive\n    - name: Setup .NET\n      uses: actions\/setup-dotnet@v4\n      with:\n        dotnet-version: '9.x'\n    # TODO: Build the project\n\nSee `.github\/DEVELOPMENT.md` for more details on how to build this project.\n\nSee: https:\/\/docs.github.com\/en\/copilot\/customizing-copilot\/customizing-the-development-environment-for-copilot-coding-agent<\/code><\/pre>\n<p>Note that Copilot currently only supports <code>ubuntu-latest<\/code> as its\nruntime OS. If your project needs Windows or macOS builds, Copilot may\nbe somewhat restricted in what it can do. See (or upvote!)\n<a href=\"https:\/\/github.com\/copilot-coding-agent\/user-feedback\/issues\/40\">copilot-coding-agent\/user-feedback#40<\/a> for more details.<\/p>\n<p>One important detail: make sure that <code>copilot-setup-steps.yml<\/code> is\nconfigured to <em>always<\/em> complete, even if your build fails. This is\ndone using <code>continue-on-error: true<\/code>.<\/p>\n<pre><code class=\"language-yml\">- name: Run dotnet build\n  id: copilot-build\n  continue-on-error: true\n  run: dotnet build -bl\n- name: Upload logs\n  uses: actions\/upload-artifact@v4\n  if: steps.copilot-build.outcome == 'failure'\n  with:\n    name: copilot-artifacts\n    path: '**\/*.binlog'<\/code><\/pre>\n<p>Copilot (or a human) might push a commit that breaks the build. If you\nleave a comment like <code>@copilot fix error XYZ<\/code>, it needs to be able get\npast its setup steps and actually fix the problem. Having a copy of\nthe failed logs can also be useful for humans troubleshooting in the\nfuture.<\/p>\n<h2>Step 4: Setup MCP Servers (Optional)<\/h2>\n<p>A Model Context Protocol (MCP) server provides &#8220;tools&#8221; that Copilot\ncan draw from to accomplish specific goals. In our experience, the\n<a href=\"https:\/\/github.com\/MicrosoftDocs\/mcp\">Microsoft Learn Docs MCP Server<\/a> is one of the most powerful\ntools available. This gives Copilot important context on many topics\nbefore modifying your code.<\/p>\n<p>To set this up, go to your repository <code>Settings &gt; Copilot &gt; Coding Agent &gt; MCP Configuration<\/code>:<\/p>\n<pre><code class=\"language-json\">{\n  \"mcpServers\": {\n    \"microsoft-docs-mcp\": {\n      \"type\": \"http\",\n      \"url\": \"https:\/\/learn.microsoft.com\/api\/mcp\",\n      \"tools\": [ \"*\" ]\n    }\n  }\n}<\/code><\/pre>\n<p>It is also a good idea to add the following guidance at the top of\n<code>copilot-instructions.md<\/code>:<\/p>\n<pre><code class=\"language-markdown\">## Development Guidelines\n\n**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.<\/code><\/pre>\n<p><div class=\"alert alert-primary\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Security Note<\/strong><\/p>Just like the firewall allow list, the MCP server configuration is a potential security risk. Be sure to review any MCP server&#8217;s source code and\/or documentation to decide if it is safe to use.<\/div><\/p>\n<p>To setup GitHub Copilot Coding Agent end-to-end:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/07\/github-copilot-copilot-coding-agent-workflow-scaled.png\" alt=\"GitHub Copilot Copilot Coding Agent workflow\" \/><\/p>\n<p>Note that much of this is optional, but Copilot can complete tasks\nfaster and more reliably with a complete setup.<\/p>\n<h2>Storytime: AI&#8217;s Little White Lies<\/h2>\n<p>As a fun experiment, I used the Copilot Coding Agent to create a new\n.NET MAUI app called <a href=\"https:\/\/github.com\/jonathanpeppers\/CatSwipe\">CatSwipe<\/a>. It displays cat images from\nthecatapi.com, and lets you swipe left\/no or right\/yes (similar to a\npopular a dating app).<\/p>\n<p>The trouble began, when I asked Copilot to take a screenshot of the\nrunning Android app. I was hoping it would do something like:<\/p>\n<ol>\n<li>Boot emulator: <code>emulator -avd Pixel_7_API_35<\/code><\/li>\n<li>Install and run the app.<\/li>\n<li><code>adb shell screencap \/sdcard\/screenshot.png<\/code><\/li>\n<li><code>adb pull \/sdcard\/screenshot.png<\/code><\/li>\n<\/ol>\n<p>Unfortunately, due to a configuration issue, it couldn&#8217;t start the\nAndroid emulator, and things quickly went off the rails! It instead\ndecided to:<\/p>\n<ol>\n<li>Write a C# console app.<\/li>\n<li>Use <code>System.Drawing<\/code> to <em>generate<\/em> an image of an Android emulator\nand what it imagined the app would look like.<\/li>\n<li>Check in the screenshot to the repository, as if it was a real\nscreenshot!<\/li>\n<\/ol>\n<p>This experience led us to come up with strategies for keeping Copilot\non track:<\/p>\n<ul>\n<li>\n<p>When assigning issues, always add links to documentation. Write the\nissue in a way that a junior engineer (human) could pick up the\ntask.<\/p>\n<\/li>\n<li>\n<p>Be terse and direct but not necessarily rude. Mention <em>how<\/em> you&#8217;d\nlike something done and don&#8217;t expect Copilot to discover novel\nsolutions on its own.<\/p>\n<\/li>\n<li>\n<p>Anticipate what might go wrong for Copilot. Tell it to &#8220;give up&#8221; if it\ncannot complete a task and report the error message.<\/p>\n<\/li>\n<li>\n<p>Write scripts for common tasks and put examples in\n<code>copilot-instructions.md<\/code> that Copilot can use as a reference.<\/p>\n<\/li>\n<\/ul>\n<p>When Copilot does the wrong thing, it likely needs more context or\nmore instructions. Think of this as &#8220;debugging&#8221; your\n<code>copilot-instructions.md<\/code> file. Keep this in mind as you review pull\nrequests from Copilot, ask it to update <code>copilot-instructions.md<\/code>\nduring code review.<\/p>\n<h2>Conclusion<\/h2>\n<p>The GitHub Copilot Coding Agent is already showing strong potential in\nour day-to-day development. We&#8217;ve found it particularly effective for\nhandling well-scoped, low-risk tasks. By leveraging Copilot for these\n&#8220;easy&#8221; issues, we save valuable engineering time and keep our team\nfocused on more complex, high-impact work.<\/p>\n<p>Copilot has been most successful in the dotnet\/android repository,\nwhere we&#8217;ve specifically assigned it simpler refactoring-related\ntasks. In the past 28 days we&#8217;ve seen the following results with pull\nrequests:<\/p>\n<table>\n<thead>\n<tr>\n<th>Author<\/th>\n<th style=\"text-align: right\">Count<\/th>\n<th style=\"text-align: right\">Merge %<\/th>\n<th style=\"text-align: right\">P50 time to merge<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>@copilot<\/td>\n<td style=\"text-align: right\">17<\/td>\n<td style=\"text-align: right\">82.4%<\/td>\n<td style=\"text-align: right\">10:15:34<\/td>\n<\/tr>\n<tr>\n<td>All others<\/td>\n<td style=\"text-align: right\">49<\/td>\n<td style=\"text-align: right\">87.8%<\/td>\n<td style=\"text-align: right\">11:36:35<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>P50 is 50th percentile, or what you might think of as the <em>most\ncommon<\/em> time a PR could be merged. In dotnet\/android, Copilot has been\npretty successful compared to all other PRs.<\/p>\n<p>In dotnet\/maui, we&#8217;ve been more <em>optimistic<\/em>: assigning PRs we knew\nmight be too tough for Copilot to complete:<\/p>\n<table>\n<thead>\n<tr>\n<th>Author<\/th>\n<th style=\"text-align: right\">Count<\/th>\n<th style=\"text-align: right\">Merge %<\/th>\n<th style=\"text-align: right\">P50 time to merge<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>@copilot<\/td>\n<td style=\"text-align: right\">54<\/td>\n<td style=\"text-align: right\">16.7%<\/td>\n<td style=\"text-align: right\">10:15:22<\/td>\n<\/tr>\n<tr>\n<td>All others<\/td>\n<td style=\"text-align: right\">255<\/td>\n<td style=\"text-align: right\">52.9%<\/td>\n<td style=\"text-align: right\">14:36:47<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Take these numbers with a grain of salt, as we have certainly been\nfocusing a decent amount of time on Copilot. We could easily be giving\nCopilot PRs more attention as a result! Over the next several months,\nwe should have a better picture of Copilot Coding Agent&#8217;s full impact\non the product.<\/p>\n<p>A few things that GitHub Copilot Coding Agent doesn&#8217;t do yet:<\/p>\n<ul>\n<li>\n<p><strong>Comment <code>@copilot do this<\/code> on an existing PR opened by a human<\/strong>:\nThis would be beneficial to fix tiny &#8220;nitpicks&#8221; instead of waiting\nfor one of our contributors (potentially a customer) to make the\nchange.<\/p>\n<\/li>\n<li>\n<p><strong>Support running on Windows or macOS<\/strong>: This is unfortunately a big\nneed for .NET MAUI, as the product targets Windows and iOS. Ideally,\nwe could detect the issue or pull request is specific to a platform,\nand programmatically choose which OS to run on.<\/p>\n<\/li>\n<\/ul>\n<p>As the tool improves, we expect to expand its role in our development\nprocess.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How the .NET MAUI Team uses GitHub Copilot for Productivity<\/p>\n","protected":false},"author":1345,"featured_media":57262,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,7233,7781,756],"tags":[4,568,7244,7870],"class_list":["post-57261","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-maui","category-ai","category-csharp","tag-net","tag-ai","tag-maui","tag-meai"],"acf":[],"blog_post_summary":"<p>How the .NET MAUI Team uses GitHub Copilot for Productivity<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/57261","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/1345"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=57261"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/57261\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/57262"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=57261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=57261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=57261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}