Tuesday, April 22, 2025

Three types of technical debt

Let's compare technical debt and real-world financial debt. (Aren't I "fun"?)


TL;DR:  Many developers are creating future problems for themselves because they're not thinking about the consequences of their actions. For some languages and technologies, the examples aren't great and don't teach everything that's needed to avoid future problems.

credit card, shark, question mark

I'm going to consider three broad categories.


1. Credit card - make a deliberate choice to put something off (paying for an item/service) knowing that will have to come back to it (& pay) later, possibly with some interest.

Having a little of this is normally not a problem.


2. Loan shark - No other options when you have to do something now. It's solving a short-term problem but likely creating a bigger and more expensive one for the future.

Avoid as far as possible. (If you're in real financial debt, please talk to someone--don't struggle alone.)


3. Financially illiterate - No one ever taught you about money or saving. You spend money without thought, have multiple store and credit cards, have little or no savings, and live "pay-check to pay-check".

"This must be fine", or so you think. "Doesn't everyone live like this?" "I'm sure it will all work out in the end--somehow."


Hopefully, the comparisons are clear.

What the term "illiterate" might hide is that this isn't always the developer's fault. 

Some languages/technologies/frameworks are taught in only the most basic ways. The student is shown how to get started or do the basics, but the instructions never go beyond that.
Even examples and so-called "best practices" don't help. "Expert" code looks like beginner code, and not in a good way.

Everyone makes the same mistakes, builds up a code base with the same issues, and talks about the code/language/framework with the same complaints.
Eventually, the problem becomes clear, and someone suggests a rewrite with a different technology.

It sounds like declaring financial bankruptcy, and that future financial problems will be avoided by only trading in cash/gold/crypto. 

Is it the technology/medium that's the problem? Or is it how it's used?


If you're learning, are you learning how to do something without creating future problems for yourself?

If you're teaching, are you setting up your students for long-term success?

If you're creating a language/technology/framework, are you doing so in a way that encourages doing things in ways that avoid long-term problems?



Friday, April 11, 2025

Choosing a framework to build a native app on Windows with .NET

"Thinking face" surrounded by logos of different frameworks

By "Native," I mean not using web technologies, either as a remotely hosted website or bundled into a package that is installed and runs locally.

Let's get this out of the way first. 

There is no "best". 

There is only best for your current circumstances and requirements at the current time.


That said, how do you decide what might be best for your circumstance?

Each option has slightly different capabilities. It's essential that you know what capabilities you'll need before you start building. If you don't know what you need to build, it's impossible to say which is best for you. This may make the decision for you. 
Doing the work in advance to fully understand the requirements can save a lot of frustration and wasted time and effort if you discover you need something the framework can't do. 

So, second disclaimer out of the way, how do you decide which .NET framework to evaluate first? 

If the first thing you evaluate does all you need, it's probably fine to go with that. If not, try another.


Q1. Do you think you might ever need to run on more than just Windows?

If Yes - go to Q2.

If No, go to Q4.


Q2. Do you want the software to also run on the web?

If Yes - Evaluate UNO Platform first.

If No - go to Q3.


Q3. Are you primarily interested in building an app that runs on desktop, or on mobile devices?

If Desktop - Evaluate Avalonia first.

If Mobile - Evaluate .NET MAUI first.


Q4. Do you already have any existing apps built with WinForms, WPF, UWP, or WinUI?

If Yes - continue using that. (Unless it's UWP, then go to Q7.)

If No - go to Q5


Q5. Are you building a "traditional line-of-business" app or something that could be described as "forms over data"?

If Yes - go to Q6.

If No - go to Q7.


Q6. Are you happy to use/learn XAML?

If Yes - evaluate WPF first.

In No - evaluate WinForms first.


Q7. Do you need to support XBox devices, game pad controls, or ink-based input?

If Yes - evaluate UWP first.

If No - evaluate WinUI first.



Happy evaluating!



Yes:

  • The above is probably an oversimplification.
  • There may be many situations where you don't go with the first suggested option above.
  • It doesn't consider personal preference. But if you already have a preference, you should probably use that. (Assuming it can do all you need).
  • I haven't considered support for different programming languages. (But you're almost certainly using C#).


This isn't intended to be a definitive guide. Without knowing your exact requirements, existing skills, and preferences, I can't say for sure. 

I just wanted to share some simple questions that others have found helpful when overwhelmed by the options available.


Thursday, April 10, 2025

What does it mean to "design" an app?

emoji of paint

We use the word "design" for lots of different things and in lots of different ways. 

Not all meanings are the same. I think we'd benefit from clarifying what we mean when we say the word, or we could use different (or additional) words.


The dictionary has lots of meanings for "design".
I like the broad definition of "to intentionally create something to meet pre-defined requirements."

I think of design as more than how something looks; it also includes how it works, behaves, and is used.


A "good" designer can create something that looks nice.

A "great" designer can create something effective, effortless, pleasurable, and desirable to use.
This typically involves more than just how it looks and is intrinsic to a thing, not something that's added at the end.


When "designing an app", what are the different people involved doing?

During the "design phase," what are different people doing?

If doing anything relating to the UI, does that count as "design"?

Is design something that's done to a piece of software once it technically works so that it looks nicer?

When is "design time" from a coder's perspective? Is it all the time up until they start running (or debugging) the code?

Is design something we work as a team to do, to produce the [best] final product?


Is design only about colors and shapes? Or does it require a lot more thought and iteration to do well?



When we talk about "designers" referring to people and roles, we frequently add extra words to make it clear what type of designer we mean, or the type of designing they're doing.

When we talk about "designers" referring to tools, there often isn't the effort to clarify. Should there be? Is what you mean by a designer the same as what I mean by a designer? Depending on context or circumstance, probably not.



What do we mean by "design", and what do we expect from a "designer"?
 - I'm sure I'll keep coming back to these questions.


Wednesday, April 09, 2025

I am not a futurist

Crystal ball
10+ years ago, I thought the future of app complexity could be solved by having text- and therefore also speech-based interfaces. I built a concept of an app that used this as a way of interacting with multiple social networks and news sites.

5 years ago, I thought the vast array of partially overlapping functionality in the multitude of apps could be addressed by having apps that offered extensibility points to allow different apps to provide specific functionality that was all consumed from one (or a few) place(s).


I couldn't persuade people that a text-based interface as the primary way of interacting with complex apps was the future. Nor were they convinced that my prototype could scale to more complex and varied input.

Rather than offer extensibility, app developers wanted to "own it all" and do everything themselves. The dream of controlling (& therefore monetising) everything was too appealing.


Now we're told that "Agentic AI is the future." Apps will all become agents, and we'll do everything through a chat interface.

Obviously, I see the parallels with my examples above.
But I don't see that as the one scenario to rule them all.

The use of "Agents" in AI/LLMs (probably connected via MCP) definitely has a place in the future and addresses some of the scenarios that AI (and even possible future General AI) could never do.

However, just because something new comes along doesn't mean everything that went before becomes obsolete and redundant.
Some of the things that can now be done with agentic AI may provide a better alternative than what was available previously. Some things will still be better, easier, or even faster with a dedicated solution and a non-text-based interface.

It's great to be excited by the new and the shiny, but that doesn't always mean the replacement of everything that went before.


---

I wonder what other abandoned side-projects I have that will turn out to be relevant in the future?

I hope that not all of my predictions about what's coming soon are as far out as the two I mentioned above.


Wednesday, April 02, 2025

Never create an IValueConverter again?

tl;dr: If using WPF or MAUI you can easily create a MarkupExtension to allow the use of static methods with a Binding, instead of using an IValueConverter. If using UWP or WinUI, you can do this with `x:Bind`.


An `IValue Converter` is a way to modify a value that is provided via a binding. I rarely use them, but others use them frequently and find the experience to be less than ideal.

I stopped using them a long time ago (while still using Silverlight) because they had a significant and noticeable performance impact. The performance impact today isn't as noticeable, but I haven't gone back.

By not using them, I also realised that they make the code easier to test. The logic I was performing in the converter (the way the docs and conventions of the time encouraged) actually made more sense in the ViewModel. When the logic is all in the ViewModel I only have to test that. I don't need to have tests that run the app and manipulate the UI to verify the internal logic.  (Yes, I test my MVVM code, and still have separate tests for UI verification--but not the internal application logic.)

That's me, but there are people who do create ValueConverters and don't feel happy about it.

There are two complaints that I hear frequently:

  1. The syntax is verbose.
  2. The need to create and initialize extra types seems unnecessary when all that is really needed is a static function.
This isn't such an issue for anyone using WinUI as that includes the `x:Bind` MarkupExtension which already supports the ability to call static functions as part of a binding.

So, if you had this class:

public static class Converter
{
    public static string MakeUpperCase(string input)
    {
        return input.ToUpper();
    }
    
    // .. other functions
}


You could apply the function as part of the binding like this:

<TextBlock
    Text="{x:Bind utils:Converter.MakeUpperCase(VM.Message)}" />


Yes, I'm using a trivial example throughout so as not to get caught up in the complexities of what the conversion function does and can instead focus on how it is used.


That's all well and good for WinUI code, but what if you're using .NET MAUI or WPF, where there is no x:Bind support?


Well, if what you want is the ability to provide a static function as part of a binding, why not create a MarkupExtension that does that? The X in XAML serves as a reminder of using something intended to be eXtended. Plus, this is software. If using a framework that doesn't provide the desired functionality, unless there's something preventing it, you can add it yourself.


So, why not create a MarkupExtension that behaves like a binding but also accepts a static function and automatically applies that to the value that is used?
It seems so obvious that I can't imagine why everyone isn't already doing this.

In this example, I've called by MarkupExtension "BossBinding" and it can be used like this:

<Label Text="{rx:BossBinding Message,
             Converter={x:Static utils:Converter.MakeUpperCase}}"
     />

This is from a .NET MAUI app. 
In all other ways, the BossBinding works and can be used like a regular Binding, but the Converter property takes a static function. 

To make it as flexible as a ValueConverter, it requires a slightly more complex signature, but the contents of the function is what would go in the `Convert` function of a class that implements `IValueConverter` so it doesn't feel that complicated.

public static class Converter
{
    public static Func<object, object> MakeUpperCase => (input) =>
    {
        if (input is string str)
        {
            return str.ToUpper();
        }
        return input;
    };

    // other functions
}


If wanting to be stricter about the types used in the conversion, a version of the static function and the property of the MarkupExtension could use types other than `object`.

To make this work for both .NET MAUI and WPF, it was necessary to have different implementations of the `ProvideValue` function. This was due to the differences between the two frameworks and the way they support bindings internally.

The XAML still ends up being the same.
For example, here's a snippet from a WPF app that does a similar thing to the above, but also uses a static method that accepts a parameter to set the text color based on the bound value.

<TextBlock 
    Text="{rx:BossBinding Path=Message,
             Converter={x:Static utils:Converter.MakeUpperCase}}"
    Foreground="{rx:BossBinding Path=Message,
       ConverterWithParameter={x:Static utils:Converter.ChangeColor},
       ConverterParameter=True}"
    />


The conversion function here is again very generic, matching what is done with an IValueConverter.
The code is also somewhat arbitrary and was created primarily as a quick way to demonstrate that this can work with parameters and return types other than strings.

public static Func<object, object, object> ChangeColor =>
(input, parameter) =>
{
     if (input is string str
         && parameter is string parameterAsString)
    {
        if (bool.TryParse(parameterAsString,
                 out bool parameterAsBool)
             && parameterAsBool)
        {
            switch (str.Length % 3)
            {
                case 0: return Colors.Blue;
                case 1: return Colors.Red;
                case 2: return Colors.Green;
            }
        }
    }

    return Colors.Black;
};



That solves the second problem mentioned at the start, but what about the first (syntax verbosity)?
Maybe that's something ENAMEL can help with..



Am I going to use something like I've shown above regularly in future? 
Maybe. I rarely use ValueConverters, so probably not.
It solves the problem of needing to create and instantiate custom types when all that's needed is a static function, but I still think it requires too much text in the XAML file.
Still, it's an option for anyone who prefers it. We don't all have to write code the same way, and there may be good reasons to sometimes need this.

If and when I do need to use a value converter, I prefer the automatic generation of MarkupExtensions based on the class implementing IValueConverter. I find it makes the required XAML much shorter and clearer. 

It means that instead of having to write this in the XAML file:

<Label Text="{Binding Message,
             Converter={StaticResource MakeUpperCaseConverter}}" />

I can write this:

<Label Text="{conv:MakeUpperCase Message}" />


But that's probably another post for another day.



I appreciate being reminded that there are simple ways to create something that solves a problem when you don't like what comes out of the box.


What I really reject is the idea that someone should complain about a limitation of a framework (which can never do everything that everyone might want or need) when there are simple solutions that any developer should be able to write themselves. I'm almost surprised that this is a situation where developers don't want to write code to solve a problem they're facing. Writing more code as the first solution is frequently something that developers need to be stopped from doing.