Category Archives: Development

HAVIT Named an OpenAI Select Partner

HAVIT, s.r.o., a Prague-based digital studio for the AI era focused on custom software development, AI implementation and adoption, and Microsoft Azure, today announced that it has been named an OpenAI Select Partner within the OpenAI Partner Network.

The OpenAI Partner Network is a global program for partners to build, sell, and deliver AI solutions with OpenAI. It brings together partners with deep industry expertise, delivery capabilities, and customer relationships while equipping them with resources, enablement, and support to help enterprises adopt OpenAI frontier models and products and turn them into measurable impact.

As an OpenAI Select Partner, HAVIT will continue working with OpenAI to help organizations build, deploy, and scale AI solutions responsibly and effectively. This work will help organizations get more useful work from every token and stronger performance per dollar with GPT‑6 Astra, while using ChatGPT Work to turn ambitious goals into finished work. Since 1997, HAVIT has delivered more than 200 projects across fintech, automotive, healthcare, and other industries, combining strategy, design, engineering, and AI agents under one roof.

“Being named an OpenAI Select Partner confirms the direction we have been taking for the past years — bringing AI from experiments into production systems our customers rely on every day. We plan to work closely with OpenAI to embed frontier models into the applications we build and operate, so our customers get measurable outcomes: faster processes, lower operating costs, and software that genuinely works for its users,” said Robert Haken, Founder & Software and Cloud Architect, HAVIT.

HAVIT supports organizations across industries with AI implementation and adoption (from demo to production), AI onboarding of developer teams, digital product development with complete product teams, and Microsoft Azure expertise — from architecture to operations as a Microsoft Cloud Solution Provider. Its work includes AI agents and AI capabilities deployed directly into customers’ production systems, as well as mentoring development teams through the transition to AI-assisted development.

Looking ahead, HAVIT plans to expand its OpenAI-related offerings, invest in team enablement, and scale AI deployments within customer solutions, helping customers translate AI ambition into business outcomes.

Learn more about the OpenAI Partner Network: https://openai.com/business/partners

When the spec is a pull request – How application changes now start at Broker Trust

Broker Trust, a.s. deployed Claude Team with HAVIT. After targeted training, a new way of working took hold: instead of writing a specification and waiting, a product owner builds the simpler changes himself in Claude Code and submits them as pull requests. Before the deployment, a comparable change typically took 3–4 weeks from idea to production; the fastest of these pull requests was merged to production 16 hours after it was submitted.

The challenge

BETY2 is the CRM used by Broker Trust’s network of financial advisors. The product side knew exactly what needed improving — but every change, however small, went through the same pipeline: write a specification, hand it to development, wait for capacity, clarify it over meetings. And because a written specification is only ever an approximation of what someone had in mind, clarification came with every request. For a simple change, the path from idea to production typically took 3–4 weeks — most of it spent waiting, not working.

The solution

Broker Trust deployed Claude Team across its organization — 42 seats, including 10 Premium seats with Claude Code — and administers its own tenant and licensing. The deployment grew from 10 seats to 42 within three weeks of go-live.

But the change that mattered was not the seat count. It was hands-on training for the people closest to the product. HAVIT, s.r.o., Broker Trust’s long-standing application development supplier, onboarded and trained selected users in lectures and workshops focused on one specific skill: using Claude Code to turn an idea into working code against their own codebase. The sessions ran in June and July 2026.

Two product owners went through the training. The first of them is already contributing to the codebase: Martin Budín, product owner of BETY2. He now works a different way — instead of writing a specification and waiting for a developer, he describes the change to Claude Code, iterates on it against the real codebase, and submits the result as a pull request. HAVIT’s developers review and merge it. He has submitted four pull requests this way; three of them have been merged and deployed to production.

The important part is what this does to the design step. When a product owner explores a change directly in the code, the proposal that reaches the development team isn’t a static mockup or a paragraph of prose — it’s a working implementation you can click through and argue with. Ambiguity that used to surface late, during development, now surfaces at the start.

The same shift reached design work. Broker Trust’s product team now prepares application design proposals with Claude itself, instead of scheduling the work with a designer — so the design step no longer waits on anyone’s calendar.

Nothing about code review changed to make this possible. Every change still goes through review by HAVIT’s developers before it reaches production; product owners open pull requests, they don’t merge them. What changed is who can produce a credible starting point — and how fast.

The measured results

The fastest change went from pull request to production in 16 hours — down from a typical 3–4 weeks before the deployment, a reduction of over 95 % in cycle time. Before the deployment, a small BETY2 change waited for a specification, a hand-over, and free development capacity — typically 3–4 weeks end to end. Now the product owner submits a working implementation, and the only remaining step is code review: the fastest of his pull requests was reviewed, merged, and deployed to production within 16 hours of being submitted.

A working implementation now exists on day one instead of week three. The product owner builds each change with Claude Code and submits it as a working, clickable pull request the same day he takes it up — where previously nothing reviewable existed until a developer picked the specification up, typically weeks later. Developer time per change fell accordingly: from hours of implementation against a written specification to the review of a working pull request.

Design preparation dropped from 4 weeks to 1 week. Application design proposals that the product team previously prepared with a designer over a typical 4 weeks are now prepared by the product team itself with Claude in 1 week — and what reaches development is a clickable proposal.

In numbers, in the first two months of production use:

  • 4 pull requests authored by a Broker Trust product owner; 3 reviewed, merged, and deployed to production
  • Fastest pull request from submission to production: 16 hours — versus a typical 3–4 weeks per change before the deployment
  • Design preparation for application changes: from 4 weeks with a designer to 1 week by the product team itself
  • 2 product owners trained to work with Claude Code against their own application
  • Claude adoption grew 4× — from 10 to 42 seats — within three weeks of go-live

“We used to have to describe every change, however small, hand it over and wait for capacity to free up. Today, for the simpler ones, our product owner builds it himself and submits it as a pull request for review. What helped us most is that we’re no longer discussing the wording of a specification, but a finished thing we can click through.”

— Petr Musil, Technical architect, Broker Trust

What’s next

The experience carries forward. Broker Trust is preparing its own agentic tooling on Claude to connect its internal systems — and is building much of it in-house.

Slow .NET builds/tests? An installed preview SDK might be the culprit

If you have a .NET preview SDK installed on your machine (for example, the .NET 11 preview), the dotnet CLI may default to it — and in my case (apparently due to a bug in the preview), this caused builds and tests to run significantly slower than with the release SDK. It also shows up with AI coding agents that invoke dotnet from the command line.

The fix is to add a global.json to the solution root that opts out of preview SDKs:

{
"sdk": {
"version": "10.0.0",
"allowPrerelease": false,
"rollForward": "latestFeature"
},
"test": {
"runner": "Microsoft.Testing.Platform"
}
}

The key bit is allowPrerelease: falsedotnet will skip the preview SDK and pick the release one. Adjust version to match your target .NET version.

We keep the current version in our project template: havit/NewProjectTemplate-Blazor/global.json.

WASM: AggregateException_ctor_DefaultMessage (Could not resolve type with token …)

We ran into an interesting issue after installing .NET 9 SDK 9.0.204 (and 9.0.300 didn’t help either) with published Blazor WebAssembly front-ends. The app failed to load, and the browser console output showed this error:

ManagedError: AggregateException_ctor_DefaultMessage (Could not resolve type with token 01000024 from typeref (expected class 'System.Reflection.Assembly' in assembly 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'))
    at an (dotnet.runtime.5nhp1wfg9b.js:3:26894)
    at Kt.resolve_or_reject (dotnet.runtime.5nhp1wfg9b.js:3:26449)
    at dotnet.runtime.5nhp1wfg9b.js:3:172714
    at dotnet.runtime.5nhp1wfg9b.js:3:172778
    at fr (dotnet.runtime.5nhp1wfg9b.js:3:35046)
    at Fc (dotnet.runtime.5nhp1wfg9b.js:3:172361)
    at dotnet.native.swgexbmoy7.wasm:0x1f1a4
    at dotnet.native.swgexbmoy7.wasm:0x1c8ae
    at dotnet.native.swgexbmoy7.wasm:0xea19
    at dotnet.native.swgexbmoy7.wasm:0x1ec88

Our first suspicion was trimming, but to make a long story short, this turned out to be the classic issue that comes with builds after installing a new SDK: you need to clear the build agents’ working folders if your builds don’t run in completely clean environments and rely on any kind of incremental setup.

Translated to local development in Visual Studio: make sure to clean the solution and manually delete the bin and obj folders.

Why am I posting this? In case someone else runs into the same error, maybe they’ll find this post and save time troubleshooting. We’ve pretty much gotten used to builds breaking after a new SDK install, and our go-to move is wiping the build agent workspace before diving deeper.

But this was a first for us: the build actually succeeded, but the output was “broken” in a way that only surfaced when running the Blazor WASM frontend in the browser.

[Blazor] await periodicTimer.WaitForNextTickAsync() – a good servant but a bad master

Beware of using await periodicTimer.WaitForNextTickAsync(). This method is appealing due to its asynchronous signature, making it easy to set up periodic tasks, which might tempt you to implement UI updates with it, especially in Blazor:

protected override async Task OnInitializedAsync()
{
    await StartTimerAsync();
}

private async Task StartTimerAsync()
{
    using var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
    while (await timer.WaitForNextTickAsync())
    {
        // do some UI updates here
    }
}

Warning! While this approach doesn’t block the UI thread thanks to async-await, the issue is that the method calling such code never actually completes.

For instance, if StartTimerAsync() is called directly from OnInitializedAsync, OnParametersSetAsync, OnAfterRenderAsync, or an action callback, the parent method will never finish, leading to some unexpected consequences, like:

  • If called from an HxButton.OnClick button handler, the spinner remains stuck, never stopping.
    • The button also stays under single-click protection, disabled, and unusable.
  • If called from OnInitializedAsync, the first roundtrip won’t invoke OnParametersSet[Async], which won’t execute until a new roundtrip arrives.
  • Calling from OnParametersSetAsync leaves an unfinished task in ComponentBase.CallStateHasChangedOnAsyncCompletion() and requires handling to prevent multiple timers from starting, as OnParametersSetAsync is called repeatedly.
  • If called from OnAfterRenderAsync(bool firstRender), it could block the await base.OnAfterRenderAsync(firstRender) call, disrupting inherited functionality (especially crucial for firstRender = true, which only runs once).

So, PeriodicTimer.WaitForNextTickAsync() is more appropriate in scenarios where it’s safe for the calling code to continue indefinitely, such as in a Main method for a console application handling cyclical tasks or within BackgroundService.ExecuteAsync(). In general, however, the calling method should be allowed to complete. Instead, a traditional setup using Task.Run(..) is recommended, placing the timer (or even a regular Timer) on the ThreadPool without awaiting its completion in the current method (fire-and-forget). In Blazor, this requires manually invoking StateHasChanged() or possibly DispatchExceptionAsync().

Example:

public MyComponent : IDisposable
{
    private PeriodicTimer timer;

    protected override async Task OnInitializedAsync()
    {
        _ = Task.Run(StartTimerAsync);
    }

    private async Task StartTimerAsync()
    {
        timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
        while (await timer.WaitForNextTickAsync())
        {
            // do some UI updates here
            StateHasChanged(); // as needed
        }
    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}

Don’t forget cleanup with timer.Dispose(), or the Timer will keep running even after the component is destroyed, leading to resource leaks.

See also the ASP.NET Core Blazor documentation:

ASP.NET Core Blazor synchronization context: Invoke component methods externally to update state

Go to file member (Visual Studio)

I’ve been struggling for a long time with how to quickly navigate to a member in a larger file when I already know its name.

Resharper/Rider has a direct Go to file member shortcut with Alt + \.

Visual Studio 2022 has a Go to member feature, but by default, it’s scoped to the entire solution.

It’s one of those classic Go to… tools with the m: prefix that searches for members but across the whole solution. Worse, it doesn’t prioritize results from the current file 😭. There’s no shortcut or a clear way to pre-scope it to the current file.

HOWEVER!!! There’s one feature I just discovered today that makes it usable (at least for me) – it remembers the last scope, and it does so separately for different use cases. Plus, it shares the same shortcut Alt + \.

So:

You open it once through Go to member (Alt+\), change the scope to Current file, and from then on, it will remember that setting.

This doesn’t affect other use cases. For Go to all (Ctrl + T) or Go to file (Ctrl + Shift + T), it still remembers the Current solution scope for me.

A real quality of life changer! 🤣🥳

SQL LocalDB: Upgrade to 2022 (15.0.2000)

For me it was quite confusing to find the 2022 version of LocalDB and it is not a streamline process to upgrade your local default instance.

The easiest way to upgrade your LocalDB instance to 2022 is:

  1. Download the LocalDB 2022 installer by using the SQL Server Express installer.
    https://download.microsoft.com/download/5/1/4/5145fe04-4d30-4b85-b0d1-39533663a2f1/SQL2022-SSEI-Expr.exe
    1. Run the installer and select “Download Media”.
    2. Select “LocalDB” + click Download.
  2. Before running the SqlLocalDB.msi installer, delete your current MSSQLLocalDB instance:
sqllocaldb stop MSSQLLocalDB
sqllocaldb delete MSSQLLocalDB
  1. Run the new SqlLocalDB.msi (2022) installer. It will create a new MSSQLLocalDB instance.
  2. RESTART YOUR PC! (Otherwise MS SQL Management Studio will still tell you you have an old version running, etc. etc.)
  3. Now you can re-attach your original databases one by one using SQL Server Management Studio (RClick + Attach…)
  4. Done.

gRPC code-first for Blazor WebAssembly front-end

gRPC is a phenomenon of our time. This modern and performance-efficient protocol is rapidly spreading, and today we will show how to use it for communication between the Blazor WebAssembly front-end and the ASP.NET Core backend (host):

  1. We will efficiently use the possibilities of sharing code between the server and client part. We will use the code-first arrangement and put the “contract” (interface for the called service and data object definitions) into the assembly shared by both the server and client parts of the solution.
  2. To overcome browser limitations, we will use the gRPC-Web extension.

We will show the entire implementation on a simple example – we will use the default Blazor WebAssembly App template from Visual Studio (ASP.NET Core hosted, version of the .NET7 template) and we will convert the prepared Fetch data example, which uses the REST API in this template, to a gRPC-Web call using code-first.

Let’s do this, it’s just a few steps:

1. MyBlazorSolution.Server – Preparing ASP.NET Core host

First, we prepare the server-side infrastructure for gRPC. We will go directly to the version with the gRPC-Web extension with code-first support and install NuGet packages

We register support services in dependency-injection in Startup.cs:

builder.Services.AddCodeFirstGrpc(config => { config.ResponseCompressionLevel = System.IO.Compression.CompressionLevel.Optimal; });

We add gRPC middleware somewhere between UseRouting() and endpoint definition (before MapXy() methods):

app.UseGrpcWeb(new GrpcWebOptions() { DefaultEnabled = true });

2. MyBlazorSolution.Shared – Service contract definition (code-first)

Now we define in the form of an interface what our service will look like. We will then use the interface on the server side (we will create its implementation) and on the client side (we will generate a gRPC client that will implement the interface and we will directly use it in our code via dependency injection).
Add to the project a NuGet package that allows us to decorate the interface with necessary attributes

Find the example WeatherForecast.cs file from the project template. It contains the definition of the return data message, which the sample REST API now returns to us. We will convert this class into the following form:

[DataContract]
public class WeatherForecast
{
    [DataMember(Order = 1)]
    public DateTime Date { get; set; }

    [DataMember(Order = 2)]
    public int TemperatureC { get; set; }

    [DataMember(Order = 3)]
    public string? Summary { get; set; }

    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
  • We added the [DataContract] attribute to mark the class we will use as the gRPC data message.
  • We added [DataMember(Order = ...)] attributes that mark the elements to be transmitted via gRPC (others are ignored, here TemperatureF is calculated and recalculated from other data on the client anytime). Each element needs to be set Order, which defines the fixed layout for the used protobuf serialization.
  • We replaced the original DateOnly type with DateTime. We have to stick to types supported by used protobuf serialization.

Next, we need to create an interface that will describe the whole service:

[ServiceContract]
public interface IWeatherForecastFacade
{
    Task<List<WeatherForecast>> GetForecastAsync(CancellationToken cancellationToken = default);
}
  • The [ServiceContract] attribute tells us the applicability for gRPC (can be used later for automatic registrations).
  • By the nature of network communication, the entire interface should be asynchronous.
  • We can use the optional CancellationToken, which can convey a signal of premature termination of communication by the client (or disconnection).

3. MyBlazorSolution.Server – Implementing the gRPC service

Now we need to implement the prepared interface on the server side (we will use slightly modified code from the sample WeatherForecastController, which you can now delete):

public class WeatherForecastFacade : IWeatherForecastFacade
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public Task<List<WeatherForecast>> GetForecastAsync(CancellationToken cancellationToken = default)
    {
        return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Today.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToList());
    }
}

Now we have to add the gRPC service in Startup.cs:

app.MapGrpcService<WeatherForecastFacade>();

4. MyBlazorSolution.Client – gRPC client in Blazor WebAssembly

Now all that is left is to use the service in the Blazor WebAssembly front-end. The entire definition is available in the form of the IWeatherForecastFacade interface with its WeatherForecast data class.

We will add the necessary NuGet packages to the project:

Register the gRPC-Web infrastructure and the client (in factory form) in Program.cs:

builder.Services.AddTransient<GrpcWebHandler>(provider => new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));

builder.Services.AddCodeFirstGrpcClient<IWeatherForecastFacade>((provider, options) => =>
    {
        var navigationManager = provider.GetRequiredService<NavigationManager>();
        var backendUrl = navigationManager.BaseUri;

        options.Address = new Uri(backendUrl);
    })
    .ConfigurePrimaryHttpMessageHandler<GrpcWebHandler>();

Well, now we can use IWeatherForecastFacade anywhere in the front-end project by having the service injected using dependency injection. So, for example, we’ll modify FetchData.razor to use our new gRPC service instead of the original REST API:

@inject IWeatherForecastFacade WeatherForecastFacade

...

@code {
    private List<WeatherForecast>? forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await WeatherForecastFacade.GetForecastAsync();
    }
}

Done. The project should now be executable and the Fetch data page will now communicate via gRPC-Web.

You can check your solution against the sample repository

Other extensions and more advanced techniques

The gRPC service can, of course, accept input. In this case, use one input parameter for the incoming message – a data class created in the same way we prepared the WeatherForecast output. (Usually these classes are referred to as Data Transfer Object and thus given the suffix Dto. The implementation is usually as a C# record.)

If we have authentication and authorization in our project, then we can use the [Authorize] attribute on the implementing class/method, just as we would on a controller/action.

We can apply arbitrary techniques to the published gRPC endpoint like any other mapped server endpoint (rate limiting, caching, …).

gRPC has support for interceptors which can be used to further improve gRPC communication

  • pass exceptions from server to client (basic support is built-in, but you may want to enrich it with specific handling of custom scenarios),
  • passing the required culture from client to server (what language the front-end is switched to),

In a more advanced variant of the layout, you can also provide automatic registration of interface and data contracts without having to decorate them with [ServiceContract], [DataContract] and [DataMember(Order = ...)] attributes. All this and much more can be found ready in:

Both open-source with MIT license, free of charge.

Blazor WASM: Unsupported connection to AAD using the standard auth-library (Microsoft.AspNetCore.Components.WebAssembly.Authentication)

If you (like me) were hoping to “easily” create a Blazor application that connects to a generic OIDC identity-provider using the Microsoft.AspNetCore.Components.WebAssembly.Authentication library, while also supporting connections to Azure Active Directory (AAD) this way, you’ll be disappointed.

The idea is simple, AAD does support OIDC and therefore now why shouldn’t a generic OIDC client be able to connect to AAD as well. Unfortunately the hurdles are more than you would like to overcome.

While the Microsoft.Authentication.WebAssembly.Msal library for AAD is an extension of the underlying Microsoft.AspNetCore.Components.WebAssembly.Authentication library, it does a lot more than just boilerplate configuration of the underlying generic OIDC.

The basic difference can be found, for example, in the “interop” part with the underlying oidc-client JavaScript module.

  • AuthenticationService.ts in Microsoft.AspNetCore.Components.WebAssembly.Authentication,
  • AuthenticationService.ts in Microsoft.Authentication.WebAssembly.Msal.
    This is manifested for example when retrieving access-tokens. The MSAL version “fixes” the AAD specificity that when querying a token endpoint does not always return an access-token with all required scopes (the details are for a broader discussion, but e.g. you don’t get a token that has User.Read scope in it along with the custom-scope of your API, etc.).

The base library uses the “caching” of tokens in the oidc-client UserManager and relies on the assumption “If token-endpoint returned an access-token, then that token has all the required scopes in it.” (i.e., it stores the required scopes for the retrieved token, and then returns the already retrieved token the next time the same scopes are requested).

The “fixed” MSAL library knows this shortcoming of the underlying oidc-client and knows that while it claims to have a token for some set of scopes, the access-token may not actually have those scopes. Therefore, it “turns off” caching at this level and always gets the token again.

Announcing HAVIT Blazor 1.4.3 – Free Bootstrap 5 components for ASP.NET Blazor

Didn’t have a chance to announce our component bundle on this KnowledgeBase yet…

Now we released version 1.4.3. What’s new?

  • HxInputDateHxInputDateRange and HxCalendar have new MinDate and MaxDate parameters to allow customization of selectable dates in calendars (also added to Defaults to be able to customize the application-wide defaults)
  • HxInputDate and HxInputDateRange have new CalendarDateCustomizationProvider which allows further customization (enabled/disabled, CssClass) of individual dates in dropdown calendars
  • ⚠️ HxCalendar parameters MinYear and MaxYear replaced with the new MinDate and MaxDate parameters
  • HxCalendar has new simplified month/year navigation
  • HxInputDate fixed validation icon position if CalendarIcon is used
  • HxInputFileCore has new HxInputFileCore.Defaults.MaxFileSize application-wide default to be able to limit maximum file size,
  • HxInputFile has new HxInputFile.Defaults.InputSize application-wide default to be able to set form component size
  • HxSidebar fixed ability to scroll on mobile if viewport is overlapped
  • HxSidebar has new CSS variable for customising background-colour
  • Updated to Bootstrap 5.1.3 and Bootstrap Icons 1.6

What is HAVIT Blazor?

HAVIT Blazor is a free open-source (MIT) component bundle build on top of Bootstrap 5. It not only covers all Bootstrap 5 components but also brings a few enterprise-level ones (e.g. HxGrid, HxInputDate, HxAutosuggest, HxInputTags) and special ones (e.g. HxGoogleTagManager).

Interactive documentation & demos – https://havit.blazor.eu

Source code – https://github.com/havit/Havit.Blazor

Forms

Buttons & Indicators

Data & Grid

Layout & Typography

Navigation

Modals & Interactions

Special

Enterprise-application template [OPTIONAL]

Beside the components there is a ready to run enterprise level application template which includes gRPC code-first communication, full layered architecture stack (Model, DataLayer, Services, …) and much more.