I once heard, “If you fear something, learn about it, disassemble it to the tiniest pieces, and the fear will just go away.

Well, it didn’t work. I read a book about building LLM from scratch, which helped me understand the model's architecture and how it works inside. However, I am still concerned about the power of AI models and the risks our world may face in the future. Although we still don't understand many fundamental concepts of how the human brain works, and some scientists say we are not even close to getting to human-level intelligence, I am still a bit worried about the scale and speed the new technologies emerge. Many inventions of science were not achieved by logical thinking or inference but by mistake or trial and error. Spawning millions of model instances and automating them to make “random” experiments to discover something new doesn’t seem that impossible to me.

The book shows how to build the smallest version of GPT-2 in Python and preload model weights published by OpenAI. By the way, GPT-3 has the same architecture, but the model is scaled to a larger number of parameters.

I was curious if this could be done in C#, and I found the TorchSharp library. It is a wrapper for native libraries used by PyTorch. The API was intentionally kept to be as close to Python as possible, so the code does not look like .NET at all. But it makes the library easy to learn and use, since a vast majority of examples are in Python. What surprised me is that the actual LLM implementation in C# has only about 200 lines of code. All the magic is in the model weights. PyTorch/TorchSharp provides a very nice abstraction over the primitives from which deep neural networks are composed.

I was wondering if it makes sense to do a session about it, for example, at our MeetUpdate. The problem is that I am not an AI scientist, and the topic is hard. I think I understand all the crucial aspects and will be able to explain what is going on. But still, there are many things I have practically no experience with. Second, understanding the session requires at least some knowledge of how neural networks work and the basics of linear algebra. I am not sure what the experience of the audience would be. And finally, I would be speaking about something that is not my creation at all - it would be merely a description of things others have invented, and my added value would only be in trying to explain it in a short meetup session.

On the other hand, playing with it was really fun, and maybe it can motivate someone to start learning more about ML and neural networks.

Shall I do it?

Crazy developer explains LLM internals to the students

My new book, “Modernizing .NET Web Applications,” is finally out - available for purchase in both printed and digital versions. If you are interested in getting a copy, see the new book’s website.

A pile of copies of my new book Modernizing .NET Web Applications

It was a challenging journey for me, but I liked every moment of it, and it is definitely not my last book. I just need to bump into another topic and get enthusiastic about it (which seems not to be that hard).

I finished the manuscript on the last day of June, but some things had already changed before the book was published. For example, the System.Data.SqlClient package became deprecated. Additionally, all samples in the book used .NET 8, but .NET 9 is just around the corner, and there will be many new and interesting features and performance improvements. Despite the fact that they do not primarily target the area of modernization, they are highly relevant - they constitute one of the strongest arguments for moving away from legacy frameworks. Chapter 2 is dedicated to the benefits of the new versions of .NET, and it is one of the longest chapters of the book. Why else would you modernize if not to use the new platform features?

To ensure you can stay updated, I’ve set up a newsletter where I’ll regularly post all the news concerning the modernization of .NET applications.

The book had an official celebration and announcement at Update Conference Prague, and I’d like to thank all the people around me who helped the book to happen.

I just returned from Chicago from Microsoft Ignite - the largest tech conference about Microsoft technology. Unsurprisingly, it was mainly about AI, and it was a terrific experience.

I got the chance to be part of the Expert Meetup zone. For each of the ten main areas of interest, there were several booths where the attendees could ask any questions. I was at the booth focused on Serverless in Azure, so we discussed Azure Functions, Azure Container Apps, and .NET Aspire, my favorite dev tool of recent months. I met many great people from the product groups who stand behind these technologies.

Since Ignite is not primarily a developer conference, most sessions were a bit further from my technical interest. However, being the CEO of RIGANTI, I want to understand how enterprises around the world implement AI, which use-cases are relevant to them, and what challenges they face. These insights greatly help us steer our company strategy and give better advice to our clients about integrating AI into their businesses.

I also attended an interesting session about the modernization of legacy systems, a topic similar to my recently published book. When speaking about this topic, I always specialized purely in .NET Framework applications, for this is what I meet most of the time. However, this session went much further in the past - most of it was about mainframes and ancient languages like COBOL or Smalltalk. It showed how to use AI to analyze or reverse-engineer the original intent of ancient code routines, how to extract business rules, build missing or incomplete documentation, and how to generate test cases to ensure the new code will provide the same behavior and functionality. This was a very different approach in contrast to my strictly deterministic and non-AI-based approach presented in the book, and I got plenty of new ideas.

Another very interesting session was about using Azure Kubernetes Service at scale. The team has been adding an impressive number of features, focusing on making Kubernetes easy to use and enhancing security - for example, by container vulnerability scanning and patching. I was amazed to see so many features and improvements they delivered recently. Apparently, AKS is a very popular service, and the product group has a chance to do so many interesting things.

And as always, I couldn't miss Mark Russinovich's session on Azure infrastructure. It conflicted with my Expert Meetup sessions, so I had to watch it from recording during my flight back, but it is thrilling to see the effort Microsoft puts in their data centers to make them energy efficient.

Tomas Herceg at Microsoft Ignite in front of GitHub logo

Thursday was the first day of our Update Conference Prague - the largest event for .NET developers in the Czech Republic with more than 500 attendees. On the same day, I also had an online session at .NET Confa global online .NET conference organized by Microsoft.

Originally we were thinking of setting up a studio at Update from where I could stream my session, but because I was speaking at another conference the previous week, there was not enough time for testing the setup. I am lucky that the venue where Update takes place is only about 100 meters from the RIGANTI office.

Same as last year, my .NET Conf session was about modernizing legacy applications, but this time not from the technical point of view. I focused on arguments the tech leads or developers can use to explain the risks and benefits to managers and other non-technical stakeholders.

I talked about security risks that are often seen in old libraries and frameworks, then about performance improvements of the new .NET that can translate to significantly lower infrastructure costs. I also discussed hiring and developer happiness - an important criterion in the long-term perspective. Most developers want to use the new versions to keep their knowledge up-to-date, and there is an increasingly large group of developers who started with the new .NET Core and never touched the old stuff.

Then I focused on planning and estimation, and recommended to prepare a “business plan” - evaluate not only risks coming from DOING the migration, but also from NOT DOING it. You can lead a few experiments to get the idea of complexity and make rough estimates based on the results.

The last important thing I mentioned was communication. In many cases, being predictable is way more valuable than deliver on time, for many legacy applications are only internal tools and the deadlines are often only virtual. Some developers complain that spending a few hours making a presentation about the current progress is a waste of time, and use the time to write code in a hope that the deadline will be saved. In reality, these few hours will probably not save the day, and the stakeholders will face an unexpected surprise. Clear and intensive communication can prevent this.

You can find more information on this topic in my new book Modernizing .NET Web Applications. It contains a detailed guide for migrating from ASP.NET Web Forms, ASP.NET MVC, WCF, SignalR, Entity Framework 6, and other .NET Framework technologies to their equivalents in .NET 8 and beyond, and much more.

I got several requests to publish the slide deck. You can find it below:

I’ve seen various demos on hosting Blazor applications inside MAUI apps, and I’ve been wondering about the use cases – on the first look it didn’t seem so practical to me. However, a bit later I noticed that having such thing few years ago, it would save us quite a lot of work.

At RIGANTI, we’ve been building several desktop applications for our customers. One of the application contained quite complex reservation system. The application was built using WPF as it needed to interact with various hardware devices (RFID readers, receipt printers, credit card terminals, and so on), and so the reservation calendar (a complex component) was built using WPF.

Later we found that we need a similar calendar on the web – the project has another public facing site which would take advantage of the reservation system, and thus we had to rebuild similar UI on the web. A hybrid app would help us to save a lot of time.


How to integrate web app into MAUI app

I’ve looked at the BlazorWebView control implementation and found out that it’s not complicated – basically it is a wrapper over the WebView2 component. It would be nice to offer a similar feature to DotVVM users. DotVVM apps use the MVVM pattern, same as in MAUI, so it may be even more interesting – both desktop and web UI can use the same Model-View-ViewModel approach.

The implementation in Blazor directly invokes the components to render the HTML. It is not invoking the entire HTTP request pipeline in ASP.NET Core, which can be more performant, however the application may not take advantage of some parts of ASP.NET Core, e. g. the authorization.

Also, there are some custom mechanisms to serve static files to the web app. I haven’t explored the details on Android an iOS yet, so it may be necessary to do it that way, but the static pages middleware in ASP.NET Core could do the same job if it gets the correct implementation of IFileProvider.

Since the DotVVM runtime has a lot of dependencies on ASP.NET Core services, I’ve found that the easiest way would be to invoke directly the RequestDelegate to handle the entire HTTP request using ASP.NET Core pipeline.


Same as in BlazorWebView, the app is running on a special origin 0.0.0.0 – it’s not a valid IP address, but WebView thinks that it is fine, and doesn’t do any DNS resolution. We could use localhost, but if the WebView would need to call something on the local machine, there could be conflicts or some problems with sharing cookies. This seems more feasible.

The crucial part for integration is to intercept the HTTP request sent by the WebView and provide the response without performing any networking operation.

_webview.CoreWebView2.AddWebResourceRequestedFilter($"{AppOrigin}*", CoreWebView2WebResourceContext.All);

_webview.CoreWebView2.WebResourceRequested += async (s, eventArgs) =>
{
    …
};

The request is handled like this:

public async Task<DotvvmResponse> ProcessRequest(Uri requestUri, string method, IEnumerable<KeyValuePair<string, string>> headers, Stream contentStream)
{
    using var scope = serviceScopeFactory.CreateScope();
    var httpContext = new DefaultHttpContext()
    {
        Request =
        {
            Scheme = requestUri.Scheme,
            Host = new HostString(requestUri.Host, requestUri.Port),
            Path = requestUri.PathAndQuery,
            Method = method
        },
        Response =
        {
            Body = new MemoryStream()
        },
        ServiceScopeFactory = serviceScopeFactory,
        RequestServices = scope.ServiceProvider
    };
    foreach (var header in headers)
    {
        httpContext.Request.Headers.Add(header.Key, header.Value);
    }            
    if (contentStream != null)
    {
        httpContext.Request.Body = contentStream;
    }
    await requestDelegate(httpContext);

    return new DotvvmResponse(
        httpContext.Response.StatusCode,
        httpContext.Response.Headers.SelectMany(h => h.Value.Select(v => new KeyValuePair<string, string>(h.Key, v))),
        (MemoryStream)httpContext.Response.Body);
}

You can obtain the RequestDelegate using the standard ASP.NET Core dependency injection – it is registered there.


Communication with the host

The hosted web app will probably need to communicate with the MAUI app. Since both MAUI and DotVVM runs in the same process, it is theoretically possible to directly call methods in MAUI/DotVVM viewmodels, but DotVVM was designed as stateless and thus its C# viewmodel exists only for a short period of time – only during execution of a particular HTTP request. Most of the time, the viewmodel is represented by the JavaScript object in the page itself.

Therefore, the DotvvmWebView implementation contains several useful methods:

  • GetViewModelSnapshot – this method obtains a snapshot of the viewmodel and returns it as dynamic. I’ve been thinking about a strongly-typed way of obtaining the viewmodel, but there are many problems – using the same viewmodel class is not a good idea since it has many dependencies (on HttpContext and other services) – there would have been some interface. Dynamic might be good enough.
  • PatchViewModel – the MAUI app can send patches to the page viewmodel. Again, this is fully dynamic API, but since we are working with JavaScript, it’s probably not such a big deal.
  • CallNamedCommand – this function allows to call commands declared using <dot:NamedCommand> control in the page. Currently, you need to specify the viewId, which is a DotVVM internal ID of the associated JS module – I want to introduce some API that will get the viewId using a CSS selector – you would be able to specify CSS selector for a control that contains the named command.

DotVVM can send events to the MAUI app by calling _webview.SendNotification(methodName, arg1, arg2…) in a static command – this will invoke the PageNotificationReceived event on DotvvmWebView. Using this mechanism, you can respond to the events in DotVVM pages.


I’ve implemented the prototype of DotVVM + MAUI integration on GitHub. Currently, it supports only Windows UWP host, but I plan to add support for more platforms. There are some further challenges in making this component production-ready – DotVVM compiles its pages on the first access, which will be slow on mobile devices – we’ll probably need some way of pre-compilation.