By now, you’ve probably heard of Microsoft Copilot, the innovative tool designed to enhance productivity by providing intelligent assistance across various Microsoft applications. Millions of users use Copilot inside of Windows, on Edge, through the Copilot mobile app, and even on Xbox. What you may not know is that the development team behind Copilot transitioned their backend from a monolithic architecture to a microservices-based approach in four months with a small team of developers. This team was one of the first internal Microsoft customers to use .NET Aspire. Since then, .NET Aspire has become an integral part of their onboarding and local dev experience.
Transitioning from Monolith to Microservices with .NET Aspire
Initially, the Copilot backend was built as a monolithic application. This approach allowed the team to get things up and running quickly without complicated dev environment setups. However, as Copilot’s popularity took off, the need for a more scalable and maintainable architecture became evident. The team decided to rebuild the backend as a suite of microservices, enabling different teams to have ownership over specific parts of the codebase and iterate independently.
Today, the Copilot team primarily leverages .NET Aspire for local development, but they do not use .NET Aspire for deployment.
To learn more about the transition, watch this video with Copilot architect, Pedram Rezaei, from .NET Conf 2024:
The team saw major improvements in their modernized application.
- The new backend is three times faster and significantly more efficient.
- Reduced the codebase to 5% of its original size while achieving feature parity.
- Leveraged .NET Aspire for rapid development and a seamless microservices architecture.
- Utilized features like distributed tracing and integrated Docker support to enhance development.
- Achieved over 99.999% reliability, crucial for a widely used service like Copilot.
Developer Onboarding
According to the Copilot team, one of the standout features of .NET Aspire is its ability to simplify the developer onboarding process. New team members can get up-and-running quickly, often within their first day. The setup process involves installing basic tools, configuring credentials, cloning the repository, and running a few commands to start the development environment. “You clone the repo and then you’re basically good to go. You just run dotnet build
to compile the project and then execute it with dotnet run
.” said Youssef Moussaoui, a member of the technical staff at Microsoft AI.
Local Development
The Copilot team continues to use .NET Aspire in their local development process to simplify their code, pull in key dependencies, and debug, inspect, and analyze their application running locally. The team is split across both Windows and Mac machines and use a variety of IDEs and editors, including VS Code. .NET Aspire standardizes a lot of the developer process despite their different OSs and tools.
Before .NET Aspire, getting the entire application up and running was not so easy. “It’s almost impossible to run multiple services locally in a dev machine, right? And to me, that’s the beauty of .NET Aspire. I can hit F5 and it launches the entire end to end of the product and I can run it,” said Pedram Rezaei, architect on Copilot.
The team also leverages .NET Aspire to simplify their code and debug more easily while doing local development. For example, Service Discovery in .NET Aspire helped “when we want to communicate with another microservice,” says Youssef. “We just use the name of the microservice and that’s helped simplify the code quite a bit.”
In addition to building their own microservices, .NET Aspire has enabled the team to more easily pull in dependencies, like Redis, for local development. “We have Azure Blob Storage resources. We have Azure Queue resources. We have all these Azure resources that we can easily set up using Aspire.”
The team further uses the .NET Aspire Dashboard for visualizing their running application during local development. “If you’re running into some issues, you can visualize them and try to figure out what’s going on,” says Youssef. The UI aspect of .NET Aspire’s dashboard was one of the driving factors that excited the team about using .NET Aspire.
The Copilot team initially developed a modular monolith that communicated with a few services. This approach was chosen for expediency, with the code structured to facilitate a transition to microservices as the application scaled. Over time, the necessity for this transition became clear, leading to the modular monolith being divided into multiple services. Aspire empowered the team to confidently scale from one to numerous services.
However, this shift introduced complexities in testing, with more moving parts, increased inter-service connections, and additional dependencies to manage .NET Aspire’s integration testing capabilities simplified this process, making it feasible. Consequently, the Aspire team implemented numerous enhancements to streamline the creation and diagnosis of distributed tests. These improvements included log capturing, enhanced reliability during test startup and shutdown, and other quality-of-life fixes that ensured resources were ready before sending requests.
These advancements not only benefited the Copilot team but also enhanced .NET Aspire’s own reliability. The same improvements are utilized to test .NET Aspire’s internal operations and various integrations.
Conclusion
.NET Aspire was a valuable tool in Microsoft Copilot’s backend transition and in the team’s everyday development. As the Copilot team continues to innovate and expand Copilot’s functionalities, .NET Aspire remains an indispensable tool, enabling them to deliver high-quality solutions efficiently and effectively.
It’s great to see .NET Aspire becoming widespread within Microsoft 🥳