﻿<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
	<channel>
		<title>VukVuk</title>
		<link>/</link>
		<description />
		<copyright>Copyright © 2025</copyright>
		<pubDate>Thu, 23 Oct 2025 07:21:21 GMT</pubDate>
		<lastBuildDate>Thu, 23 Oct 2025 07:21:21 GMT</lastBuildDate>
		<item>
			<title>Corporations and the AI Hype Train</title>
			<link>/posts/corporations-and-the-ai-hype-train</link>
			<description>&lt;p&gt;Some corporations really &lt;em&gt;&lt;strong&gt;ride the AI hype train&lt;/strong&gt;&lt;/em&gt;. A few days ago, I heard that some companies are not just forcing developers to use GitHub Copilot, but also &lt;em&gt;&lt;strong&gt;check how much developers are using AI daily&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/corporations-and-the-ai-hype-train</guid>
			<pubDate>Thu, 21 Aug 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Some corporations really &lt;em&gt;&lt;strong&gt;ride the AI hype train&lt;/strong&gt;&lt;/em&gt;. A few days ago, I heard that some companies are not just forcing developers to use GitHub Copilot, but also &lt;em&gt;&lt;strong&gt;check how much developers are using AI daily&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I'm always amazed by how little understanding some managers have of what developers really do.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you want to measure something, measure the actual output. Do not measure some arbitrary parameter you don't even understand. Although many of us do not work in creative fields, &lt;strong&gt;never underestimate the human capability to find novel ways to abuse the system.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Why would you write code with quick autocomplete like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;if(!isAdmin)
{
	throw new UnauthorizedException();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you can type:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-LLM"&gt;Check if the user is an admin; if not, the user should not be able to continue.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;It's slower, more cumbersome, but at the end of the day, the dashboard says your productivity is up!&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And no, I do not have a silver bullet for measuring the dev productivity. But, I'm 100% sure this isn't it. Every proxy metric eventually measures &lt;em&gt;&lt;strong&gt;bullshit per minute&lt;/strong&gt;&lt;/em&gt;, not output.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Book: Micro Frontends in Action</title>
			<link>/posts/book-micro-frontends-in-action</link>
			<description>&lt;p&gt;&lt;em&gt;Micro Frontends in Action&lt;/em&gt; is Michael Geer's 2020 book. It is a &lt;strong&gt;practical guide&lt;/strong&gt; to implementing micro frontends, an approach that brings the microservices concept to the front end. The book provides a way to split monolithic frontend applications into smaller, more independent pieces, making large-scale web applications &lt;em&gt;theoretically&lt;/em&gt; more maintainable and scalable.&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/book-micro-frontends-in-action</guid>
			<pubDate>Tue, 25 Mar 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;&lt;em&gt;Micro Frontends in Action&lt;/em&gt; is Michael Geer's 2020 book. It is a &lt;strong&gt;practical guide&lt;/strong&gt; to implementing micro frontends, an approach that brings the microservices concept to the front end. The book provides a way to split monolithic frontend applications into smaller, more independent pieces, making large-scale web applications &lt;em&gt;theoretically&lt;/em&gt; more maintainable and scalable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Microfrontends&lt;/code&gt; is a term that comes from the ThoughtWorks TechnologyRadar (Nov 2016). However, even nine years later, this approach is still not mainstream. Companies with large websites like Spotify, Amazon, and IKEA use micro frontends to scale their front-end development. Still, companies of this size and websites probably used something similar, long before 2016, because they always had a problem with hundreds of teams working concurrently. They find custom solutions they see fit.&lt;/p&gt;
&lt;p&gt;I liked the book, and I read it in just two days. It's a great introduction to Micro Frontends as the concept. The only problem is the actual implementation. The book describes a few approaches, but as far as I can see, almost none are mainstream approaches. If I had to implement the Micro Frontends, I would probably start with something simple, isolation on the &lt;code&gt;URL&lt;/code&gt; level and separate applications. Maybe, with better frameworks with better support for micro frontends, I would change my mind.&lt;/p&gt;
&lt;p&gt;The one thing, the book does not focus on, but I find interesting is upgrading frontends to other technologies. Maybe Micro Frontends could be used like a &lt;a href="https://vukvuk.com/posts/strangler-pattern"&gt;strangler fig pattern&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="author"&gt;Author&lt;/h2&gt;
&lt;p&gt;Michael Geers is a software developer and architect who focuses on building user interfaces.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://micro-frontends.org/"&gt;https://micro-frontends.org/&lt;/a&gt; - author's website&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vukvuk.com/posts/web-components-with-blazor"&gt;https://vukvuk.com/posts/web-components-with-blazor&lt;/a&gt; - One of my experiments with Micro Frontends with Blazor&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Dead Horse Theory in Software Projects</title>
			<link>/posts/dead-horse-theory</link>
			<description>&lt;p&gt;&lt;em&gt;&amp;quot;When you discover that you are riding a dead horse, the best strategy is to dismount.&amp;quot;&lt;/em&gt;&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/dead-horse-theory</guid>
			<pubDate>Mon, 17 Mar 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;h2 id="what-is-the-dead-horse-theory"&gt;What is the Dead Horse Theory?&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;"When you discover that you are riding a dead horse, the best strategy is to dismount."&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;But it's hard to acknowledge the failure and face the problem head-on. It's much easier to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Buy a stronger whip&lt;/strong&gt; - Try to force people to work harder.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change riders&lt;/strong&gt; - Change the leadership but still stick with the failed approach.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Declaring the horse is not really dead&lt;/strong&gt; - Be in denial.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forming a committee to study the horse&lt;/strong&gt; - Bureaucracy is rarely the answer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comparing the dead horse to others that are worse&lt;/strong&gt; - Rationalizing failure by talking about others who also have dead horses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rebranding the horse&lt;/strong&gt; - Make cosmetic changes without actual improvement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hiring consultants to revive the horse&lt;/strong&gt; - Outsource the problem to consultants, who can only prolong the conclusion that the horse is dead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="dead-horse-theory-in-software-projects"&gt;Dead Horse Theory in Software Projects&lt;/h2&gt;
&lt;p&gt;&lt;img src="/media/content/everyone-was-thinking-it.png" alt="Everyone was thinking it, I just said it."&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Author's thoughtful Portrait during explantation of dead horse project&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Compared to traditional engineering, software engineering is a relatively young industry. Despite its youth, we have gone through multiple paradigm shifts, from structured programming to object-oriented to cloud and now AI-driven development. Our reliance on emerging technologies and generally flexible approaches makes us unique and especially prone to the dead horse theory.&lt;/p&gt;
&lt;p&gt;For example, asking to &lt;em&gt;&lt;strong&gt;add a new floor with the Olympic pool&lt;/strong&gt;&lt;/em&gt; between the 9th and 10th floors of the existing residential building would be &lt;em&gt;stupid and unthinkable&lt;/em&gt; in civil engineering. But we in software development are accustomed to similarly absurd requests. No wonder we continue to maintain legacy code that should be rewritten and stick to a failing architecture instead of refactoring.&lt;/p&gt;
&lt;h2 id="conclusions-and-personal-dead-horses"&gt;Conclusions and Personal Dead Horses&lt;/h2&gt;
&lt;p&gt;I worked on several dead-horse projects. Horses' deaths were different, but the result was always the same. I should have pivoted much earlier and tried something different in my startups, but my stubbornness and pride did not let me see the painful truth. In a corporate environment, I also got stuck with some dead-horse projects. In these cases, developers were not the problem. Higher-ups did not believe the horse was dead. They still don't.&lt;/p&gt;
&lt;p&gt;&lt;img src="/media/content/oh-agent-smith.gif" alt="a man wearing sunglasses and a suit is saying it is inevitable"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Mr. Anderson, the horse is dead. A complete rewrite is inevitable.&lt;/em&gt;&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Data as Code: An Alternative to Storing Static and Semi-static Data in Databases</title>
			<link>/posts/data-as-code</link>
			<description>&lt;p&gt;We often unquestioningly follow the mantra: &lt;em&gt;&lt;strong&gt;&amp;quot;Put everything in the (relational) database.&amp;quot;&lt;/strong&gt;&lt;/em&gt; But what happens when your application grows to hundreds or even thousands of tables, many containing just a handful of static or rarely-changing entries? Suddenly, your &lt;em&gt;&lt;strong&gt;database looks like an unorganized closet, not an efficient data store&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/data-as-code</guid>
			<pubDate>Sun, 09 Mar 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;h2 id="is-storing-all-data-in-a-database-always-the-best-choice"&gt;Is storing all data in a database always the best choice?&lt;/h2&gt;
&lt;p&gt;We often unquestioningly follow the mantra: &lt;em&gt;&lt;strong&gt;&amp;quot;Put everything in the (relational) database.&amp;quot;&lt;/strong&gt;&lt;/em&gt; But what happens when your application grows to hundreds or even thousands of tables, many containing just a handful of static or rarely-changing entries? Suddenly, your &lt;em&gt;&lt;strong&gt;database looks like an unorganized closet, not an efficient data store&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Why do we store even simple static datasets, such as selectors for measurement units, in the database? Why do we store rarely changing data like lists of countries and languages in data storage optimized for reading and writing? Is there a way to simplify architecture and improve the performance of static data?&lt;/p&gt;
&lt;h2 id="why-use-data-as-code-dynamic-and-static-data"&gt;Why Use Data as Code?  Dynamic and Static Data&lt;/h2&gt;
&lt;p&gt;The main argument for the &amp;quot;&lt;em&gt;store everything in (relational) database&lt;/em&gt;&amp;quot; approach was that the entire data for an application should be stored in the database, so it is possible to recreate everything just with a query. It is a valid argument, but the cases in which you must do this are sporadic. Another argument is that multiple applications (from different teams) should use the same database. I would strongly disagree with this (in most cases). This reminds me of the tech legend - &lt;em&gt;Bezos API Mandate&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;All teams will henceforth expose their data and functionality through service interfaces.&lt;/li&gt;
&lt;li&gt;There will be no other form of interprocess communication allowed: no direct linking, no direct reads of another team’s data store, no shared-memory model, no back-doors whatsoever. The only communication allowed is via service interface calls over the network.&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Direct access to the database from multiple applications &lt;em&gt;may&lt;/em&gt; be the fastest, but the price of database coupling is too much to pay.&lt;/p&gt;
&lt;p&gt;Another problem is why we often do not split more strictly dynamic - frequently changed, user-created data from &lt;code&gt;(semi)static&lt;/code&gt; - infrequent, almost unchanging, non-user, application-used data. An example of (semi)static data would be the list of countries, and an example of dynamic data would be a customer's order history. In the worst-case scenario, &lt;code&gt;Database-per-Tenant,&lt;/code&gt; you must update all your databases with static data.&lt;/p&gt;
&lt;p&gt;An alternative, but still in the domain of &amp;quot;&lt;em&gt;all data should be in database&lt;/em&gt;&amp;quot;, is having a separate static data database. The problem with this approach is that you lose the ability to query join calls. Sure, there are ways to join tables, even in separate databases, but let's not go with that route, especially for the frequently used queries.&lt;/p&gt;
&lt;h2 id="data-as-a-service-for-semi-static-data"&gt;Data as a Service for (Semi) Static Data&lt;/h2&gt;
&lt;p&gt;An interesting solution is &lt;code&gt;Data as a Service&lt;/code&gt;. Put the (semi) static data in the separate service and make a synchronous call. In many cases, this works great. You have a specialized standalone service that is easy to update with new data. You are also free to share this service with many applications.&lt;/p&gt;
&lt;p&gt;The main problem is the data access. You must make a synchronous call to an external service and get the data. You can cache, but if there is a chance that data could be updated, you need a way to invalidate the cache. Things will get even more complex if it is essential to have the same data across all servers or applications, and if there is a cache per service.&lt;/p&gt;
&lt;p&gt;Also, there are problems with the nature of services. If you access something external, there will always be some latency and performance issues, and you lose the ability to go offline.&lt;/p&gt;
&lt;p&gt;For (semi) static data, the main benefit of data as a service is the possibility of independent deployment of extensive data that is frequently updated but still static data. Security could also be a benefit because you can easily switch from secret data to jibberish for development, even on the same service.&lt;/p&gt;
&lt;p&gt;However, I especially like data as the service approach because it can be centralized. We are avoiding local data duplication, especially in the database per customer approach, thus making database backups more efficient. Also, when the data is in your application, it is your problem, but when you access it, it is &amp;quot;&lt;em&gt;someone else's&lt;/em&gt;&amp;quot;.&lt;/p&gt;
&lt;h2 id="data-as-code"&gt;Data as Code&lt;/h2&gt;
&lt;p&gt;While Data as a Service can work, it introduces network latency and cache complexity. Is there a way if we could store semi-static data directly in our application?&lt;/p&gt;
&lt;p&gt;Data as Code is a somewhat &lt;em&gt;controversial&lt;/em&gt; approach to storing static data in the applications. As developers, we have a '&lt;em&gt;natural tendency&lt;/em&gt;' for everything to follow the &lt;em&gt;&lt;strong&gt;Single Responsibility Principle&lt;/strong&gt;&lt;/em&gt;. Following that principle strictly, &lt;strong&gt;code should be a &lt;em&gt;set of instructions&lt;/em&gt;&lt;/strong&gt;, not the data for the application. However, I genuinely believe we can store a lot of (semi)static data in the app code or as a separate library in many enterprise applications.&lt;/p&gt;
&lt;h3 id="important-data-parameters"&gt;Important Data Parameters&lt;/h3&gt;
&lt;p&gt;There are a few parameters we hate to take into consideration when we decide to store Data as Code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Change Frequency&lt;/strong&gt; - How often is data changed? Is it rare, like a list of possible units in the combo box? Or is it some regulation that is changed more frequently, like several times a year?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complexity&lt;/strong&gt; - How many fields? Are some of the fields related to other data?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Number of Rows&lt;/strong&gt; - How significant is the data list? 5 rows? 10k rows?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Frequency&lt;/strong&gt; - How often is the data accessed or queried?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="data-as-library"&gt;Data as Library&lt;/h3&gt;
&lt;p&gt;The simplest solution for storing Data as Code is to put it with the main code. The obvious advantage with this approach is that you have everything '&lt;em&gt;under one roof&lt;/em&gt;'. The disadvantage is that there will be a longer build time, mainly if you use code-generating tools that automatically regenerate the entire data set as code. You can not also give the access and capabilities of updating outside the primary team that easy.&lt;/p&gt;
&lt;p&gt;However, having data as a separate library, especially while working on the method signatures, can be tedious. Good &lt;code&gt;infrastructure as code&lt;/code&gt; helps because it is very easy to set up new libraries, but you still have to push the signature changes, build it, and then reference it or upgrade the library version in the main code.&lt;/p&gt;
&lt;p&gt;Many tools can automatically update libraries to the newest version for the data used in several services.&lt;/p&gt;
&lt;h2 id="data-as-code-in-the.net"&gt;Data as Code in the .NET&lt;/h2&gt;
&lt;p&gt;I use &lt;code&gt;Data as Code&lt;/code&gt; in the .NET in several ways.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Smart Enum&lt;/li&gt;
&lt;li&gt;Static Class&lt;/li&gt;
&lt;li&gt;Resx (+ Static Class)&lt;/li&gt;
&lt;li&gt;Embedded Databases&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="smart-enum"&gt;Smart Enum&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Ardalis.SmartEnum&lt;/code&gt; is probably my favourite library. It expands the enum concept by adding object-oriented capabilities.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;using Ardalis.SmartEnum;

public class OrderStatus : SmartEnum&amp;lt;OrderStatus&amp;gt;
{
    // Define static instances (similar to enum values)
    public static readonly OrderStatus Pending = new OrderStatus(&amp;quot;Pending&amp;quot;, 1, true, &amp;quot;Your order is pending.&amp;quot;);
    public static readonly OrderStatus Shipped = new OrderStatus(&amp;quot;Shipped&amp;quot;, 2, false, &amp;quot;Your order has been shipped.&amp;quot;);
    public static readonly OrderStatus Delivered = new OrderStatus(&amp;quot;Delivered&amp;quot;, 3, false, &amp;quot;Your order has been delivered.&amp;quot;);

    // Extra properties
    public bool CanCancel { get; }
    public string NotificationMessage { get; }

    // Constructor is private to prevent new instances outside predefined ones
    private OrderStatus(string name, int value, bool canCancel, string message)
        : base(name, value)
    {
        CanCancel = canCancel;
        NotificationMessage = message;
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only thing I almost always expand is to force Guid usage for Id. By giving a unique Guid to every SmartEnum instance, I ensure that the proper &lt;code&gt;Id&lt;/code&gt; is used in the data storage. For example, I often work with countries and languages and frequently swap these things by mistake, like &lt;code&gt;Country.France.Id&lt;/code&gt; with &lt;code&gt;Language.French.Id&lt;/code&gt;. The data would be valid with an integer &lt;code&gt;Id&lt;/code&gt;, but it shouldn't be. If we take care of the uniqueness of the &lt;code&gt;Guid&lt;/code&gt;, we can quickly check if the &lt;code&gt;Guid&lt;/code&gt; exists in the &lt;code&gt;SmartEnum&lt;/code&gt;. Of course, we lose the ability to insert the data manually (you will not remember the &lt;code&gt;Guid&lt;/code&gt; as a value, as you would an integer).&lt;/p&gt;
&lt;p&gt;The only advantage I find with classic C# enums is that they are compile-time. In some cases, you have to use enums.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use Smart Enums for data?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you have some data with a possible complex relationship between other data, one item of the Smart Enum can reference another item of another Smart Enum list - &lt;em&gt;Countries have a list of Languages&lt;/em&gt;. Use also when you need true enum capabilities like referencing something by name - &lt;code&gt;Country.Serbia&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="static-class"&gt;Static Class&lt;/h3&gt;
&lt;p&gt;Another good approach is to use static classes. We can determine the memory footprint at the compile time if we use static class with static readonly list and hardcoded values. In .Net, the garbage collector does not need to manage it since it's static &lt;code&gt;read-only&lt;/code&gt; and exists during the lifetime of the applications. There are no problems with thread-safety because it's immutable. Performance is optimal because accessing static fields is very fast, and there could be no memory leaks; it's just part of the application's static memory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use Static Classes for data?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you have a lot of items, and you need all of them in the memory all the time! Even tens of thousands of items are not a problem with modern hardware.&lt;/p&gt;
&lt;h3 id="resx-static-class"&gt;Resx (+ Static Class)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;.resx&lt;/code&gt; files&lt;/strong&gt; (Resource Files) in .NET are XML-based files primarily used to store localizations. However, it is possible, although rarely used, to store strings, images, and other objects.&lt;/p&gt;
&lt;p&gt;The main difference between this solution and the others is that it does not load data into memory before first access. You can dispose of it manually, by dropping the reference to &lt;code&gt;ResourceManager&lt;/code&gt; and forcing garbage collection. Reload is automatic, just by re-accessing the resource&lt;/p&gt;
&lt;p&gt;At compile time, &lt;code&gt;.resx&lt;/code&gt; files are converted into binary &lt;code&gt;.resources&lt;/code&gt; files. When a row is accessed, &lt;code&gt;ResourceManager&lt;/code&gt; loads it into a dictionary-like structure, enabling O(1) lookups for subsequent requests.&lt;/p&gt;
&lt;p&gt;However, &lt;code&gt;.resx&lt;/code&gt; is a key-value structure. It's not optimized for searching by more than one parameter.&lt;/p&gt;
&lt;p&gt;Using Static Class (or SmartEnum) with Resx is also possible. You can separate data into often used with static class, and less used and more extensive data in Resx files. For example, you can put the main searchable, frequently accessed, part in static classes, and the other fine details about that 'row' you can put in Resx, and call it when necessary. You can search by more than one value with Static Class, and put additional data in the Resx, and access it only when necessary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use Resx (+ Static Class)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Whenever you have a lot of data and you want to have the capability to load it sparingly.&lt;/p&gt;
&lt;p&gt;Sure, you can create your own solution for loading data from textual files like &lt;code&gt;json&lt;/code&gt;, &lt;code&gt;csv&lt;/code&gt;, &lt;code&gt;xml&lt;/code&gt;, but with &lt;code&gt;Resx&lt;/code&gt; files, you get Microsoft's battle-proven solution.&lt;/p&gt;
&lt;h3 id="embedded-databases"&gt;Embedded Databases&lt;/h3&gt;
&lt;p&gt;The most heavy-duty type data as the code would be a '&lt;em&gt;true&lt;/em&gt;' database deployed in the library form, with binary storage format, optimized for fast reads/writes and efficient storage with minimal overhead. However, these biggest strengths are also their greatest weaknesses, because you need specialized data updating tools.&lt;/p&gt;
&lt;p&gt;There are many c# compatible embedded databases. They come in many types, such as &lt;em&gt;Key-Value Stores, Document-Based Databases, Relational Embedded Databases, Object Databases&lt;/em&gt;, etc. The most popular are &lt;code&gt;SQLite&lt;/code&gt; and &lt;code&gt;LiteDB&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Generally, I don't use this approach. But I've been thinking about one problem for the last few months, and I will probably try to use &lt;code&gt;LiteDb&lt;/code&gt; for some data.&lt;/p&gt;
&lt;h2 id="data-generation-for-data-in-code"&gt;Data Generation for Data in Code&lt;/h2&gt;
&lt;p&gt;Creating and updating &lt;code&gt;Data in Code&lt;/code&gt; is especially interesting. With traditional databases, we have well-known procedures and scripts. Here are the things that are a little different.&lt;/p&gt;
&lt;h3 id="manual"&gt;Manual&lt;/h3&gt;
&lt;p&gt;For simple use cases, we are going to type everything manually. It's quick and easy, and we have source control to check the changes.&lt;/p&gt;
&lt;h3 id="llm"&gt;LLM&lt;/h3&gt;
&lt;p&gt;LLMs are great tools for inserting data in the code. The &lt;em&gt;only&lt;/em&gt; problems are hallucinations. &lt;em&gt;&lt;strong&gt;We have to be very careful&lt;/strong&gt;&lt;/em&gt; and check all the data manually. Once, I generated some static data, and LLM '&lt;em&gt;dropped&lt;/em&gt;' a char from the &lt;code&gt;guid&lt;/code&gt;. &lt;em&gt;It was not a fun day&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Country	Code
Austria	AT
Belgium	BE
...


Create ardalis.smartenum list
use guid as Id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;LLM Input&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;using Ardalis.SmartEnum;

public sealed class Country : SmartEnum&amp;lt;Country, Guid&amp;gt;
{
    public static readonly Country Austria = new(&amp;quot;Austria&amp;quot;, new Guid(&amp;quot;f53aee0c-d8e2-4b07-832d-f1f25b0ecacf&amp;quot;));
    public static readonly Country Belgium = new(&amp;quot;Belgium&amp;quot;, new Guid(&amp;quot;7b4c62b4-5ed8-4578-9aee-3aa9454c84b6&amp;quot;));
...

    private Country(string name, Guid id) : base(name, id) { }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;LLM Output with ChatGPT 4.5 (research preview)&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;new Ardalis.SmartEnum.CountryCode(
    new Ardalis.SmartEnum.Entry(
        name: &amp;quot;Austria&amp;quot;,
        id: &amp;quot;9f3e4d0b-75a8-1126-a84b-bdcdff1b8607&amp;quot;, // Replace with actual GUID
        displayText: &amp;quot;AT&amp;quot;
    ),
    new Ardalis.SmartEnum.Entry(
        name: &amp;quot;Belgium&amp;quot;,
        id: &amp;quot;9f3e4d0b-75a8-1126-a84b-bdcdff1b8608&amp;quot;, // Replace with actual GUID
        displayText: &amp;quot;BE&amp;quot;
    ),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;LLM Output with Deepseek R1 Distill 14B - Obvious mistake&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Check the output! Even high-end LLMs will sometimes do just a part of the job because &amp;quot;&lt;em&gt;they think&lt;/em&gt;&amp;quot; you need an explanation of how static data works. I would also add a &lt;code&gt;guid&lt;/code&gt; column to &lt;code&gt;Excel&lt;/code&gt; and force that data. &amp;quot;&lt;em&gt;Weaker&lt;/em&gt;&amp;quot; LLMs that could be run locally, like Deepseek R1 Distill with just 14B parameters, did not write good code. Be even more careful with the output of your original static data!&lt;/p&gt;
&lt;h3 id="scripts-and-apps"&gt;Scripts and Apps&lt;/h3&gt;
&lt;p&gt;We can create custom scripts or apps to generate data. Sometimes, I use &lt;code&gt;csx&lt;/code&gt; (C# Script Files) and c# Console Applications. The advantage of this approach is that you have &lt;em&gt;'complete power'&lt;/em&gt; of the whole programming language. You can make many modifications, such as cleaning up the data. The disadvantage is that you have a somewhat more complex &lt;code&gt;pipeline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Scripts and Apps have an excellent support for &lt;code&gt;csv&lt;/code&gt; and &lt;code&gt;Excel&lt;/code&gt;. You can even use &lt;code&gt;Microsoft Access&lt;/code&gt; if necessary. However, remember that Access is Windows only, and for Excel, you need additional libraries, sometimes commercial.&lt;/p&gt;
&lt;p&gt;If you use the &lt;code&gt;embedded database&lt;/code&gt; route, this is your only option (or at least the most elegant).&lt;/p&gt;
&lt;h3 id="t4-text-templates"&gt;T4 Text Templates&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Text Template Transformation Toolkit&lt;/strong&gt; (T4) is a free, open-source, template-based text generator. It is supported in &lt;code&gt;Rider&lt;/code&gt; and &lt;code&gt;Visual Studio&lt;/code&gt;, and you specify in project files that you will (re)generate &lt;code&gt;tt&lt;/code&gt; files during each build.&lt;/p&gt;
&lt;p&gt;T4 templates let us generate C# code dynamically. Here's a simple example that generates an enum from a list of colors:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;&amp;lt;#&amp;#64; template debug=&amp;quot;false&amp;quot; hostspecific=&amp;quot;true&amp;quot; language=&amp;quot;C#&amp;quot; #&amp;gt;
&amp;lt;#&amp;#64; output extension=&amp;quot;.cs&amp;quot; #&amp;gt;
&amp;lt;#&amp;#64; assembly name=&amp;quot;System.Core&amp;quot; #&amp;gt;
&amp;lt;#&amp;#64; import namespace=&amp;quot;System&amp;quot; #&amp;gt;
&amp;lt;#&amp;#64; import namespace=&amp;quot;System.Collections.Generic&amp;quot; #&amp;gt;

namespace GeneratedEnums
{
    public enum ColorEnum
    {
&amp;lt;#
    // Define color names and their hex values
    var colors = new Dictionary&amp;lt;string, string&amp;gt;
    {
        { &amp;quot;Red&amp;quot;, &amp;quot;#FF0000&amp;quot; },
        { &amp;quot;Green&amp;quot;, &amp;quot;#00FF00&amp;quot; },
        { &amp;quot;Blue&amp;quot;, &amp;quot;#0000FF&amp;quot; },
        { &amp;quot;Yellow&amp;quot;, &amp;quot;#FFFF00&amp;quot; },
        { &amp;quot;Cyan&amp;quot;, &amp;quot;#00FFFF&amp;quot; },
        { &amp;quot;Magenta&amp;quot;, &amp;quot;#FF00FF&amp;quot; },
        { &amp;quot;Black&amp;quot;, &amp;quot;#000000&amp;quot; },
        { &amp;quot;White&amp;quot;, &amp;quot;#FFFFFF&amp;quot; },
        { &amp;quot;Gray&amp;quot;, &amp;quot;#808080&amp;quot; }
    };

    foreach (var kvp in colors)
    {
#&amp;gt;
        &amp;lt;#= kvp.Key #&amp;gt; = 0x&amp;lt;#= kvp.Value.Replace(&amp;quot;#&amp;quot;, &amp;quot;&amp;quot;) #&amp;gt;,
&amp;lt;#
    }
#&amp;gt;
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;The example illustrates how generating enum from a `tt file is possible.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You always have the option to read rows from &lt;code&gt;csv&lt;/code&gt; or &lt;code&gt;Excel&lt;/code&gt; and insert them in the code as objects (or enum values).&lt;/p&gt;
&lt;p&gt;The disadvantage of T4 is its speed and somewhat &lt;em&gt;clunky&lt;/em&gt; tooling. Rider, for some reason, works much better than Visual Studio. Large Excel files will freeze even the better hardware. Also, generating data on the build will prolong &lt;a href="https://vukvuk.com/posts/report-build-times"&gt;build times&lt;/a&gt; significantly. I recommend moving these data as separate &lt;code&gt;nugets&lt;/code&gt;, even if they are used only in one application.&lt;/p&gt;
&lt;p&gt;Also, remember that in practice, the same code data generated from a basic c# console application will be created faster than with T4.&lt;/p&gt;
&lt;p&gt;Why does Microsoft call T4, '&lt;a href="https://learn.microsoft.com/en-us/visualstudio/modeling/writing-a-t4-text-template?view=vs-2022"&gt;T4 Text Templates&lt;/a&gt;' and not &lt;code&gt;T6&lt;/code&gt;?&lt;/p&gt;
&lt;h2 id="updating-data-on-dev-machine-or-pipeline-and-testing-data"&gt;Updating Data on Dev Machine or Pipeline and Testing Data&lt;/h2&gt;
&lt;p&gt;Should we update data on the local dev machine and push the 'compiled' data into the source control directly, or should we have a pipeline that 'compiles' the data, pushes the changes into source control, and then builds the nuget?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;It depends.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If the developers only update data, compiling it locally and pushing it for additional compilation is fine. If the plan is to open data updating for non-technical personnel, you must create a mechanism for updating the source files, generating code as data, and finally, compiling.&lt;/p&gt;
&lt;h2 id="data-warning"&gt;Data Warning!&lt;/h2&gt;
&lt;p&gt;For simple use cases, like a list of colors, changes for mistakes are low. However, even more complex data will significantly increase the chance for error. &lt;em&gt;&lt;strong&gt;Think your Excel data is error-free&lt;/strong&gt;&lt;/em&gt;? &lt;strong&gt;Think again&lt;/strong&gt;. Even minor formatting mistakes can break your data import process.&lt;/p&gt;
&lt;p&gt;Create innovative unit tests. Maybe even load the data from &lt;code&gt;Excel&lt;/code&gt; separately in tests and unit tests against some of that data. Check at least the number of items with tests.&lt;/p&gt;
&lt;p&gt;However, this problem is not with &lt;code&gt;Data as Code&lt;/code&gt;; you have the same problem with &lt;code&gt;SQL Scripts&lt;/code&gt;. But, if someone knows how to write and run SQL Scripts, he knows how to clean up data.&lt;/p&gt;
&lt;h2 id="concerns"&gt;Concerns&lt;/h2&gt;
&lt;h3 id="memory"&gt;Memory&lt;/h3&gt;
&lt;p&gt;The first concern is always the memory usage. But, in most cases, it is negligible. We made so many bad decisions regarding computers and software development regarding optimization; a little bit of data in the memory will not break your app.&lt;/p&gt;
&lt;h3 id="recompilation"&gt;Recompilation&lt;/h3&gt;
&lt;p&gt;In most cases, &lt;code&gt;Data as Code&lt;/code&gt; requires recompilation. With modern continuous integration tools, we have much bigger issues than data updating if we have a problem with recompilation and deployment.&lt;/p&gt;
&lt;h3 id="versioning"&gt;Versioning&lt;/h3&gt;
&lt;p&gt;With &lt;code&gt;Data as Code&lt;/code&gt; in &lt;em&gt;libraries&lt;/em&gt;, having different data on different services can cause problems. However, we can automatically reference the newest libraries with modern deployment tools.&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Type-Safe&lt;/th&gt;
&lt;th&gt;Advanced Searching&lt;/th&gt;
&lt;th&gt;Requires Recompilation&lt;/th&gt;
&lt;th&gt;Usable in Attributes&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data as a Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Best for sharing access across multiple applications but introduces network latency and cache complexity.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enums&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Best for small set of simple data and usage with Attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Smart Enums&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes (In-Memory LINQ)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Best for structured data with relationships, metadata, and enum-like behavior, allowing rich metadata and filtering.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Static Classes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes (In-Memory LINQ)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Best for frequently accessed large datasets that remain in memory for optimal performance, supporting multi-property searches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resx (+ Static Class)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Limited (Key Lookup Only) / ✅ Yes (with Static Class)&lt;/td&gt;
&lt;td&gt;❌ No / ✅ Yes (with Static Class)&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Best for storing large but infrequently accessed data but has limited search capabilities.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Embedded Databases (SQLite, LiteDB)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes (SQL Queries)&lt;/td&gt;
&lt;td&gt;⚠️ Requires running an update script&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Best for heavy-duty, structured data storage requiring efficient reads and writes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;Data as code is &lt;em&gt;&lt;strong&gt;not&lt;/strong&gt;&lt;/em&gt; a &lt;code&gt;silver bullet&lt;/code&gt;. It's another valuable tool. For many applications, we can simplify work with data significantly by evaluating data usage and update patterns.&lt;/p&gt;
&lt;p&gt;You always have the option to combine multiple approaches. For example, I wanted to have type safety for UI localization for one project. The translations were in &lt;code&gt;resx&lt;/code&gt; files. When working with translations in code, I used &lt;code&gt;smart enums&lt;/code&gt;, and for 'tagging' attributes for automatic translations, I used &lt;code&gt;enums&lt;/code&gt;.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Mermaid - Diagramming and Charting Tool</title>
			<link>/posts/mermaid</link>
			<description>&lt;p&gt;&lt;code&gt;Mermaid&lt;/code&gt; is a JavaScript library that converts text, strict markdown-like syntax, to diagrams. It is similar to &lt;a href="https://vukvuk.com/posts/plantuml"&gt;PlantUML&lt;/a&gt; but has broader use, popularity, and support.&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/mermaid</guid>
			<pubDate>Sun, 23 Feb 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;h2 id="what-is-mermaid.js"&gt;What is Mermaid(.js)?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Mermaid&lt;/code&gt; is a JavaScript library that converts text, strict markdown-like syntax, to diagrams. It is similar to &lt;a href="https://vukvuk.com/posts/plantuml"&gt;PlantUML&lt;/a&gt; but has broader use, popularity, and support.&lt;/p&gt;
&lt;p&gt;Compared to traditional diagramming tools like &lt;code&gt;Visio&lt;/code&gt; and &lt;code&gt;Draw.io&lt;/code&gt;, where you create diagrams by drawing elements and relationships, with &lt;code&gt;Mermaid,&lt;/code&gt; you create diagrams by strictly defining elements and their relationships with code. The &lt;code&gt;Diagram as code&lt;/code&gt; is the Mermaid's greatest strength and the most significant weakness. It's fantastic because if you know all the elements and relationships, it's easy to define them with code. Creating tools for generating mermaid diagrams from existing code or data is simple. You can even use LLMs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Give me c# factory example with Mermaid diagrams.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;classDiagram
    class IProduct {
        &amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
        +Operation()
    }
    class ConcreteProductA {
        +Operation()
    }
    class ConcreteProductB {
        +Operation()
    }
    class ProductFactory {
        +CreateProduct(string)
    }
    IProduct &amp;lt;|-- ConcreteProductA
    IProduct &amp;lt;|-- ConcreteProductB
    ProductFactory --&amp;gt; IProduct : creates

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Simple LLM Input and Result&lt;/em&gt;&lt;/p&gt;
&lt;div class="mermaid"&gt;classDiagram
    class IProduct {
        &amp;lt;&amp;lt;interface&gt;&gt;
        +Operation()
    }
    class ConcreteProductA {
        +Operation()
    }
    class ConcreteProductB {
        +Operation()
    }
    class ProductFactory {
        +CreateProduct(string)
    }
    IProduct &amp;lt;|-- ConcreteProductA
    IProduct &amp;lt;|-- ConcreteProductB
    ProductFactory --&gt; IProduct : creates

&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Mermaid Diagram from LLM&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;However, as a &lt;em&gt;visual thinker&lt;/em&gt;, I find it &lt;em&gt;&lt;strong&gt;hard to be creative with Mermaid&lt;/strong&gt;&lt;/em&gt;. When &lt;em&gt;&lt;strong&gt;brainstorming&lt;/strong&gt;&lt;/em&gt;, I'm much more comfortable with something like &lt;a href="https://whimsical.com/"&gt;Whimsical&lt;/a&gt;, where I can focus on elements and relationships by dragging and dropping. Also, for more complex diagrams, or diagrams where I need to be precise about where I want to put the elements, Mermaid is not that useful. &lt;em&gt;&lt;strong&gt;As developers, we often neglect that data visualizations are stories you need to tell, not just truthful representations of the facts.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Mermaid, as a text-based tool, is &lt;em&gt;version control friendly&lt;/em&gt;. It integrates into most CI/CD, Markdown, documentation tools, popular CMS, Static Site Generators, IDEs, and Text Editors. Most of the diagrams for this blog are created with Mermaid.&lt;/p&gt;
&lt;p&gt;Mermaid is in active development and frequently adds new diagrams and features.&lt;/p&gt;
&lt;h2 id="key-diagram-types-and-examples"&gt;Key Diagram Types and Examples&lt;/h2&gt;
&lt;p&gt;As of 11.4.1, supported Diagrams&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flowchart&lt;/li&gt;
&lt;li&gt;Sequence Diagram&lt;/li&gt;
&lt;li&gt;Class Diagram&lt;/li&gt;
&lt;li&gt;State Diagram&lt;/li&gt;
&lt;li&gt;Entity Relationship Diagram&lt;/li&gt;
&lt;li&gt;User Journey&lt;/li&gt;
&lt;li&gt;Gantt&lt;/li&gt;
&lt;li&gt;Pie Chart&lt;/li&gt;
&lt;li&gt;Quadrant Chart&lt;/li&gt;
&lt;li&gt;Requirement Diagram&lt;/li&gt;
&lt;li&gt;Gitgraph (Git) Diagram&lt;/li&gt;
&lt;li&gt;C4 Diagram 🦺⚠️&lt;/li&gt;
&lt;li&gt;Mindmaps&lt;/li&gt;
&lt;li&gt;Timeline&lt;/li&gt;
&lt;li&gt;ZenUML&lt;/li&gt;
&lt;li&gt;Sankey 🔥&lt;/li&gt;
&lt;li&gt;XY Chart 🔥&lt;/li&gt;
&lt;li&gt;Block Diagram 🔥&lt;/li&gt;
&lt;li&gt;Packet 🔥&lt;/li&gt;
&lt;li&gt;Kanban 🔥&lt;/li&gt;
&lt;li&gt;Architecture 🔥&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C4 Diagram is experimental, and diagrams with Fire Emojis are not supported on older versions.&lt;/p&gt;
&lt;h3 id="kanban-diagram"&gt;Kanban Diagram&lt;/h3&gt;
&lt;p&gt;I especially like the new Kanban diagram. As someone who does not want to use heavy tools for project management, this is a great way to keep track of tasks and progress. &lt;em&gt;&lt;strong&gt;My PO is not amused&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Creating even complex diagrams like Kanban is easy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
config:
  kanban:
    ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#'
---
kanban
  Todo
    [Create Documentation]
    docs[Create Blog about the new diagram]
  [In progress]
    id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.]
  id9[Ready for deploy]
    id8[Design grammar]&amp;#64;{ assigned: 'knsv' }
  id10[Ready for test]
    id4[Create parsing tests]&amp;#64;{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' }
    id66[last item]&amp;#64;{ priority: 'Very Low', assigned: 'knsv' }
  id11[Done]
    id5[define getData]
    id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]&amp;#64;{ ticket: MC-2036, priority: 'Very High'}
    id3[Update DB function]&amp;#64;{ ticket: MC-2037, assigned: knsv, priority: 'High' }

  id12[Can't reproduce]
    id3[Weird flickering in Firefox]

&lt;/code&gt;&lt;/pre&gt;
&lt;div class="mermaid"&gt;---
config:
  kanban:
    ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#'
---
kanban
  Todo
    [Create Documentation]
    docs[Create Blog about the new diagram]
  [In progress]
    id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.]
  id9[Ready for deploy]
    id8[Design grammar]&amp;#64;{ assigned: 'knsv' }
  id10[Ready for test]
    id4[Create parsing tests]&amp;#64;{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' }
    id66[last item]&amp;#64;{ priority: 'Very Low', assigned: 'knsv' }
  id11[Done]
    id5[define getData]
    id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]&amp;#64;{ ticket: MC-2036, priority: 'Very High'}
    id3[Update DB function]&amp;#64;{ ticket: MC-2037, assigned: knsv, priority: 'High' }

  id12[Can't reproduce]
    id3[Weird flickering in Firefox]
&lt;/div&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;I love&lt;/em&gt; the &lt;code&gt;Mermaid&lt;/code&gt;. It's one of my favorite tools for documentation. With growing tool support and LLMs that &amp;quot;speak &lt;strong&gt;&lt;a href="https://harrypotter.fandom.com/wiki/Mermish"&gt;Mermish&lt;/a&gt;&lt;/strong&gt;,&amp;quot; I expect it to be used even more!&lt;/p&gt;
&lt;p&gt;We developers often think code is the only way to present information. In many cases, graphical representation is not only more straightforward but more practical in representing ideas. Mermaid is an excellent tool for achieving this.&lt;/p&gt;
&lt;h2 id="references-and-links"&gt;References and Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mermaid.js.org/"&gt;Official Mermaid Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mermaid-js.github.io/mermaid-live-editor/"&gt;Mermaid Live Editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mermaid-js/mermaid"&gt;Mermaid on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Report Build Times</title>
			<link>/posts/report-build-times</link>
			<description>&lt;p&gt;Uneven increases in build times are one of the '&lt;em&gt;silent&lt;/em&gt;' indicators that could point the project may be heading in the wrong direction. As someone who likes to put &lt;code&gt;data in code&lt;/code&gt; and generate code with &lt;code&gt;T4 Text Templates&lt;/code&gt;, I have to be careful not to overextend. I treat these build times as &lt;em&gt;&lt;strong&gt;Canary in the Coal Mine&lt;/strong&gt;&lt;/em&gt;, an early warning system.&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/report-build-times</guid>
			<pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Uneven increases in build times are one of the '&lt;em&gt;silent&lt;/em&gt;' indicators that could point the project may be heading in the wrong direction. As someone who likes to put &lt;code&gt;data in code&lt;/code&gt; and generate code with &lt;code&gt;T4 Text Templates&lt;/code&gt;, I have to be careful not to overextend. I treat these build times as &lt;em&gt;&lt;strong&gt;Canary in the Coal Mine&lt;/strong&gt;&lt;/em&gt;, an early warning system.&lt;/p&gt;
&lt;p&gt;I like to use a small C# Script File -&lt;code&gt;csx&lt;/code&gt;, to measure the build times of the projects and to store the results in the markdown file in the root of the projects (location of the &lt;code&gt;sln&lt;/code&gt;). To force myself to run this &lt;em&gt;build speed test&lt;/em&gt;, I have created a &lt;code&gt;bat&lt;/code&gt; files that prepare the environment for development (starting services like db, docker, etc.) and run build speed tests. Having it as clean as possible in a desktop environment is an advantage.&lt;/p&gt;
&lt;p&gt;Having build times over time in the git is excellent because you can see how the project is evolving and how the build times are changing. This is &lt;strong&gt;not&lt;/strong&gt; a '&lt;em&gt;scientific&lt;/em&gt;' or '&lt;em&gt;precise&lt;/em&gt;' measurement, but it can give you a good idea of how the project evolves. Sure, if we store build times in the pipeline, but this is simpler, and you can even compare different developer computers.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DateTime&lt;/th&gt;
&lt;th&gt;Computer&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Build Time&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-28 09:32:14&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;221.87&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-28 16:45:24&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;236.97&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 08:07:31&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;246.35&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 10:03:38&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;36.61&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;Removed T4 Templating to separate projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 10:06:39&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;18.69&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 11:28:16&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;60.43&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 11:30:10&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;21.48&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 11:32:02&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;29.32&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 12:41:07&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;20.32&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 12:41:39&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;19.38&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 12:42:15&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;16.45&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 15:33:54&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;17.14&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 15:34:31&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;17.23&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 15:57:31&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;17.55&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-12-29 15:58:02&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;17.40&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-02-09 10:31:02&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;20.24&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-02-13 08:16:18&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;19.25&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-02-14 16:05:23&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;21.33&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-02-14 16:05:58&lt;/td&gt;
&lt;td&gt;ELSA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shockley.sln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;19.69&lt;/td&gt;
&lt;td&gt;✔️👊😺🐯✈️💥🚀&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Build times over time for the &lt;code&gt;Shockley.sln&lt;/code&gt; solution on the &lt;code&gt;ELSA&lt;/code&gt; computer.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I did some optimization for Shockley, removed some parts of the code that generated cs files from &lt;code&gt;csv&lt;/code&gt; to separate projects, and built speed up considerably. Also, after 45 days since the end of the year, you can see that the project is a little slower. Unfortunately, I was already deep into the project when I added this script, so I don't have the initial build times, but I'm guessing they were much faster.&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;It's always beneficial to track software metrics as early as possible. Metrics do not have to be precise; they should be just good enough to provide insights. Having data for local build times is one easy metric we can implement quickly.&lt;/p&gt;
&lt;h2 id="section"&gt;👊😺🐯✈️💥🚀&lt;/h2&gt;
&lt;p&gt;And for those wondering, 👊😺🐯✈️💥🚀 is an &lt;em&gt;obvious&lt;/em&gt; reference for Razor and T-Bone from &lt;em&gt;&lt;strong&gt;SWAT Kats&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/GI8ZDzMCjMI?si=F4R8ewvT7tZjn9Dk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Library: Spectre.Console</title>
			<link>/posts/library-spectre-console</link>
			<description>&lt;p&gt;I've always been fascinated with the &lt;strong&gt;Amiga demo community&lt;/strong&gt; and the &lt;strong&gt;Warez scene&lt;/strong&gt;. I've especially loved &lt;code&gt;cracktros&lt;/code&gt;, small graphical demos that were played before a cracked game. Names like &lt;em&gt;Razor 1911&lt;/em&gt;, &lt;em&gt;Skid Row&lt;/em&gt;, &lt;em&gt;Paradox&lt;/em&gt;, &lt;em&gt;Crystal&lt;/em&gt;, and &lt;em&gt;Red Sector&lt;/em&gt; would pop up on the screen, often with better graphics and music than in-game.&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/library-spectre-console</guid>
			<pubDate>Mon, 17 Feb 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;I've always been fascinated with the &lt;strong&gt;Amiga demo community&lt;/strong&gt; and the &lt;strong&gt;Warez scene&lt;/strong&gt;. I've especially loved &lt;code&gt;cracktros&lt;/code&gt;, small graphical demos that were played before a cracked game. Names like &lt;em&gt;Razor 1911&lt;/em&gt;, &lt;em&gt;Skid Row&lt;/em&gt;, &lt;em&gt;Paradox&lt;/em&gt;, &lt;em&gt;Crystal&lt;/em&gt;, and &lt;em&gt;Red Sector&lt;/em&gt; would pop up on the screen, often with better graphics and music than in-game.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo       _---~~(~~-_.
echo     _{        )   )
echo   ,   ) -~~- ( ,-' )_
echo  (  \`-,_..\'., )-- '_,)
echo ( \` _)  (  -~( -_ \`,  }
echo (_-  _  ~_-~~~~\`,  ,' )
echo   \`~ -^(    __;-,((())))
echo         ~~~~ {_ -_(())
echo                \`\\  }
echo                  { }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, I'm not doing anything so cool, like creating &lt;code&gt;cracktros&lt;/code&gt; in the &lt;em&gt;&lt;strong&gt;enterprise software arena&lt;/strong&gt;&lt;/em&gt;. Still, at least I can put some &lt;em&gt;magic&lt;/em&gt; in the console applications with &lt;code&gt;ASCII Splash Screens&lt;/code&gt;. The tool that helps me reach these &lt;em&gt;lofty goals&lt;/em&gt; of putting a tiny bit of personality in bland corporate applications is &lt;code&gt;Spectre.Console&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;&lt;img src="/media/content/spectre-console-example.png" alt="spectre-console-example.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Spectre.Console&lt;/code&gt; is a library that helps us create beautiful console applications in .NET.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dotnet add package Spectre.Console
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It has many features, like tables, progress bars, JSON visualizer, selection, multi-selection, tree view, etc. Even if you don't have experience with console graphics, it's simple to use and much more straightforward than GUI development.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;AnsiConsole.Status()
        .Spinner(Spinner.Known.Star)
        .SpinnerStyle(Style.Parse("blue"))
        .Start("Translating with OpenAI...", ctx =&amp;gt;
        {
            var translationResults = translationService.Translate(
                englishValue,
                context,
                LanguageData.List.Where(x =&amp;gt; x != LanguageData.EnglishUK).ToList());

            foreach (var translation in translationResults.Where(x =&amp;gt; x.Key != LanguageData.SerbianLatinAlphabet))
            {
                machineTranslations.Add(translation.Key.ShortName, translation.Value);
                ctx.Status($"Translated to {translation.Key.Name}");
            }

            // Display translations in a table
            var table = new Table()
                .AddColumn("Language")
                .AddColumn("Translation")
                .Border(TableBorder.Rounded);

            foreach (var translation in machineTranslations)
            {
                table.AddRow(
                    $"[blue]{LanguageData.List.Single(x =&amp;gt; x.ShortName == translation.Key).Name}[/]",
                    translation.Value);
            }

            AnsiConsole.Write(table);
        });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Simple spinner and the table&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;I love &lt;code&gt;command-line interfaces (CLI)&lt;/code&gt;. They provide a simple but effective way to input and output information. Sure, they are limited compared to GUI, but with creative ways of using CLI, we can often create simple but usable visualizations, even in console apps, with little effort.&lt;/p&gt;
&lt;p&gt;I use &lt;code&gt;Spectre.Console&lt;/code&gt; for every console application. It's a great way to make it more user-friendly and visually appealing. Spectre.Console's widgets are a great way to represent the data meaningfully in the CLI. The best thing is that you do not have to do that much. You can create your console app and then just '&lt;em&gt;order&lt;/em&gt;' &lt;code&gt;ChatGPT&lt;/code&gt; or &lt;code&gt;Claude.Sonnet&lt;/code&gt; to make it more visually appealing or to visualize some data with &lt;code&gt;Spectre.Console&lt;/code&gt;! It works great!&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spectreconsole.net/"&gt;Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spectreconsole/spectre.console"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;video controls=""&gt;
  &lt;source src="/media/content/spectre-console-table.webm" type="video/webm"&gt;
  Your browser does not support the video tag.
&lt;/video&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>New Generation of IDEs (part 2): Multidimensional Navigation in Photo Libraries</title>
			<link>/posts/new-generation-of-ides-part-2</link>
			<description>&lt;p&gt;In &lt;a href="https://vukvuk.com/posts/new-generation-of-ides-part-1"&gt;part 1&lt;/a&gt;, I wrote about Visual Studio navigation. In part 2, I will compare IDEs with modern Photo Libraries&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/new-generation-of-ides-part-2</guid>
			<pubDate>Sun, 16 Feb 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;h1 id="new-generation-of-ides-part-2-multidimensional-navigation-in-photo-libraries"&gt;New Generation of IDEs (part 2): Multidimensional Navigation in Photo Libraries&lt;/h1&gt;
&lt;p&gt;In &lt;a href="https://vukvuk.com/posts/new-generation-of-ides-part-1"&gt;part 1&lt;/a&gt;, I wrote about Visual Studio navigation. In part 2, I will compare IDEs with modern Photo Libraries&lt;/p&gt;
&lt;p&gt;I had a digital camera (now just a camera) since the top models were just a few megapixels. I quickly concluded that I could not just copy pictures to the folder with a description of the photo session. I needed something sortable, so I added '&lt;a href="https://vukvuk.com/posts/finding-the-perfect-date"&gt;perfect date&lt;/a&gt;' as the prefix in the folder name.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;YYYY&amp;gt;
  &amp;lt;YYYY-MM&amp;gt;
    &amp;lt;YYYY-MM-DD&amp;gt; - &amp;lt;Device&amp;gt; - &amp;lt;Description&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This structure works, even after ~20-25 years.&lt;/p&gt;
&lt;p&gt;The problem is, &lt;strong&gt;this is not enough&lt;/strong&gt;. Sure, I can somewhat find the pictures I want, but I cannot easily find all the photos we as a family took from vacations with all devices.&lt;/p&gt;
&lt;p&gt;If we want to add &lt;strong&gt;additional dimensions&lt;/strong&gt; for organizing photos, we can use &lt;em&gt;Photo Organizers&lt;/em&gt;. With photo organizers, we use for example timestamps to extract just the images from specific periods, even if it is from different folders (dates and devices).&lt;/p&gt;
&lt;p&gt;The next problem the Photo Organizers can help with is when we want to show all the pictures from some specific place. Geotagging and map views work great for that.&lt;/p&gt;
&lt;p&gt;Then, sometimes, I want the pictures of my children. AI and face recognition work excellently for that.&lt;/p&gt;
&lt;p&gt;Organizing a group of pictures by folder with date is a good first step. But adding additional &amp;quot;&lt;em&gt;&lt;strong&gt;dimensions&lt;/strong&gt;&lt;/em&gt;&amp;quot; for navigation, with Photo Organizers, greatly enhances the usability of digital Photo Libraries&lt;/p&gt;
&lt;h2 id="multidimensional-code-searching"&gt;Multidimensional Code Searching&lt;/h2&gt;
&lt;p&gt;There are many possibilities with &amp;quot;&lt;em&gt;multidimensional navigation&lt;/em&gt;&amp;quot; of the code. For example, when I want to create a new database query, as a true &lt;code&gt;pro&lt;/code&gt;, I find it easier to copy and modify the existing code than to write everything from scratch. Wouldn't it be great to have the capability to &lt;code&gt;browse classes&lt;/code&gt; by some characteristics like &lt;code&gt;have sql query&lt;/code&gt;, &lt;code&gt;is MediatR Handler&lt;/code&gt;, etc?&lt;/p&gt;
&lt;p&gt;Sure, you can argue that you can already do many things with clever searches. But I believe we can do better in the context of searches.&lt;/p&gt;
&lt;p&gt;I'm solving most of these problems with strict naming and class rules. But, for legacy applications or on projects where I'm not the one '&lt;em&gt;making rules&lt;/em&gt;,' I find it much more challenging because, in most cases, &lt;em&gt;&lt;strong&gt;I'm not looking for what I need; I'm searching for the specific implementation of what I wanted&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Does &lt;code&gt;code discovery&lt;/code&gt; in legacy applications (or the ones you have not worked on) have to be so complex? Can we search and browse classes by some better criteria than by &lt;em&gt;projects, folders and files&lt;/em&gt;?&lt;/p&gt;
&lt;h2 id="code-browsing"&gt;Code Browsing?&lt;/h2&gt;
&lt;p&gt;Just by searching for something, you may find all the instances, but you still don't have the ability to easily browse those instances. Yes, modern IDEs give us a list of results and code previews. But can we do more? How can we make it easier to switch from one class to another?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Are tabs in multiple windows the best we can do?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="computation-cost-of-the-indexing"&gt;Computation Cost of the Indexing&lt;/h2&gt;
&lt;p&gt;Modern IDEs can index many of these things already. Yeah, I'm angry because IDEs are getting slower and slower, and this indexing is one reason. But I'm sure we can create smarter local indexing than we have now. Just think about it: we use these IDEs to develop applications with petabytes of data and billions of rows! And, for some reason, we cannot make local IDEs work fast on computers with 16-24 cores and 32 threads with 32-64GB RAM. That's usually much more than a single node on the server! Also, modern CPUs and GPUs have specialized chipsets that are great for AI-assisted tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Indexing &lt;em&gt;should&lt;/em&gt; not be a problem with modern computers!&lt;/strong&gt; We could even run a decent-sized LLM locally and analyze every class whenever there is a change for better indexing.&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;With modern computers we should be capable of actions like opening IDEs instantaneously. Multidimensional indexing should not only be much faster, but we should be able to index many more things. With our high-end PCs, we could run more advanced code search engines and even preemptively run local LLMs for code analysis and tagging in the background. And maybe we could put more effort into code visualization and representation.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Why are we not doing that?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>LLM Incantations and Spellbooks</title>
			<link>/posts/llm-incantantions-and-spellbooks</link>
			<description>&lt;p&gt;We live in interesting times where software development is going through a paradigm shift - &lt;strong&gt;&lt;em&gt;Human&lt;/em&gt; developers are no longer the only ones writing code&lt;/strong&gt;!&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/llm-incantantions-and-spellbooks</guid>
			<pubDate>Wed, 05 Feb 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;We live in interesting times where software development is going through a paradigm shift - &lt;strong&gt;&lt;em&gt;Human&lt;/em&gt; developers are no longer the only ones writing code&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;To summon a code from the &lt;code&gt;magical Nvidia box&lt;/code&gt; in the &lt;code&gt;astral cloud&lt;/code&gt;, we need proper incantations:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Write me a person class.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I pray to &lt;code&gt;Claude/Sonnet&lt;/code&gt;, I get a &lt;code&gt;Python&lt;/code&gt; class:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;class Person:
    def __init__(self, first_name, last_name, age, email=None):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.email = email
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a much better incantation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Write me a person class with Name, Surname, DateOfBirth. Add methods to change Surname.

Use C#. Use Factory Method. Methods should return new object if successful Result with CSharpFunctionalExtensions.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and this returns:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using System;
using CSharpFunctionalExtensions;

namespace Domain
{
    public class Person
    {
        public string Name { get; }
        public string Surname { get; }
        public DateTime DateOfBirth { get; }

        private Person(string name, string surname, DateTime dateOfBirth)
        {
            Name = name;
            Surname = surname;
            DateOfBirth = dateOfBirth;
        }

        public static Result&amp;lt;Person&amp;gt; Create(string name, string surname, DateTime dateOfBirth)
        {
            if (string.IsNullOrWhiteSpace(name))
                return Result.Failure&amp;lt;Person&amp;gt;(&amp;quot;Name cannot be empty&amp;quot;);

            if (string.IsNullOrWhiteSpace(surname))
                return Result.Failure&amp;lt;Person&amp;gt;(&amp;quot;Surname cannot be empty&amp;quot;);

            if (dateOfBirth &amp;gt; DateTime.UtcNow)
                return Result.Failure&amp;lt;Person&amp;gt;(&amp;quot;Date of birth cannot be in the future&amp;quot;);

            if (dateOfBirth.Year &amp;lt; 1900)
                return Result.Failure&amp;lt;Person&amp;gt;(&amp;quot;Date of birth cannot be before 1900&amp;quot;);

            return Result.Success(new Person(name, surname, dateOfBirth));
        }

        public Result&amp;lt;Person&amp;gt; ChangeSurname(string newSurname)
        {
            if (string.IsNullOrWhiteSpace(newSurname))
                return Result.Failure&amp;lt;Person&amp;gt;(&amp;quot;New surname cannot be empty&amp;quot;);

            if (newSurname == Surname)
                return Result.Failure&amp;lt;Person&amp;gt;(&amp;quot;New surname must be different from current surname&amp;quot;);

            return Result.Success(new Person(Name, newSurname, DateOfBirth));
        }

        public override string ToString()
        {
            return $&amp;quot;{Name} {Surname} (Born: {DateOfBirth:d})&amp;quot;;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After many tweaks, I have a decent spell:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Write me a person class with Name, Surname, DateOfBirth. Add methods to change Surname.


Use C#. Use file block-scoped namespaces. Use Factory Method. Methods should return new object if successful Result with CSharpFunctionalExtensions. For every method as input use class with FluentValidation, not primitive types. 
Method input class should be subclass of the main class. Everything should be sealed. Do not use inheritance for requests, classes should be standalone.
Do not add comments in code.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's not perfect. I could argue about validation, but it's a decent start.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;
namespace Domain;

using FluentValidation;
using CSharpFunctionalExtensions;

public sealed class Person
{
    public string Name { get; private set; }
    public string Surname { get; private set; }
    public DateTime DateOfBirth { get; private set; }

    private Person(string name, string surname, DateTime dateOfBirth)
    {
        Name = name;
        Surname = surname;
        DateOfBirth = dateOfBirth;
    }

    public sealed class CreatePersonRequest
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public DateTime DateOfBirth { get; set; }
    }

    public sealed class CreatePersonRequestValidator : AbstractValidator&amp;lt;CreatePersonRequest&amp;gt;
    {
        public CreatePersonRequestValidator()
        {
            RuleFor(x =&amp;gt; x.Name).NotEmpty().MaximumLength(100);
            RuleFor(x =&amp;gt; x.Surname).NotEmpty().MaximumLength(100);
            RuleFor(x =&amp;gt; x.DateOfBirth).NotEmpty().LessThan(DateTime.Now);
        }
    }

    public static Result&amp;lt;Person&amp;gt; Create(CreatePersonRequest request)
    {
        var validator = new CreatePersonRequestValidator();
        var validationResult = validator.Validate(request);

        if (!validationResult.IsValid)
            return Result.Failure&amp;lt;Person&amp;gt;(string.Join(&amp;quot;, &amp;quot;, validationResult.Errors));

        return Result.Success(new Person(request.Name, request.Surname, request.DateOfBirth));
    }

    public sealed class ChangeSurnameRequest
    {
        public string NewSurname { get; set; }
    }

    public sealed class ChangeSurnameRequestValidator : AbstractValidator&amp;lt;ChangeSurnameRequest&amp;gt;
    {
        public ChangeSurnameRequestValidator()
        {
            RuleFor(x =&amp;gt; x.NewSurname).NotEmpty().MaximumLength(100);
        }
    }

    public Result&amp;lt;Person&amp;gt; ChangeSurname(ChangeSurnameRequest request)
    {
        var validator = new ChangeSurnameRequestValidator();
        var validationResult = validator.Validate(request);

        if (!validationResult.IsValid)
            return Result.Failure&amp;lt;Person&amp;gt;(string.Join(&amp;quot;, &amp;quot;, validationResult.Errors));

        return Result.Success(new Person(Name, request.NewSurname, DateOfBirth));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can work on the code now. I will refactor the parts I don't like and make them more standardized with the projects I work on. I will even ask LLM to write unit tests, etc.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;But what happens with the spell?&lt;/strong&gt;&lt;/em&gt; Is it forever lost? Was the purpose just a one-time chant?&lt;/p&gt;
&lt;p&gt;Storing base spells and reusing them when necessary would be much easier than writing them from scratch, and it would produce more consistent results. But, if we decide to store the &lt;code&gt;base spells,&lt;/code&gt; where should we store them, and how should we organize them?&lt;/p&gt;
&lt;p&gt;The problem is even worse when you need to instruct multiple LLMs simultaneously. Good instructions for one are not necessarily good instructions for the other. For example, I'm &lt;em&gt;&lt;strong&gt;deeply offended&lt;/strong&gt;&lt;/em&gt; when &lt;code&gt;Claude/Sonnet&lt;/code&gt; for my &lt;code&gt;c#  Blazor&lt;/code&gt; transforms it to &lt;code&gt;React&lt;/code&gt;. I must be specific in my prompt to keep it in the &lt;code&gt;c#&lt;/code&gt; realm. For &lt;code&gt;ChatGPT&lt;/code&gt;, I never have to do that (even if I clean the stored data). This problem is even more pronounced with picture generators.&lt;/p&gt;
&lt;h2 id="the-solutions"&gt;The Solutions?&lt;/h2&gt;
&lt;p&gt;Storing and sharing prompts is an as old idea as LLMs themself. There are browser plugins, even websites like &lt;a href="https://prompthero.com/"&gt;prompthere.com&lt;/a&gt; or &lt;a href="https://promptbase.com/"&gt;promptbase.com&lt;/a&gt;, where users share or sell prompts.&lt;/p&gt;
&lt;p&gt;I don't find these approaches practical for development. I don't want to share them, at least not outside the team. Also, most prompts are project-specific.&lt;/p&gt;
&lt;p&gt;My requirements are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Markdown&lt;/code&gt; Format&lt;/strong&gt; - It must support &lt;code&gt;markdown&lt;/code&gt;, de-factor LLM, and human-readable format.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;History&lt;/strong&gt; - It would be nice to have an option to see the changes, but the focus should be on the current version.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Searchable&lt;/strong&gt; - It needs to have at least some search capabilities. Tagging and other similar capabilities are not necessary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt; - It probably should not be public, but it is not some secret.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Basic solution:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Put everything into a single markdown file in &lt;code&gt;git&lt;/code&gt; and publish it on the website with a &lt;code&gt;static website generator.&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Maybe one day, I will &lt;em&gt;instruct AI to write an AI prompt&lt;/em&gt; for me inside of the IDE, but in the meantime, we have to find something less elegant but still practical.&lt;/p&gt;
&lt;p&gt;My current solution is a plain spellbook in the &lt;code&gt;markdown&lt;/code&gt; format stored on &lt;code&gt;git&lt;/code&gt; and published as a &lt;code&gt;static website&lt;/code&gt;. I have something similar with &lt;a href="https://vukvuk.com/posts/chocolatey"&gt;Chocolatey&lt;/a&gt;, an essential list of
&lt;code&gt;commands&lt;/code&gt; I use when installing something.&lt;/p&gt;
&lt;p&gt;Practical solutions can be simple but still functional.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>New Generation of IDEs (part 1): The problem of File-Centric IDEs</title>
			<link>/posts/new-generation-of-ides-part-1</link>
			<description>&lt;p&gt;I've been thinking a lot about how we write code in IDEs and organize files and classes (and types) in &lt;code&gt;c#&lt;/code&gt; and other modern software languages and projects.&lt;/p&gt;</description>
			<guid isPermaLink="false">/posts/new-generation-of-ides-part-1</guid>
			<pubDate>Tue, 28 Jan 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;I've been thinking a lot about how we write code in IDEs and organize files and classes (and types) in &lt;code&gt;c#&lt;/code&gt; and other modern software languages and projects.&lt;/p&gt;
&lt;p&gt;With this series of texts, I want to explore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Code Organization&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Why do folders and files still have such importance in modern code solutions?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code Editing and Visualization&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Is it possible to browse classes by how often they are edited together?&lt;/li&gt;
&lt;li&gt;Is it possible to visualize and even edit multiple related classes together?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;How can we create an LLM-friendly IDE to easily see the code suggestions/changes without using &lt;code&gt;git diff&lt;/code&gt;?
&lt;ul&gt;
&lt;li&gt;How can we have more than one suggestion at the same time?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Is it possible to detect similar classes and instruct LLMs to use some classes as inspiration when answering questions?&lt;/li&gt;
&lt;li&gt;How do we store LLM instructions as part of the solution?&lt;/li&gt;
&lt;li&gt;How can the generation of LLM tests be made easier?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Can we find a solution in other fields?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="solution-and-project-organization-in.net"&gt;Solution and Project Organization in .NET&lt;/h2&gt;
&lt;p&gt;I think we have two types of project organization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Language organization&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File organization&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From a &lt;strong&gt;language perspective&lt;/strong&gt;, we have namespaces and classes. We can argue that we have projects (assemblies), too, but is a project more of a file type of organization or a language one?&lt;/p&gt;
&lt;p&gt;From a &lt;strong&gt;file perspective&lt;/strong&gt;, we have textual files that can have a single or more classes. We have folders and Projects - specialized file types that somehow group multiple files into one unit. That grouping can be done by folder, like in .NET Core, or by direct file reference, like in .NET (classic).&lt;/p&gt;
&lt;p&gt;Of course, c# is multipurpose language, and it does not have to follow this organizational pattern strictly, but in most cases, it does. Most of us work on enterprise projects, where c# has the most significant strengths.&lt;/p&gt;
&lt;h2 id="the-problem-file-centric-ides"&gt;The Problem? - File Centric IDEs&lt;/h2&gt;
&lt;p&gt;Modern IDEs and Text Editors &lt;strong&gt;can only work with files!&lt;/strong&gt;  &lt;em&gt;Why do we not use a different concept to open classes and not worry about how it is stored on our data system?&lt;/em&gt; &lt;strong&gt;Why are we so focused on the concept of files?&lt;/strong&gt; Compilers work with classes, not files.&lt;/p&gt;
&lt;p&gt;Sure, it made sense decades ago. Just take any text editor and do the work. But, most of us today use powerful IDEs and enhanced Text Editors that use &lt;em&gt;more computing power for LLMs per single call than all the computers in the world in the 60s&lt;/em&gt;. Why not use that power differently?&lt;/p&gt;
&lt;h2 id="project-and-file-navigation-in-modern-ides"&gt;Project and File Navigation in Modern IDEs&lt;/h2&gt;
&lt;p&gt;The two most popular ways of navigating the files in software projects are the equivalent of the &lt;code&gt;explorer&lt;/code&gt;, where we have a list of projects with a list of folders and files recursively. The second is some type of &lt;code&gt;search&lt;/code&gt;, where we type parts of file name, class name, etc., and &lt;em&gt;jump to&lt;/em&gt; specific parts of some file.&lt;/p&gt;
&lt;p&gt;There are more practical ways, such as a &lt;code&gt;'go to'&lt;/code&gt; declaration, usage, etc. I frequently use source control's &lt;code&gt;'list of changed files'&lt;/code&gt; to jump to edited files. There are ways we can bookmark lines that some may find helpful.&lt;/p&gt;
&lt;p&gt;There are options like '&lt;code&gt;peek definition&lt;/code&gt;' in Visual Studio and '&lt;code&gt;quick definition&lt;/code&gt;' in Rider that show small popup windows with details from other classes. It even gives you the option to edit the class. But it still switches your focus from one class to another, but in a popup window. You don't see the relationship.&lt;/p&gt;
&lt;p&gt;Perhaps I'm missing something, but is the pinnacle of the environment for code writing tabs and windows?&lt;/p&gt;
&lt;p&gt;The problem remains: &lt;em&gt;&lt;strong&gt;There is no simple way to open, browse, and show related classes we often edit together.&lt;/strong&gt;&lt;/em&gt; It is always a process to find and open related files.&lt;/p&gt;
&lt;h2 id="relationship-based-navigation"&gt;Relationship-based navigation?&lt;/h2&gt;
&lt;p&gt;&lt;img src="/media/memes/its-all-connected.jpg" alt="it's all connected"&gt;&lt;/p&gt;
&lt;p&gt;I am a "&lt;em&gt;&lt;strong&gt;visual thinker&lt;/strong&gt;&lt;/em&gt;". I need &lt;em&gt;Trello&lt;/em&gt; and &lt;em&gt;Post-It notes&lt;/em&gt;; I need some way to &lt;strong&gt;visualize entities and relationships&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I like working with C# in Visual Studio and Rider, but I have to "&lt;em&gt;&lt;strong&gt;imagine the visual part&lt;/strong&gt;&lt;/em&gt;." I feel there is an opportunity to create some "Class Navigator" where you can visually explore and write code through class relationships, not by file organization.&lt;/p&gt;
&lt;p&gt;I still don't know what that would look like, but I believe there is a better way to write code than opening a file and then another file and organizing windows.&lt;/p&gt;
&lt;p&gt;I will try to explore this topic by creating a plugin for Rider, with limited capabilities, around a single idea of alternative-related class visualizations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Part 2&lt;/strong&gt; will explore navigation in modern photo libraries and how it enables "&lt;em&gt;multidimensional&lt;/em&gt;" navigation.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
	</channel>
</rss>