NDC London
This post is in English, since I wrote it in English for an English audience. During NDC London I attended the following sessions:
Keynote: AI without the BS, for humans
By Scott Hanselman
He had recently been to South Africa, meeting youth that was a part of a “reversed” boot camp: they were paid to learn programming, so that whenever companies needed programmers they were available without the need to train them. They were concerned that they would not be needed in the age of generative AI. While Scott did not see this happening, he did see the need to democratize the landscape and saw great potential in small language models of the kind that could run locally using NPU units on modern laptops and phones.
He also demonstrated in a pedagogical manner how the language models use probabilities to guide the token selection, and show-cased a tool where you can see where the model makes a “decision” that guides the completion in a specific direction.
Cursed C#
By youtuber Nick Chapsas. Making the distinction between good code, bad code, cursed code and evil code, we explored cursed c# in which potentially confusing patterns is being used.
Have you ever wanted to write
foreach in 5
To mean iterate up to 5 in c#? Well, you can by implementing GetEnumerator for ints (or whatever type)
Or perhaps you wanted to write
await 5
To sleep for 5 seconds. That can also be achieved by implementing GetAwaitable for whatever type.
Overloading of operators and implementing stuff in the Dispose-method of IDisposable is also completely cursed
Personally, I was not overly impressed with this one. But fun guy to listen to if you are into youtubers.
Migrating from a monolith to a new service
By Connell Sharp.
Sharp presented a case-study from StackOverflow where they had migrated a service from a monolith to its own microservice.
First a good discussion on which parts of the monolith could (and should) be separated out. DDD was a useful part of this discussion, also separating core business domains (like what people would buy your services for) and generic subdomains (like auth, user handling, billing).
To accomplish this they used strangler fig pattern with YARP. The nice thing about YARP is that it is “just” a nuget package that you install in your .NET package, and it will route whatever you have not migrated to its old service transparently. They also needed a new frontend for the components that were using this service, and they made “microfrontend” for this where they dynamically imported the component (bundled from the new .NET project), so that they could release independently from the monolith and have the consumers experiencing the new component versions with every page reload.
He also discussed the considerations they made on migration of data, with eventual consistency with event-based updating of the microservices database together with timed synchronization in case of message loss/failure.
Consistency and Agreements in Distributed Systems
Jimmy Bogard followed up with an extended talk on the subject going into the fundamentals of how messaging needs to work for distributed systems: going into 2-phase commits, outbox/inbox pattern and sagas.
Building it up from the fundamentals makes it seem like an impossible task, but luckily there are frameworks which deliver on these concepts like NServiceBus. I would recommend watching this talk since it serves as a good introduction to the field.
Thinking Like an Architect
Author Gregor Hohpe shared his experience as a chief architect. Not aiming to be the smartest person in the room, but trying to make everyone smarter. Recognizing that leaders are not dumb even if they do not speak the technical language of the engineer, and that metaphors are valuable in getting your point across.
Old to Gold: How to Modernize Your Legacy ASP.NET Apps Gradually
Jonathan Tower demonstrated how one can use .NET upgrade assistant in combination with YARP to go from .NET Framework based applications to modern .NET.
The .NET upgrade assistant can be used from Visual studio, but also as a standalone dotnet tool.
System.web-namespace is a real challenge to migrations; it represents everything that was wrong about .NET Framework and made it necessary to go over to .NET Core. System.Web has deep integration with IIS. Luckily, there exists some shims for .NET that might help.
Ligershark can be used to bundle js with .NET.
Build RAG from Scratch
Phil Nash gave what I think was my favorite talk of the first day.
One of the major fears of LLMs is their “hallucinations” which is where they may, in the absence of clear data, veer wildly of course. RAG, or retrieval augmented generation, is a part of the solution to this problem by ensuring that the LLM will have access to relevant facts when replying.
During the talk he implemented a basic retrieval-augmented generation from scratch using a (very naive) vector containing just the word occurrence for the set of NDC abstracts. So you would have a vector with 2800 “features” (each being a unique word occurring or not), and then you could calculate a cosine similarity for each abstract to eachother to find which are related based on whether they use the same words. Obviously, a modern embedding model would provide a more clever set of features using a smaller vector, but to really grasp the concept this was very pedagogical.
Also discussed how modern datastores, including SQL, NoSQL and vector databases all support vector indexes at this point, and that some can even do the embedding work for you with a model of your choosing.
Obviously, taking the embeddings of an entire encyclopedia would not be very helpful, since it would presumably contain everything, so chunking larger documents is important.
There are also many different embedding models: some language specific, others subject specific.
Where next, C#?
Mads Torgersen, the chief language designer of C# (and fellow Scandinavian!) gave an interesting talk on the development of C#. How they try to keep the process open (everything on github) and maintain backward compatibility. The newest C# iteration will bring some improvements, but nothing revolutionary.
Dictionary expressions are coming, like collection expressions in the previous version. You will now be able to write: var dict = [“myKey”:”someValue” ] and the compiler will realize that this is a dictionary being instantiated.
You can do nameof(List<T>) if you want to in the next version.
Null conditional assignment is coming, allowing you to assign a value to a field if the object exists.
Spans is becoming more and more important in the C# landscape.
Type unions (native Result-type e.g.) is still on the drawing board. What will come soon(ish) is a revamped async state-machine, which should have good performance implications.
Real time event visualization in F# and Fable
Vagif Abilov of NRK gave a workshop on how to use Fable for web applications. Fable compiles F# to javascript, so that you can develop web pages in a sane programming language. But since it ends up being javascript, you can without issues include e.g. react components.
F# has type providers, allowing you to to make types of things with schemas, like CSS! So you can do styling with proper types.
To me it was very interesting to see how you could explicitly pass messages around to control state.
https://github.com/object/FableWorkshop.2024
Demystify cloud-native development with .NET Aspire
Maddy Montaquila presented what Microsoft is doing forward with .NET Aspire. Aspire she emphasized is not a platform that you have to fully embrace, you can use bits and pieces of it.
The cloud landscape is vast and unstandardized, but the Aspire-team feel like they are in a good place to embrace that diversity with lightweight modules to add connectors for various databases, message buses and cloud providers.
The motor of Aspire is the App host, described as a C# docker compose on crack. It basically allows you to say that my service needs a database, and then it will configure up the relevant database whether it is as a container on your docker desktop or a managed SQL server in Azure/AWS.
Importantly, telemetry is baked in to all of these connectors together with a lightweight dashboard so that you can easily monitor the health of your system.
Maddy further argued that you can use Aspire just for the developer experience, and that deployment can still be made like you have always done it (don’t touch it if it works).
There do exist some community integrations for .NET framework, but it is not a priority from the Aspire team.
Keynote: The past, present, and future of AI for application developers
Steve Sanderson presented how we got from Eliza in the 60s to today. Eliza worked by simple string replacements, and already had people fooled.
Even further back, Markov had studied Pushkin and realized that he could do statistics on how letters followed each other (z rarely follows after a x). This approach gave rise to Markov models which allows you to generate text, seemingly from nothing, but it grows impossible to compute with any significant context length.
He further gave a very interesting outline of how, technically, one could go from text completion to chats.
Real-time audio is absolutely a thing now, and it is getting to a point where we can implement it in our applications.
The Unsung Hero of Modern Software: Asynchronous Messaging
Irina Dominte gave a clear presentation of asynchronous messaging and how it is fundamentally different from the synchronous HTTP.
Asynchronous messaging gives you documents, events, commands, queries and request/reponse patterns.
But it introduces its own complexities; do you want at least once, at most once or exactly once? Messages can be delivered point to point, or published to many subscribers.
Navigating complexity in event-driven architectures: A domain-driven approach
David Boyne threw cold water in the face of everyone feeling like asynchronous messaging would make everything easy. Many considerations to make.
If you send minimal messages, with just ids which the downstream service would need to query the database for, this would induce a back pressure in your system,
On the other hand, if you pile on with all available data, this creates a tight coupling which makes it difficult to change your publisher.
Language is important. The field names mean something specific in our domain, but it might not mean the same in a different domain. Anti-corruption layers in our subscribing services would ensure that the message is translated into the domain that is appropriate for that services domain.
An alternative is to have an open host service, only messages with a generic model is published.
David gave a shoutout to Contextive that provides ubiquitous business language annotations for terms in your IDE.
The performance loop—A practical guide to profiling and benchmarking
Daniel Marbach gave a presentation on how he is doing performance improvements in C# guided by profiling and benchmarking. It is necessary to build a harness where you isolate the system under test. The presentation focused on memory and CPU, although he admitted that IO often came with much larger gains. A earlier presentation with the same title has a recording available https://www.youtube.com/watch?v=owoY7jd2oIg