Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
160347 stories
·
33 followers

Using the Camera in .NET MAUI with CameraView

1 Share

Learn how to use the .NET MAUI Community Toolkit CameraView control in your app, from initializing to creating the user interface.

A functionality that is common in mobile applications is the ability to work with the camera. Although the .NET MAUI framework includes classes and methods to use the camera, having a component that allows creating interfaces that offer additional functionality helps speed up application development. That is why in this article, we will examine the CameraView control of the .NET MAUI Community Toolkit.

What Is the CameraView Control

CameraView is a control within the CommunityToolkit.Maui.Camera package that helps perform tasks beyond taking photos and videos, allowing control of features such as flash, zoom, saving files and even hooks for reacting to different events.

This allows you to create experiences within your application without needing to navigate to an external application, giving you full control of the buttons, visual state and destination of captured files.

Using the CameraView Control in Your .NET MAUI Applications

Let’s demonstrate the CameraView control’s potential. To do this, in your project open the NuGet package manager and install the following packages:

CommunityToolkit.Maui.Camera
CommunityToolkit.Mvvm

The first package is the one we need to use the control and the APIs, while the second will allow using the MVVM pattern more easily in the project.

Once you have installed the camera control package, you will see a ReadMe.txt file with the steps to follow to correctly configure the control. This file tells us that we must go to MauiProgram.cs, adding the UseMauiCommunityToolkitCamera method for the purpose of initializing the control:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UseMauiCommunityToolkitCamera()           
...
    }
}

With the control registered, we are ready to try it.

Declaring Permissions Per Platform

Although the readme doesn’t specify it, it is well known that to use hardware features on physical devices you must request permission from the user to use them. In our case, it is necessary to request permissions to use the camera and the microphone, which must be configured differently on each platform.

For Android, you should open the file Platforms/Android/AndroidManifest.xml, adding:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" />
...
    <uses-permission android:name="android.permission.CAMERA" />

    <!--Optional. Only for video recording-->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

</manifest>

For iOS, you should open the file Platforms/iOS/Info.plist, adding the following privacy descriptions:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    ...

    <key>NSCameraUsageDescription</key>
    <string>PROVIDE YOUR REASON HERE</string>

    <key>NSMicrophoneUsageDescription</key>
    <string>PROVIDE YOUR REASON HERE</string>
</dict>
</plist>

For Windows no additional action is required.

Creating a ViewModel to Manage the Control

Once we’ve configured the project to request permissions from the user, let’s create the view model, which will allow managing the state of the control. For this, I created a class called MainViewModel with the following content.

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    string statusMessage = "Grant permissions to start the camera.";

    [ObservableProperty]
    string cameraName = "No camera selected";

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(HasPhoto))]
    string? lastPhotoPath;

    [ObservableProperty]
    string? lastVideoPath;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(RecordButtonText))]
    [NotifyPropertyChangedFor(nameof(RecordButtonColor))]
    bool isRecording;

    [ObservableProperty]
    bool isTorchOn;

    [ObservableProperty]
    float currentZoom = 1.0f;

    [ObservableProperty]
    float minimumZoom = 1.0f;

    [ObservableProperty]
    float maximumZoom = 4.0f;

    [ObservableProperty]
    CameraFlashMode selectedFlashMode = CameraFlashMode.Off;

    [ObservableProperty]
    CameraInfo? selectedCamera;

    public IReadOnlyList<CameraFlashMode> FlashModes { get; } = Enum.GetValues<CameraFlashMode>();

    public bool HasPhoto => !string.IsNullOrWhiteSpace(LastPhotoPath);

    public string RecordButtonText => IsRecording ? "Stop video" : "Record video";

    public Color RecordButtonColor => IsRecording ? Colors.Firebrick : Colors.DarkGreen;

    public void UpdateCameraInfo()
    {
        if (SelectedCamera is null)
        {
            CameraName = "No camera selected";
            return;
        }

        CameraName = $"{SelectedCamera.Name} ({SelectedCamera.Position})";
        MinimumZoom = Math.Max(1.0f, SelectedCamera.MinimumZoomFactor);
        MaximumZoom = Math.Max(MinimumZoom, SelectedCamera.MaximumZoomFactor);
        CurrentZoom = MinimumZoom;
    }

    partial void OnSelectedCameraChanged(CameraInfo? value)
    {
        UpdateCameraInfo();
    }
}

In the previous code, I created some properties that can help us manage controls in the UI, for example:

  • CameraName: To indicate which camera is selected
  • LastPhotoPath and LastVideoPath: To show the latest paths for photos and videos
  • IsRecording: To maintain the recording state
  • CurrentZoom, MinimumZoom and MaximumZoom: To control zoom values
  • OnSelectedCameraChanged(...) and UpdateCameraInfo(): To update values when there is a camera change

With the base viewmodel ready, it’s time to create the graphical interface.

Creating the UI for the Application

It’s time to create the graphical interface page for the application. We’ll do this by creating a ContentPage, replacing the content with the following:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage ...
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit">

    <Grid RowDefinitions="*,Auto" Padding="16" RowSpacing="12">
        <Border Grid.Row="0" StrokeThickness="0" BackgroundColor="Black">
            <Grid>
                <toolkit:CameraView
                    x:Name="Camera"
                    CameraFlashMode="{Binding SelectedFlashMode}"
                    IsTorchOn="{Binding IsTorchOn}"
                    SelectedCamera="{Binding SelectedCamera}"
                    ZoomFactor="{Binding CurrentZoom}" />

                <Border
                    Margin="12"
                    Padding="10,6"
                    BackgroundColor="#99000000"
                    StrokeThickness="0"
                    HorizontalOptions="Start"
                    VerticalOptions="Start">
                    <Label Text="{Binding CameraName}" TextColor="White" FontSize="12" />
                </Border>
            </Grid>
        </Border>

        <ScrollView Grid.Row="1" MaximumHeightRequest="360">
            <VerticalStackLayout Spacing="14">
                <Label Text="CameraView media demo" FontSize="22" FontAttributes="Bold" />
                <Label Text="{Binding StatusMessage}" />

                <Grid ColumnDefinitions="*,*" ColumnSpacing="12">
                    <Button Text="Take photo" Clicked="OnTakePhotoClicked" />
                    <Button
                        Grid.Column="1"
                        Text="{Binding RecordButtonText}"
                        Clicked="OnRecordVideoClicked"
                        BackgroundColor="{Binding RecordButtonColor}" />
                </Grid>

                <Grid ColumnDefinitions="Auto,*" ColumnSpacing="12" RowDefinitions="Auto,Auto,Auto">
                    <Label Text="Zoom" VerticalOptions="Center" />
                    <Slider
                        Grid.Column="1"
                        Minimum="{Binding MinimumZoom}"
                        Maximum="{Binding MaximumZoom}"
                        Value="{Binding CurrentZoom}" />

                    <Label Grid.Row="1" Text="Torch" VerticalOptions="Center" />
                    <Switch Grid.Row="1" Grid.Column="1" IsToggled="{Binding IsTorchOn}" HorizontalOptions="Start" />

                    <Label Grid.Row="2" Text="Flash" VerticalOptions="Center" />
                    <Picker
                        Grid.Row="2"
                        Grid.Column="1"
                        ItemsSource="{Binding FlashModes}"
                        SelectedItem="{Binding SelectedFlashMode}" />
                </Grid>

                <Image
                    Source="{Binding LastPhotoPath}"                    
                    Aspect="AspectFill"
                    IsVisible="{Binding HasPhoto}" />

                <Label Text="{Binding LastPhotoPath}" FontSize="12" LineBreakMode="TailTruncation" />
                <Label Text="{Binding LastVideoPath}" FontSize="12" LineBreakMode="TailTruncation" />
            </VerticalStackLayout>
        </ScrollView>
    </Grid>

</ContentPage>

In the previous code, we can highlight a few things:

  • The namespace xmlns:toolkit is used so that the control can be used via toolkit:CameraView.
  • Properties such as CameraFlashMode, IsTorchOn, SelectedCamera and ZoomFactor are used, which will be connected to the viewmodel’s properties.
  • We use events linked to the code behind OnTakePhotoClicked and OnRecordVideoClicked, because we work with capture APIs and streams.

Connecting the Content Page with the ViewModel

Once we have created the XAML code for the graphical interface, it’s time to connect it with the view model, as well as create the missing event handlers. To do this, replace the code behind of the XAML page with the following content:

public partial class MainPage : ContentPage
{
    readonly MainViewModel viewModel = new();
    Stream recordingStream = Stream.Null;

    public MainPage()
    {
        InitializeComponent();
        BindingContext = viewModel;

        Camera.MediaCaptureFailed += OnMediaCaptureFailed;
    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();
        await RequestCameraPermissions();
        await LoadCameras();
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        Camera.MediaCaptureFailed -= OnMediaCaptureFailed;
        recordingStream.Dispose();
    }

    private async Task RequestCameraPermissions()
    {
        var cameraStatus = await Permissions.RequestAsync<Permissions.Camera>();
        if (cameraStatus is not PermissionStatus.Granted)
        {
            viewModel.StatusMessage = "You need to grant camera permission.";
            return;
        }

        try
        {
            var microphoneStatus = await Permissions.RequestAsync<Permissions.Microphone>();
            if (microphoneStatus is not PermissionStatus.Granted)
            {
                viewModel.StatusMessage = "You need to grant microphone permission to record video.";
            }
        }
        catch (FileNotFoundException) when (OperatingSystem.IsWindows())
        {
            viewModel.StatusMessage = "On unpackaged Windows apps, microphone permission may require a packaged app.";
        }
    }

    private async Task LoadCameras()
    {
        try
        {
            var cameras = await Camera.GetAvailableCameras(CancellationToken.None);
            viewModel.SelectedCamera = cameras.FirstOrDefault();
            viewModel.UpdateCameraInfo();
        }
        catch (Exception ex)
        {
            viewModel.StatusMessage = $"No cameras were found: {ex.Message}";
        }
    }

    private void OnMediaCaptureFailed(object? sender, MediaCaptureFailedEventArgs e)
    {
        viewModel.StatusMessage = $"Capture error: {e.FailureReason}";
    }
}

In the previous code you can notice some interesting things:

  1. As indicated by the CameraView documentation, permissions must be requested manually from the user to use the camera and microphone, in addition to the per-platform configuration. We do this when the page starts through the method RequestCameraPermissions().
  2. It is possible to obtain the list of available cameras through Camera.GetAvailableCameras.
  3. In case an error occurs, we use the event OnMediaCaptureFailed to handle it.

This is just the beginning. Now, let’s look at how to take a photo and a video.

Taking a Photo with the CameraView Control

In the code-behind, we need a method that allows taking a photograph when pressing a button. To achieve this, we will do it through the following code:

private async void OnTakePhotoClicked(object? sender, EventArgs e)
{
    try
    {
        await using var photoStream = await Camera.CaptureImage(CancellationToken.None);
        var photoPath = Path.Combine(FileSystem.AppDataDirectory, $"photo-{DateTime.Now:yyyyMMdd-HHmmss}.jpg");

        await using var fileStream = File.Create(photoPath);
        await photoStream.CopyToAsync(fileStream);

        viewModel.LastPhotoPath = photoPath;
        viewModel.StatusMessage = "Photo saved successfully.";
    }
    catch (Exception ex)
    {
        viewModel.StatusMessage = $"Unable to take photo: {ex.Message}";
    }
}

In the previous method, we use Camera.CaptureImage to take a photograph, which returns a stream that we store locally. Next, we create a path using a safe location through FileSystemAppDataDirectory, which we use to save the stream with File.Create and CopyToAsync. Finally, we update the preview image and display the created path thanks to LastPhotoPath and StatusMessage.

Recording a Video with CameraView

To implement the recording process, we are going to create three methods. One controls the logic to know whether it is recording, starting or stopping the recording. The other two will allow starting and stopping the recording. The code looks as follows:

private async void OnRecordVideoClicked(object? sender, EventArgs e)
{
    if (viewModel.IsRecording)
    {
        await StopRecording();
        return;
    }

    await StartRecording();
}

private async Task StartRecording()
{
    try
    {
        var videoPath = Path.Combine(FileSystem.AppDataDirectory, $"video-{DateTime.Now:yyyyMMdd-HHmmss}.mp4");
        recordingStream = File.Create(videoPath);

        await Camera.StartVideoRecording(recordingStream, CancellationToken.None);

        viewModel.LastVideoPath = videoPath;
        viewModel.IsRecording = true;
        viewModel.StatusMessage = "Recording video...";
    }
    catch (Exception ex)
    {
        await recordingStream.DisposeAsync();
        recordingStream = Stream.Null;
        viewModel.StatusMessage = $"Unable to start video recording: {ex.Message}";
    }
}

private async Task StopRecording()
{
    try
    {
        await Camera.StopVideoRecording(CancellationToken.None);
        await recordingStream.DisposeAsync();

        recordingStream = Stream.Null;
        viewModel.IsRecording = false;
        viewModel.StatusMessage = "Video saved successfully.";
    }
    catch (Exception ex)
    {
        viewModel.StatusMessage = $"Unable to stop video recording: {ex.Message}";
    }
}

In the previous code, the methods do the following:

  • OnRecordVideoClicked(...): Through the property IsRecording, it determines which method should be invoked when the user presses the button in the UI.
  • StartVideoRecording(): It obtains a path using the current time to name the file and creates a stream at that location. Then, it uses the method Camera.StartVideoRecording to start the recording. It is important to keep the stream open while the recording is active.
  • StopRecording: This ends the capture using Camera.StopVideoRecording. After stopping the recording, the stream is closed to release the file.

Testing the Application

After adding all the previous code to the application, it’s time to test the functionality. When running the application, we get the following result:

CameraView showing a live camera preview on screen

You can see that all features work correctly, and that it’s possible to take photos and videos within the same application.

Conclusion

In this article you have learned about the CameraView control from the Community Toolkit, and how to integrate it into your own .NET MAUI applications. We went step by step to initialize it, configure permissions, create the view model and the user interface. With what you’ve learned, you can now create your own experiences using the control in more complex scenarios.

Read the whole story
alvinashcraft
20 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Designing product simplicity for the AI era

1 Share

When I returned from maternity leave in March 2026, I felt like I had entered an alternate universe. I rejoined a newly restructured organization, moved to a different team, and was managing a new portfolio of products and people within Azure Developer Services. 

I expected to spend time learning a new product space, but I didn’t expect to spend time relearning what it means to be a designer. 

The teams around me were already using GitHub Copilot and agents in their daily work. Designers on my team were moving beyond making static mockups and clickable prototypes in Figma. Some were contributing directly to production code using VS Code or the GitHub Copilot CLI. The traditional boundary between design and engineering seemed more blurred than ever. 

Even though I had spent portions of my leave experimenting with AI, I was still blown away by how quickly things had changed in such a short period of time.

> The same forces that were helping teams execute faster were also making it harder to achieve consistency across surfaces.

As I acclimated to my new surroundings, I found myself asking a lot of obvious questions (my kinder rebrand of “dumb” questions). Designers, engineers, and product teams across Azure Developer Services generously shared context, taught me new tools, and helped me understand how products fit together across CoreAI. As I learned more about individual products, I began to see a broader pattern. The same forces that were helping teams execute faster were also making it harder to achieve consistency across surfaces.

Beyond shared components 

CoreAI is Microsoft’s organization bringing together AI platforms, developer tools, and services—including Microsoft Foundry and Azure Developer Services—to help developers build and operate AI-powered applications. As this portfolio has grown rapidly to meet the evolving needs of AI-empowered developers, so has the number of offerings outside Azure, including the Microsoft Foundry portal and standalone Azure Developer Services portals. Each product serves a different purpose, but we want customers to experience and use them together as a platform. 

As our teams increasingly built portals, a challenge emerged: 

How do independently built products feel like parts of the same ecosystem? 

Many of these portals were already built using Fluent 2, the latest version of Microsoft’s design system. In fact, it was a requirement for any standalone portal to be built using this framework. That gave teams access to the same accessible, high-quality components and visual foundations. But we realized that even though we were using the same atomic components, our suite of products felt disjointed

> Even though we were using the same atomic components, our suite of products felt disjointed.

Think of a set of LEGO® bricks. The set can include the same color, size, and shaped pieces. However, if you give three people the same materials and ask them to build a house, they’ll all end up with something that looks pretty different.

Illustrations by Owen Richard.
Illustrations by Owen Richard.

The same thing had happened with the portals across CoreAI. Teams used the same buttons, tables, and controls while still creating dramatically different navigation models, page layouts, workflows, detail views, and dashboards. Each decision may make sense locally, but when you zoom out and look at the services as a family, you see many points of differentiation that don’t serve a purpose. In other words, the differences are not earned. This happens when humans are making decisions—and it’s even more of a problem when agents are at the helm, since they leverage historical choices that might not have been correct in the first place. 

For customers moving between related products, that friction shows up as cognitive overhead. People find themselves repeatedly learning and relearning patterns that essentially solve the same problems. What customers experience as “complexity” is often just unnecessary differentiation.

A familiar problem 

Before Microsoft, I spent several years at IBM, where I worked with Carbon, IBM’s enterprise design system. 

In addition to providing a collection of components, Carbon was an organizational strategy for creating coherence across a large and growing portfolio of products built by different teams. At IBM, I saw firsthand how a shared design system helps teams align as products evolve by giving them a common foundation while preserving room to solve distinct customer needs. Without that shared foundation, even reasonable local decisions can accumulate into complexity for customers. 

> “Inconsistency compounds.”
– Dave Chan

 As Foundry Product Designer Dave Chan poignantly observed, “Inconsistency compounds.” 

That experience shaped how I understood what I saw across CoreAI. Our teams didn’t lack talented designers, strong engineering practices, or a shared component foundation. Fluent 2 already provided much of that. The challenge was creating coherence across a rapidly expanding ecosystem of products while preserving the flexibility individual teams needed to solve unique customer problems.

> AI was changing how the software itself was being built and who—or what—was consuming the design system.

In many ways, CoreAI was facing a modern version of a challenge enterprise software organizations have wrestled with for decades. The difference was that, this time, AI was changing how the software itself was being built and who—or what—was consuming the design system.

Product simplicity as a strategic priority 

This spring, EVP of CoreAI Jay Parikh introduced product simplicity as a strategic focus area. He challenged us to reduce unnecessary complexity and create experiences that are easier to understand, learn, and trust. 

CVP of CoreAI Design John Maeda has explored this focus area and teased it out into four complementary disciplines. These pillars all contribute to the overarching notion of product simplicity: 

  1. Decision Making: Build the right thing through deep customer understanding.
  2. Engineering Craft: Build and refine resilient, high-quality systems.
  3. Coherence Making: Create consistency across experiences.
  4. Product Craft: Deliver thoughtful, polished experiences customers value.

The challenge we were facing heavily aligned with coherence making. As products evolved independently, how could they still feel connected? That’s a question that a traditional design system could solve. More importantly, how could they continue feeling connected as AI accelerated the speed at which software was being created? That’s a question for this new era.

Learning from existing success 

Fortunately, we weren’t starting from scratch. Teams working on Microsoft Foundry had already spent significant time solving similar problems. In preparation for its Next Gen release, a group of contributors developed a design system for Foundry, with Senior Designer Owen Richard playing a leading role in shaping its vision for developer audiences.  

This system delivered a smooth, clean, and modernized experience. It was further influenced by Foundry’s sister products, GitHub and Visual Studio Code, so the team crafted components and patterns that felt familiar across these canvases. Functionally, the team leveraged the best parts of Fluent under the hood, so they wouldn’t waste time reinventing the wheel. The result was a curated collection of components forked from Fluent and styled in a design language specific to Foundry. UX Engineer James Bradford refined the production components for this system, ensuring they were accessible to coding agents. In parallel, the UX engineering team also created a prototyping sandbox called Mini Foundry, which empowered designers and PMs to prototype in code while avoiding the complexities of Foundry’s production codebase. 

Through this exercise, Foundry defined variables that emerged through customer feedback, iteration, and the practical realities of building a complex product at scale.

Rather than creating yet another independent design system for Azure-based experiences, we saw an opportunity to build on the Foundry design team’s extensive work. That is the genesis of the CoreAI Design System: a framework to unify the suite of services under the CoreAI umbrella. 

The goal has never been to make every product identical. Rather, our goal was to establish shared expectations around common experiences. There’s no reason why every portal should have a different create flow. People moving between products should encounter familiar approaches to tables, detail pages, navigation models, and other recurring patterns.  

> Consistency—or lack thereof—was the symptom of a problem that simplicity could cure.

Designers and engineers should start from common foundations rather than reinventing solutions repeatedly. Not only does this simplicity improve the customer’s experience, it also saves unnecessary production toil spent reimagining patterns over and over again in each portal experience. 

Consistency—or lack thereof—was the symptom of a problem that simplicity could cure. 

Product simplicity for humans and agents 

As we worked toward product simplicity, we had to acknowledge that the way software is created has changed drastically—and it continues to do so. 

Historically, design systems were built primarily for humans. Designers worked in design tools like Figma and engineers worked in code. Design engineers and frontend developers often bridged the gap, translating design intent into production implementation. 

That model is evolving. Today, designers can describe a change to their Copilot and receive a working implementation. The artifact moving between design and engineering is no longer always a mockup or specification. Increasingly, it is code. 

If product simplicity depends on consistent implementation across products, and AI systems are participating in implementation, then design guidance must be consumable by more than just people. It must be consumable by the agents helping build the experience as well. This understanding influenced how we approached the CoreAI Design System. 

Rather than only creating a traditional component library accompanied by documentation, our goal is to design a system that supports both human decision-making and AI-assisted development workflows. This strategy will support designers at varying stages of their product teams’ AI readiness.

Illustrations by Owen Richard.
Illustrations by Owen Richard.

If you’re operating with the clickable prototype handoff, you can use our Figma library as a resource. The CoreAI Figma library is the more traditional repository of guidance and redlined componentry that designers are highly familiar with. 

If you’re making changes in code, you’ll be able to connect to our MCP server. To make this possible, we’re adding a context layer—a set of references and metadata that both humans and agents can use to interpret guidance and adapt it to their specific scenarios. This context layer ensures that recommendations aren’t just static rules but living guidance that adjusts to the needs of different products, teams, and implementation environments. 

Our MCP server provides access to getting started guidance, usage examples, implementation recommendations, and pattern documentation. This is currently experimental, and designers are testing this methodology as I write and as you read. Through this effort, James Bradford and other design engineers made notable revelations. 

  • API documentation proved to be one of the least valuable pieces of information. Agents can often understand component APIs directly from installed packages and TypeScript definitions. What they struggle with is intent. When should a pattern be used? What approach should be used and why? Examples, constraints, and guidance often matter more than API signatures.
  • Progressive disclosure benefits agents just as much as people. Instead of organizing knowledge into large documents, guidance is broken into smaller focused topics connected through references and indexes. This lets agents retrieve only the information relevant to the task at hand, reducing noise and improving implementation quality.

One goal of our system is to reduce the distance between design intent and production code. Instead of treating handoff as a static artifact, we want designers to be able to describe changes, work with AI-assisted tools, and generate experiences built from the same foundations used in production. In that world, the design system becomes less of a reference manual and more of an active source of truth shared by designers, PMs, engineers, and agents. By delivering our system through various mechanisms, we also acknowledge that not all components should be distributed the same way. Some patterns are more appropriately delivered as reusable components. Others work better as template examples that teams can fork to adapt to their own environments. Or maybe a team just needs examples demonstrating preferred approaches to common product scenarios. 

> Ultimately, consistency in service of our customers is what product simplicity is all about.

By designing the system around how both people and agents consume information, we increased the likelihood that shared patterns would be applied consistently across products. And ultimately, consistency in service of our customers is what product simplicity is all about.

Building a community around simplicity 

Creating guidance for foundational elements was only part of the work. We needed a model that supported continuous addition and refinement for high-impact patterns. Designers and design engineers from Foundry and Azure Developer Services formed a v-team (James Bradford, Alex Britez, Will Eastler, Dana Lachman, Owen Richard, Kham Udom, and myself) to continue building out pattern guidance that had the highest impact and highest usage across our canvases. 

To ensure our solutions addressed a variety of use cases, we recruited volunteers from multiple product areas for month-long sprints dedicated to formalizing and documenting these patterns. By using product designers to build out our guidance, we ensure they account for realistic scenarios that they’ve seen in their own products or peer products. The first sprint tackled data grids and detail pages, with Foundry designers Amy Chen, Dasi Fletcher, and Thoa Nguyen leading the effort. Their success set the stage for a second sprint, where Azure Developer Services designers Kristin Holifield and Xiaowei Jiang created templates for a complex but high-impact pattern: create flows. 

To maintain momentum and foster collaboration, we established weekly checkpoints and open office hours. These sessions provide a regular forum for volunteers and other design system users to: 

  • Ask questions about ongoing work or upcoming sprints
  • Share feedback and lessons learned
  • Exchange resources and best practices
  • Surface new pattern needs or challenges

When designers identify a pattern that needs attention, they can submit a ticket to our GitHub board. We triage these requests based on priority for inclusion in future sprints. For smaller contributions that don’t require a full sprint, designers are encouraged to submit ad hoc improvements directly to the system. 

Rather than treating consistency as something imposed by a central team, we see it as a capability developed collectively across the organization. 

While there’s still much to do, we can celebrate early wins and signs of success. What once could have taken months can now be accomplished in a couple days. When partners have the time to grant repo access and review PRs, Azure portal teams can adopt the baseline npm package for the design system in just a few days. In June 2026, no Azure portals were using the CoreAI Design System. In August, over five have the npm package installed, and that number is increasing as we learn about additional portal offerings. Stay tuned in to see how our portals gain coherence across experiences as they release publicly. The design system in and of itself is growing day by day as we support its expansion with dedicated sprints and ongoing contributions from other designers.

Simplicity as a competitive advantage 

The CoreAI Design System is ultimately not a story about components, documentation, or design tooling. Rather, it’s a story about product simplicity. 

We understood that every unnecessary difference creates friction. Every interaction pattern customers must relearn increases their cognitive load. Every fragmented experience makes a portfolio feel more complicated than complex. 

As AI lowers the barrier of creating new features and capabilities, experience quality becomes an increasingly important differentiator. Customers have more choices than ever before. The products that succeed won’t simply be the most powerful. They’ll be the products people can understand, learn, trust, and use with confidence. 

Product simplicity is how we get there. And increasingly, achieving that simplicity means designing systems that can be understood and optimized not only by people, but also by the agents helping people build the future.

Acknowledgements 

I’d be remiss not to name the folks who made significant contributions to this work in production, guidance, and leadership: James Bradford, Alex Britez, Will Eastler, Dana Lachman, John Maeda, Owen Richard, and Quirine van Walt Meijer.


All em dashes have been intentionally used by this post’s human author, who currently leads the design team for Azure Developer Services in CoreAI.

The post Designing product simplicity for the AI era appeared first on Command Line.

Read the whole story
alvinashcraft
24 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Aspire 13.5 Updates Dashboard

1 Share
Read the whole story
alvinashcraft
30 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Composable Agentic: Standardize Capabilities, Not Agents

1 Share

The field of AI and LLMs is moving faster than any technology I've worked with. New models, tools, and patterns keep arriving, and that speed has to be part of your AI strategy.

The web evolved quickly. HTML5 added new APIs, JavaScript gained new capabilities, languages released new versions, and cloud platforms changed how we built and operated software. None of it compares to the pace of AI right now.

The agent you standardize on today may not be the one you want in three months.

I've seen the same problem across several companies. Large organizations need governance and common practices, but cannot replace internal platforms every time the AI space changes. Startups want to use the newest tools, but can trap themselves inside a quick custom solution that starts aging almost as soon as it's adopted.

Both benefit from the same strategy: standardize the capabilities people need, not the agent they use.

I call that strategy Composable Agentic.

Agents Should Be Replaceable Clients

Composable Agentic treats an agent as a replaceable client. Codex, Claude Code, GitHub Copilot in VS Code, Gemini CLI, an open source CLI coding agent, a desktop app, a CRM assistant, and a finance system's built-in chat can all provide different user experiences. The durable part is what your company lets the agent know and do.

That includes capabilities such as:

  • Team processes and company-wide skills.
  • Project and service knowledge and documentation.
  • User, team, project, and organization memory.
  • Access to systems such as source control, tickets, designs, CRM, finance, and more.
  • Quality checks, test workflows, and review criteria.
  • Identity, permissions, approvals, and audit data.

People should be able to choose the tool that fits their role or current task without hearing, "That company capability only exists in the other agent."

A backend engineer may prefer a CLI agent. A QA engineer may work in VS Code with Copilot. A frontend engineer may use a desktop coding agent. Sales may stay in an agentic CRM, while finance uses the assistant built into its financial system. Their interfaces should be different. The company's useful knowledge and procedures should not disappear when the interface changes.

Agent Capability Plane

The durable company layer underneath the agents, I call the Agentic Capability Plane. Composable Agentic is the architectural principle: keep the functionality in the capability plane portable, governed, and quickly replaceable one part at a time.

Your agent should be a browser, not the web.

People have strong browser preferences, but switching browsers does not remove the web. Switching agents should not remove the company.

Where Lock-In Actually Happens

Changing the model endpoint is the easy form of portability. The harder lock-in grows around it.

It builds in small steps. A team puts its best QA prompt into one employee's local VS Code setup. Service knowledge ends up only in one vendor's memory feature. A custom agent defines every company tool inside its own codebase. Review rules, approval flows, and evaluation data only run in one harness.

After enough of this, changing the agent means rebuilding how the company works.

A conventional setup often ends up looking like this:

flowchart TB subgraph AGENT["Agent (ex: Repo + VS Code)"] direction TB AGENTS("AGENTS.md") AGENT_SKILLS("Skills") DOCS("Local docs") CLI("CLI tools") end

AGENT == "npx install" ==> SKILLS("Skills")
AGENT ==> MCP_INTERNAL("Internal MCP")
AGENT ==> MCP_PUBLIC("Public MCP")

There is nothing automatically wrong with this setup. Built-in capabilities are often why you choose a product. The problem starts when your company adds reusable, shared capabilities inside the same boundary. Every useful addition raises the cost of leaving.

The alternative keeps the company-owned parts outside the client:

flowchart LR subgraph AGENTS["Agents"] direction TD CLI_AGENT["CLI coding agent"] DESKTOP_AGENT["Desktop app"] TOOL_CHAT["Third-party tool chat"] end

CLI_AGENT ==> CAPABILITIES
DESKTOP_AGENT ==> CAPABILITIES
TOOL_CHAT ==> CAPABILITIES
AGENTS -- "npx skill" --> INTERNAL_SKILLS
AGENTS -- "npx skill" --> PUBLIC_SKILLS

subgraph CAPABILITIES["Agentic Capability Plane"]
    direction TD
    INTERNAL_SKILLS("Internal Skills")
    PUBLIC_SKILLS("Public Skills (Mirror)")
    CLI("CLI tools + scripts")
    MEMORY("Portable memory")
    MCP("MCP Servers")
    SUB_AGENTS("Specialist sub-agents")
end

subgraph INTERNAL_SYSTEMS["Internal Systems"]
    INFRA["Cloud Infrastructure"]
    KB["Knowledge base"]
    API["Internal API"]
end

subgraph THIRD_PARTY["3rd Party Systems"]
    TICKETS["Tickets system"]
    VCS["Version control system"]
end

CLI --> INFRA
MEMORY --> KB
MCP --> TICKETS
MCP --> VCS
INTERNAL_SKILLS -- "REST API" --> API

"Nothing embedded" needs one qualification. Each product still has its model loop, interface, permission model, and useful native features. The diagram means that company-owned capabilities do not depend on private code inside one client.

What Is Portable Today

This architecture no longer requires inventing every interface yourself. Several different standards now cover different parts of the problem.

NeedCurrent building blockWhat it does not solve
Project instructionsAGENTS.mdCompany-wide memory, live data, and enforcement
Reusable proceduresAgent SkillsSystem access and hard security boundaries
Portable packagingAgent PluginsOne permission model, sandbox, marketplace, or user experience
Live tools and dataModel Context ProtocolShared procedures, complete memory, and agent-to-agent delegation
Remote specialist agentsAgent2Agent ProtocolShared company memory or tool access by itself
Cross-tool telemetry fieldsOpenTelemetry GenAI semantic conventionsYour company's quality bar and approval policy

These standards solve separate problems. Declaring that MCP solves agent portability is like declaring that HTTP solves the web. It is an important connection layer, not the complete system. The boundaries will keep moving, too. The MCP specification now supports optional extensions, and a working group is defining Skills over MCP for serving skills through MCP servers.

Agent Skills are particularly useful for company procedures. A skill is a directory with a SKILL.md file and optional scripts, references, and assets. Compatible agents can discover the skill, load its main instructions when needed, and retrieve the heavier material only when the task calls for it.

The SKILL.md format is shared, but discovery is not standardized yet. Claude Code looks in .claude/skills, while Codex looks in .agents/skills, so a shared skill may still need symlinks, copies, or an installer before every agent can see it. I think this has to converge soon. An open format loses much of its value if every client makes teams install it differently.

The shared procedure can be as small as teaching agents how your company uses CLI tools. One internal skill can tell every compatible agent when to prefer rg, jq, gh, or an approved context-compaction tool, which flags reduce noisy output, and where those tools are available. A new agent then picks up the same habits without another round of local prompt tuning.

You can keep private skills in an internal repository and review changes like code. Public skills can come from sources such as skills.sh through npx skills add, after you inspect what you are installing. The source matters less than the boundary: the skill is an artifact the company can own, version, test, and expose to more than one client.

Agent Plugins 1.0 goes one step further by defining a portable package for Skills and MCP server configuration. It deliberately leaves permissions, trust policy, sandboxing, installation, and client-specific behavior to the client. That is evidence for the architecture, but it is not a complete enterprise platform.

Share the QA Capability, Not the QA Prompt

Take a company that has spent years improving how it tests web applications. The QA process includes accessibility checks, browser coverage, test-data rules, known regression areas, severity definitions, and a report format that product teams understand.

If all of that lives as a prompt on one QA engineer's laptop, the company does not own a QA capability. It owns a good local setup.

A composable version could contain:

company-agentic-capabilities/
├── AGENTS.md
├── skills/
│   └── qa-web-application/
│       ├── SKILL.md
│       ├── references/
│       │   ├── accessibility.md
│       │   ├── browser-matrix.md
│       │   └── severity-model.md
│       ├── scripts/
│       │   └── run-deterministic-checks.ps1
│       └── assets/
│           └── qa-report-template.md
├── evals/
│   └── qa-web-application/
└── mcp.json

The skill explains when to use the capability and how the company performs the work. Its scripts run deterministic checks. MCP connections retrieve the ticket, design context, test history, and current deployment. The evals check whether changes to the skill still produce reports with the required evidence and severity rules.

Now a QA engineer can invoke it from VS Code. A frontend engineer can run it from a CLI agent before opening a pull request. A backend engineer can use the same capability when a change crosses the API and user interface. They may use different models and get differently worded reports, but they follow the same company procedure and acceptance criteria.

That is a much stronger standard than telling everyone to use the same chat window.

A skill should expose details progressively instead of loading the complete QA handbook into every conversation. Token Maxing for AI Coding Agents covers how I keep the working context useful without starving the agent.

Reuse Specialist Agents Without Making Them the Boundary

Some capabilities need more than a skill. A release-readiness review may need a preconfigured specialist with read-only access to source code, CI results, tickets, security findings, and deployment history. A finance review may need an agent with an accounting-policy skill and tightly scoped access to the ledger.

Preconfigured subagents are useful, but their definitions are still commonly tool-specific. Agent Plugins 1.0 standardizes Skills and MCP servers, not one portable subagent format. Treat the subagent as an implementation behind the capability rather than the capability itself.

There are two practical options:

  • Let each compatible client start its own subagent with the shared skill, tools, and acceptance criteria.
  • Expose a governed specialist agent as a remote capability through a protocol such as A2A, then let several clients delegate to it.

The first option keeps execution close to the user's chosen tool. The second makes sense when the specialist needs central permissions, an expensive runtime, or a controlled environment. In both cases, the caller should ask for "release readiness" and receive the same required evidence. It should not need to know which framework implemented the reviewer.

This also gives the company a migration path. You can start with a skill. Add deterministic scripts when the process becomes repeatable. Move it behind a central specialist agent when access or scale requires it. The user-facing contract can stay the same.

Memory Should Not Belong to the Chat Window

Memory creates some of the strongest lock-in because it improves quietly over time. A user teaches an agent how they like reports written. A team records deployment traps. A project accumulates architectural decisions. Six months later, changing the client feels like hiring a new colleague who knows nothing.

The answer is not one giant shared memory database. Different memory has different ownership and risk:

  • User memory contains personal working preferences.
  • Team memory contains local practices and recurring decisions.
  • Project or service memory contains architecture, owners, operational history, and current constraints.
  • Organization memory contains approved terminology, policies, and shared business facts.
  • Task memory contains temporary state for one piece of work.

Start with the simplest storage that works. Markdown files on disk are readable by almost every agent, easy to version, and easy to review. The same folder can be an Obsidian vault for people who want links and graph navigation.

When Markdown stops being enough, put the memory behind a stable interface. An MCP server can expose a specialized knowledge base with search, access control, provenance, retention, and deletion. If the agents call the same memory capability, you can replace the storage without updating every prompt and workflow.

Portable Memory Capability

The important move is to separate the contract from the first storage choice. An agent asks for the current decisions for a service. It should not care whether the answer came from three Markdown files, an Obsidian vault, or a dedicated knowledge product.

My article on building an AI Orchestration Meta-Repo shows one concrete way to keep shared instructions, reviewed memory, and tool configuration outside individual product repositories.

A Day in a Composable Company

A product manager uses the company's specification skill in Jira, which remains the source of truth.

Frontend and backend engineers can use different agents to retrieve the ticket through the Atlassian Rovo MCP server, design data through the Figma MCP server, and relevant project decisions from company-controlled memory.

Both engineers invoke the shared QA skill before opening their pull requests. QA reruns it in VS Code with broader browser and accessibility coverage, then sends the report back to the ticket or test system.

Sales and finance stay in their own systems. The CRM can call a shared contract-readiness capability. The finance assistant can apply approved revenue-recognition guidance and request supporting source documents. Identity and permissions follow the employee. The capability follows the work.

The interfaces and models can change while the company capabilities stay put.

Guidance Is Not Enforcement

A skill can explain a regulation, provide the current company procedure, and supply the right report template. It should not be the only thing preventing an agent from exporting restricted data or approving a payment.

Keep hard boundaries outside the model. Identity, authorization, data access, sandboxing, and approval gates must still be enforced by the systems the agent calls. Composability should give several approved agents the same governed access. It should not give every agent the same unrestricted credentials.

Built-In Capabilities Are Still Useful

Composable Agentic is not an argument against built-in features. Sometimes the native capability is the reason to choose a tool. Use it.

The warning is about what you build yourself.

If a product has an excellent built-in review pane, use the review pane. If you write six months of company-specific review logic directly against its private extension API, you have made a different decision. Before doing that, check whether the durable parts can live in a shared skill, a CLI, an MCP server, or a separate evaluation suite.

Keep the adapter thin when a vendor-specific feature is genuinely necessary. Put the company rule and test somewhere portable, then let the adapter translate between that contract and the product.

This matters because custom agent software ages fast. A home-built tool needs someone to follow model changes, tool protocols, context handling, security expectations, and new product capabilities. If nobody stays close to it, it can fall behind within months. I have already covered the wider version of that problem in AI Anti-Patterns in 2026.

The Dark Software Factory Fails at Its First New Role

Imagine a company builds a highly specialized internal agent for producing web applications. It understands the React stack, owns the browser tools, generates tickets in a private format, and contains years of web-specific prompting inside one custom codebase.

It looks impressive until an iOS or Android developer joins the workflow.

Dark software factory

The factory does not know Xcode, SwiftUI, Android Studio, device testing, mobile release processes, or the team's platform conventions. Adding mobile support means extending the custom harness, its tools, its prompts, its evaluation system, and possibly its security model. The thing that was supposed to accelerate delivery becomes the queue every new capability has to pass through.

The QA prompt trapped on one laptop is the smaller version of the same mistake. A frontend engineer wants to run it after implementing a feature in a CLI agent, but the prompt, supporting files, and expected output only exist in the QA engineer's VS Code configuration.

Both fail for the same reason. The company capability was stored inside the current user experience.

Experiment Without Rebuilding the Workflow

Portability lets you try new tools at individual steps instead of replacing the entire setup.

Code review is an obvious example. Run CodeRabbit, Codex /review, and Claude Code Review, where available, against the same representative changes. Give them the same company review criteria and evaluate the findings against the same test cases.

You may decide that one reviewer is better for small local diffs, another catches more production bugs, and a third is too expensive for every pull request. That is useful information. Composability lets you keep two of them for different points in the process, test a new one next month, or remove one without rebuilding the company's definition of a good review.

The same approach applies to models. Use a fast model for mechanical work and a stronger model for an ambiguous architecture review. It applies to knowledge storage, specialist agents, and system integrations too.

The protocols will change. The architectural boundary should survive them.

Test the Capability in a Second Agent

A shared capability is not portable because its files look generic. If a company capability only works in one agent, it is not infrastructure yet. It is an integration.

The way to tell the difference is to run the same task in two materially different clients. Take the QA capability. A CLI coding agent and a VS Code chat make a useful pair. Check whether both can discover the skill, access the required systems, run the deterministic checks, produce the required evidence, and respect the same permissions.

The output does not have to be identical. The contract does.

This is where you find the lock-in you did not know you had: a hidden path assumption, an unsupported skill field, a proprietary tool name, local credentials, a report that only renders in one UI, or memory that cannot be read anywhere else.

Summary

Do not begin by building an Agentic Capability Plane platform. That is an excellent way to create the next custom system everyone has to wait for.

Before you build the next agent-specific integration, decide whether it belongs to the product or to the company.

If it is a company capability, keep it as a reusable part outside the client:

  • Put repeatable procedures in version-controlled skills.
  • Keep deterministic work in scripts and established CLI tools.
  • Expose live systems through maintained MCP servers or similarly stable interfaces, like REST APIs.
  • Store memory outside proprietary chat history.
  • Enforce permissions and approvals in the systems the agent calls.
  • Run the same capability in a second agent to prove it is portable.

Start with one workflow that already hurts when people switch tools. The shared QA workflow is a strong candidate because several roles use it and its report can be evaluated. If it works in two materially different agents without duplicating the instructions, you have turned an integration into infrastructure.

Only centralize the next layer when the pressure is real. Markdown memory may be enough today, while a dedicated service can come later. A local subagent may be enough until its access needs justify a remote specialist.

Let people use any approved agent, and keep the company capabilities portable and composable between them.

Read the whole story
alvinashcraft
44 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Bacpac and Dacpac, the similarities and differences

1 Share

When you need to move an entire database or move the objects in a database, bacpac and dacpac files often come up because of all of the tooling options to interact with them. Bacpac and dacpac files share some core similarities as well as some major differences in how they’re used in SqlPackage and other SQL tools, but their flexibility can create a bit of confusion. In this post, we are going to discuss exactly what makes bacpac specifically important, as well as explore the options that a dacpac is capable of.

A bacpac is primarily a portable copy of schema and data whose model is limited to the Azure SQL Database surface area. A dacpac is primarily a deployment artifact, supports broader target-platform models, and can optionally include data.

Bacpacs: Portability for Azure SQL Database

Most people are familiar with bacpac files from working with the portability options of export and import, which enable moving database objects and their data across different versions of SQL, including downgrading versions (either from Azure to SQL Server or back to older versions of SQL Server). This is a big contrast from backup files (BAK files), which can only be restored to match or upgraded versions of SQL Server. Import and export functionality is available in SQL Server Management Studio (SSMS), VS Code, the Azure Portal, and the command line through SqlPackage.

Bacpacs are a file format for flexibly moving data around but there is a tradeoff of operation speed to get to that more portable file format. A bacpac export or import is generally slower than a native SQL Server backup or restore. Bacpac operations logically extract and recreate the database schema and table data, while native backup and restore copy database pages and log records in SQL Server’s optimized backup format. In Azure SQL Database, the file-level backups are managed as a part of the service, and in SQL Server, you or your DBA are responsible for managing the proper backup processes.

A bacpac file contains the definition of the database objects as well as a copy of all the data in the tables. If you change the file extension on a bacpac file to .zip and extract that to a folder, you can view the inside of a bacpac. The contents include a model.xml file that defines the object structures, as well as a data folder that contains BCP files. These BCP files were written during export, and on import, bulk insert will populate the database with that data.

bacpac image

Data portability through bacpacs is focused on ensuring that a bacpac file can be imported to Azure SQL Database. As a result, bacpacs can only contain objects that are able to be used in Azure SQL Database. A bacpac can’t be created from a SQL Server database with objects that are not supported in Azure SQL Database, like Windows logins or file stream columns or even SQL CLR elements.

All is not lost if you run into a database that will not export to bacpac because of elements that aren’t supported in Azure SQL Database. One option is to create a copy of that database and remove the unsupported objects. However, if your intended destination is not Azure SQL Database, those object limitations may not be meaningful or desired. In this case, understanding all of the capabilities of a dacpac file creates another option.

Dacpacs: Anything and can be everything

Most people that are familiar with dacpac files know them for database deployments. When you build a SQL project the output/result is a dacpac file, but a dacpac can also be directly extracted from an existing database. A dacpac file contains the schema definition or database model of a database and dacpac tooling generates a deployment plan that can be output as T-SQL (script) or directly applied (publish). For a new database, the deployment plan is creating all of the objects from scratch. For an existing database, the deployment plan determines how to modify existing database objects such that they match the definition provided in the dacpac.

The publish and script operations have many available options that customize and analyze the deployment plan generated. An example of an option that customizes the deployment plan is “GenerateSmartDefaults”, which inserts default values when adding a non-nullable column to a table with existing rows. An example of an option that analyzes the deployment plan is “AllowTableRecreation”, which checks if the deployment plan will copy the data in a table to a new location before cancelling the deployment or allowing it to proceed. While dynamic deployments enable dacpac files to be used for numerous workflows from basic applications to multi-tenant SaaS with thousands of databases, the flexibility of the dacpac format extends its usefulness.

dacpac image

Creating a dacpac with an extract operation defaults to including all of the object definitions in the database, and these objects can be specific to any number of target platforms (SQL Server, Azure SQL Database, SQL database in Fabric, etc.). However, extract can be modified to:

  • Exclude server-scoped elements from the dacpac
  • Include data for the tables directly in the dacpac (similar to a bacpac)
  • Extract the table data into Azure Blob Storage as parquet files

dacpac data options image

With the ability to include data in or with a dacpac file, you are able to accomplish portability in scenarios where the bacpac requirement of compatibility with Azure SQL Database becomes a challenge. While dacpac publish and extract is available in most tools, taking advantage of these more complex capabilities generally requires leveraging the SqlPackage CLI.

Portability with dacpacs

The following is a quick tactical overview of moving a copy of a database from one server to another through a dacpac file using the extract and publish commands in the SqlPackage CLI. As a reminder, this is similar to export and import with a bacpac file, but there’s no guarantee that the contents are ready for import to Azure SQL Database when the dacpac is created.

For the extract step, our process includes adding “/p:ExtractAllTableData=true” and optionally “/p:ExtractReferencedServerScopedElements=false” to create a dacpac with the data copy from the original database.

sqlpackage /action:extract /sourceconnectionstring:"<source connection string>" /targetfile:"C:\extracted.dacpac" /p:ExtractAllTableData=true  /p:ExtractReferencedServerScopedElements=false

For the publish step, the data contained in the dacpac is populated on the database by default. To modify from the standard publish process we may include additional properties like:

  • /p:AllowIncompatiblePlatform=true” when moving between different database types
  • /p:ExcludeObjectTypes=Logins;Users” to skip Windows logins that were present on SQL Server when moving to Azure SQL Database
sqlpackage /action:publish /sourcefile:"C:\extracted.dacpac" /targetconnectionstring:"<target connection string>" /p:AllowIncompatiblePlatform=true /p:ExcludeObjectTypes=Logins;Users

SqlPackage works with Integrated authentication, SQL authentication, and Microsoft Entra ID authentication methods. As a result, your connection string could include “Authentication=Active Directory Interactive” for browser-enabled authentication or “Authentication=Active Directory Default” to leverage terminal authentication with a preceding “az login” command.

Recap

The most common description of bacpacs and dacpacs is that a bacpac contains data and a dacpac doesn’t, but this is an incomplete statement. By default a bacpac contains data and the objects in the database must be compatible with Azure SQL Database, while the dacpac file format excels for database deployments and has an option to include a data copy.

Reflecting on the SqlPackage operations that create bacpac and dacpac files:

  • Export: creates a bacpac containing the database schema/model and data. Used mainly for moving or archiving a database’s logical contents when interacting with Azure SQL Database.
  • Extract: creates a dacpac containing the database schema/model (tables, views, procedures, etc.). Used for schema deployment, comparison, and versioning.
  • Extract with Table Data option: creates a dacpac containing the database schema/model and data. Used for moving for moving or archiving a database’s logical contents.

Bacpac files are tightly integrated with Azure SQL Database, with import/export capabilities surfaced through the Azure Portal, az CLI, and Azure PowerShell cmdlets. Both file types, bacpacs and dacpacs, have support in SSMS, VS Code, and the SqlPackage CLI.

The post Bacpac and Dacpac, the similarities and differences appeared first on Azure SQL Dev Corner.

Read the whole story
alvinashcraft
55 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

You can just find and use agent skills mid-session

1 Share

I’m not proud of it, but I’ve been drunk on agent skills. It’s time to reset. It’s easy to fall into skills bloat as you accrue different ones for every possible scenario. Doing software engineering? Coding in Go? Performing security analysis? Add a skill! Nowadays, any of us can do basically anything. Maybe not well, but with agent skills, we get just enough confidence to try it. But we’re blowing tokens and confusing our agents with all this extra context. Plus, you only really need skills when there’s a specific (temporary?) gap in the LLM’s knowledge. Let’s be smarter. Can we do just-in-time discovery and lazy-loading of skills for our coding sessions?

Case 1: Preload skills globally or in your project

As a baseline, many of us default to installing skills ahead of time. It’s easy to grab skill files manually, or use something like the skills CLI to install into global or project directories.

For instance, before starting my coding session, I could find and install a skill for working with Google Cloud Storage.

If I choose to install into my local project directory (instead of globally), I end up with these new files on disk.

I’m using Google Antigravity here, but you’d expect to see this skill show up in whatever agentic tool you have. Both my Antigravity desktop app and CLI show this pre-existing skill.

To be sure, this is a perfectly valid way to work. But the front matter of EVERY skill comes into play in all your agentic coding turns. That gets hefty over time. You could choose to just pre-load or copy the “right” skills into your project before each session. But that assumes you know where you’re heading before every session. I sure don’t! Let’s look at more dynamic scenarios.

Case 2: Find known skills during a session and use them

Let’s consider a case where I know where the desired skills live on the internet, but I only want to grab them if I really need them.

For example, maybe I use all sorts of clouds and pick the right one for the problem at hand. In my current coding session, I’m thinking through web app ideas, and landed on a fun concept for a fast-turnaround marketplace. Its bursty use and global footprint are great for Google Cloud. I need good guidance for picking the right architecture, so I asked Google Antigravity to find the right Google Skill to help out, and store it locally.

When this finished a few moments later, I had another skill in my local project directory.

Most importantly, this skill is automatically active in this coding session. I can see that in Antigravity’s list of active skills.

My current session got better as I discovered and loaded agents on-the-fly. And these skills stay with this project and aren’t just part of the LLM (or coding session’s) temporary memory.

Case 3: Search for skills during a session and use them

The above case assumed that I knew the location of the skill I wanted. You need to be careful about loading random skills, but what if you wanted to search without knowing ahead of time where the most suitable skill was at?

I continued my coding session by asking for the best skills for Go developers.

I know Steve (the creator of the 2nd of the 3 provided options) so I told Antigravity to load Steve’s skills. Sure enough, I got a handful of terrific Go skills installed into my project.

You don’t have to know skills or sources you want ahead of time. You can search for them during a session and keep going without ever breaking flow.

Case 4: Use a seed skill that grabs the “right” skill for the scenario

I’ve seen more of this pattern lately. You know you will need a set of related skills, but don’t know which ones, or when. A seed skill is your entry point that pulls the right skill at the right time. Addy’s Osmani’s agent skills do something like this. The “meta skill” routes you to the right skill for your situation. But it does require you to install all the skills (I believe).

Could you create a seed skill that acts as a lightweight entrypoint, and then just-in-time downloads the “right” skill from a pre-defined list? Sure you can.

Specifically, I want a skill that knows about all of the Google Cloud databases, but only retrieves the corresponding skill if our session demands it.

I used Antigravity to build it (in a different session) and here’s the gist (minus the scripts and references it also created).

---
name: gcp-database-seed-skill
description: >-
  Dynamic seed skill for Google Cloud database solutions. Use this skill when the user is designing,
  choosing, provisioning, migrating, or optimizing databases on Google Cloud (Cloud SQL for MySQL/PostgreSQL/SQL Server,
  AlloyDB for PostgreSQL, Cloud Spanner, Firestore, or Bigtable), or when well-architected cloud database guidance is needed.
  Automatically routes, downloads, and installs specialized skills from google/skills on demand.
---

# Google Cloud Database & Architecture Advisor (Seed Skill)

This seed skill provides a lightweight, progressive entrypoint for all database workloads on Google Cloud. Instead of loading bulky documentation upfront, it assesses workload requirements, recommends the optimal database solution, and dynamically downloads and installs specialized skills from the official [google/skills](https://github.com/google/skills) repository into your local workspace.

---

## Workflow & Step-by-Step Procedure

When invoked on a database-related prompt, follow this 4-step workflow:

```mermaid
flowchart LR
    Step1[1. Assess Workload] --> Step2[2. Determine Database & WAF Needs]
    Step2 --> Step3[3. Download & Install Skill]
    Step3 --> Step4[4. Execute Guided Solution]
```

### Step 1: Assess Workload Requirements
Evaluate the user's requirements against key architectural dimensions:
1. **Engine Compatibility**: Does the workload require MySQL, PostgreSQL, SQL Server, or open-standard ANSI SQL?
2. **Scalability & Scale**: Single-instance vertical scaling vs. distributed multi-node horizontal sharding?
3. **Availability & DR**: Regional HA (99.95% - 99.99%) vs. Global Multi-Region zero downtime (99.999%)?
4. **Workload Characteristics**: Standard OLTP vs. High-throughput HTAP with columnar queries vs. Vector/AI search vs. NoSQL?
5. **Migration vs. New Build**: Existing application lift-and-shift vs. greenfield cloud-native design?

> 📖 Consult the detailed [Database Decision Matrix](./references/database_decision_matrix.md) for complete comparison tables.

---

### Step 2: Select the Target Skills

Map the workload to the primary database skill and check for relevant **Well-Architected Framework** pillars:

#### A. Database Engine Selection
*   **Cloud SQL (`cloud-sql-basics` / `cloud-sql-mysql` / `cloud-sql-postgresql` / `cloud-sql-sqlserver`)**:
    *   *Select when:* Straightforward relational databases (MySQL, PostgreSQL, SQL Server), standard web apps, migration-friendly lift-and-shift via Database Migration Service (DMS).
*   **AlloyDB for PostgreSQL (`alloydb-basics` / `alloydb`)**:
    *   *Select when:* Demanding enterprise PostgreSQL workloads, up to 4x transactional performance, up to 100x faster analytical/HTAP queries with columnar engine, or in-database vector search (Vertex AI / ScaNN integration).
*   **Cloud Spanner (`spanner`)**:
    *   *Select when:* Mission-critical global databases, massive scale exceeding single-instance limits, synchronous multi-region replication, horizontal scaling, and 99.999% availability with zero downtime.
*   **Discovery / Undecided (`cloud-databases-onboarding`)**:
    *   *Select when:* User is unsure or needs interactive discovery across relational, NoSQL (Firestore, Bigtable), and analytical stores.

#### B. Well-Architected Framework Triggers
Check if cross-cutting architectural pillars should be retrieved:
*   **Reliability & HA:** Mentions of 99.999% SLA, DR, RTO/RPO, regional failover → Fetch `well-architected-reliability`.
*   **Security & IAM:** Questions about Private IP, PSA, CMEK encryption, or IAM DB auth → Fetch `google-cloud-recipe-auth`.
*   **Performance & Tuning:** Connection pooling, read replicas, latency optimization → Fetch `google-cloud-solution-architecture`.
*   **Cost Optimization:** Committed Use Discounts (CUDs), rightsizing, auto-pause → Fetch `google-cloud-solution-architecture`.

> 📖 Consult [Well-Architected Triggers](./references/well_architected_triggers.md) for full trigger criteria.

---

### Step 3: Download and Install the Specialized Skill

Execute the bundled installer helper to fetch the required skill(s) directly from `google/skills` into the workspace's `.agents/skills/` directory:

```bash
# Example: Install Cloud SQL basics
python3 scripts/install_gcp_skill.py --skill cloud-sql-basics

# Example: Install AlloyDB basics
python3 scripts/install_gcp_skill.py --skill alloydb-basics

# Example: Install Cloud Spanner
python3 scripts/install_gcp_skill.py --skill spanner

# Example: Install Well-Architected Reliability
python3 scripts/install_gcp_skill.py --skill well-architected-reliability

# Example: Install all core database skills
python3 scripts/install_gcp_skill.py --all-databases
```

*Alternative using npm/npx:*
```bash
npx skills add google/skills
```

---

### Step 4: Proceed with Implementation

Once the skill is installed into `.agents/skills/`, it is immediately available for subsequent operations.
1. Reference the newly installed `SKILL.md` in `.agents/skills/<skill-name>/`.
2. Provide concrete `gcloud` commands, Terraform/IaC snippets, or client library examples tailored to the user's selected database.
3. Verify connectivity (e.g. Cloud SQL Auth Proxy, IAM authentication, VPC Private Service Access).

---

## Reference Guides

*   [Database Decision Matrix](./references/database_decision_matrix.md): Deep-dive comparison between Cloud SQL, AlloyDB, Spanner, Firestore, and Bigtable.
*   [Well-Architected Framework Triggers](./references/well_architected_triggers.md): Detailed rules for triggering security, reliability, performance, cost, and operational pillars.
*   [Known Skills Catalog](./references/known_skills_catalog.md): Complete list of remote paths and metadata in `google/skills`.
*   [Prompt Routing Scenarios & Examples](./examples/prompt_routing_scenarios.md): Real-world examples of user requests and the exact skills retrieved.

After adding this seed skill to my current coding session, I started up a database conversation regarding my app idea. We landed on Firestore as the best choice, so I asked the seed skill to retrieve only the Firestore skill.

Here, I didn’t need to download or pre-load all the possible Google Cloud database skills. There’s a lot of them. I only got the one I needed for this particular coding session. Seems cleaner?

A word about MCPs

If your skills make use of remote or local MCP servers, circumstances are different. While MCP now supports a stateless interaction pattern, most existing MCP servers don’t. When your coding session starts, there’s a handshake that happens. This means that if you introduce a new MCP during a session (in your mcp_config.json), you likely have to fork your current session, or start a new one. My tests showed this to be the case most of the time, but your mileage may vary.

I mention this because you could see skills that make heavy use of managed MCPs. That’s cool, but downloading those skills on the fly might mean they only partially work because your coding harness can’t “see” the referenced MCP server. Just be cautious!

We’re seeing an emerging “open knowledge layer” consisting of MCPs, skills, and plugins. I’d imagine we will see patterns evolve for how and when to use each component. What do you think of this lazy loading pattern I demonstrated here?



Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories