Tuesday, March 15, 2022

How you should be using (and naming) resources in XAML

... because a lot of people get it wrong!

We'll get to XAML shortly, but let's start by looking at some C# code:

It seems like a reasonable method, with an appropriate name. Doesn't it?
The name of the function clearly explains what the code does. That's a good thing. Right?

Imagine that the above was created to add a tax to a price. (This is a simplified example where tax is always the same and you can ignore the complexities of tax in the real world.) Now, is the function well named?

Think about where this method might be called from.
Will it always be evident to someone looking at the code why 20% is added to a price?
What if there were other reasons in the codebase to add 20% to a value. What's to stop them from using this function in those places too?

I asked if the above method was well named because it describes what the code does? Actually, this is a lousy way to name something.

The name should indicate the intent behind the code and suggest why it does what it does. We don't need the name to tell us what the code does. We want it to tell us what it is for. This means the name doesn't need to change when the code changes, and it clearly indicates where and why it should be used.

If we have the AddTwentyPercent method in our codebase and it is used for something other than calculating tax, what happens when the percentage for sales tax changes?

If we only change the contents of the function, we end up with something whose name does not describe what it does. Not only does it not describe it, but it also does something different. Something that could easily be misunderstood and end up with code that doesn't do what is expected or wanted. Code that is hard to reason about ("I know it says add twenty percent, but I need to remember that it actually adds 22%") and code that is harder to maintain because anyone unfamiliar with this "special case" could think it does what the name suggests.

If the tax rate changed, we could respond to this by creating a new function called--for example--'AddTwentyTwoPercent' and changing the calls to the old method related to adding the tax amount to call the new function. But how would we know which of the places that used the original function were to do with calculating tax? We'd have to go through and check them all.
We wanted to make a change to the percentage used to calculate tax. This should mean changing a single value in one place. However, we're now having to potentially review lots of code in various parts of a codebase. It should be a simple change, but a poorly named function has made this task much more complicated, time-consuming, and therefore also expensive.

Instead, what if the function had initially been named 'AddTaxToPrice'?

In that case, it would be clear what it does and where/why to use it. It should also raise a red flag if it was used for something else that happened to need to add an amount to a number that coincided with the tax percentage at that time.
If we had a function with the 'AddTaxToPrice' name, we could confidently change the contents of the function without needing to worry about where it was used or whether the name needed to be changed.
We'd have encapsulated the logic in a single place, with only a single reason to change, and minimized the chance for confusion about what the code does.


Now let's think about XAML.

Consider the following:

This might seem like a reasonable name for a resource. You may have even seen this in code presented as an example of "best practice." I actually think this is a terrible way to name a defined resource and consider it an anti-pattern. (i.e., an example of something you should NOT do.)

There are issues with this that are comparable with the C# function discussed above in the following negative ways.

  1. The name matches what the code does.
  2. The name does not indicate where or why it should be used.
  3. The code (thickness) could easily be used in very different places and for other purposes.
  4. If one of the places where this style was used needed to be changed, we'd have all the same problems as with the C# function. 

Imagine this thickness is used as the margin for Grids containing the contents of most pages in an app.
Now imagine you want to make a change to the app's design, so you have a large margin on the right-hand side of each page and add a smaller margin to the bottom of the content on each page. What do you do?

  • If you change the value of the existing resource but not the name, you get something with a name that doesn't match what it does. This leads to potential confusion for anyone (likely including yourself) reading the code in the future.
  • You could create a new resource and change all references to the old name to the new one. But, this means reviewing all uses of the original resource and only updating the appropriate ones. This could be a lot of work.

Imagine instead that a resource with the name 'StandardPageContentsMargin' had been created.

  • The exact details of this could be changed without the need to rename anything.
  • The resource's details could be changed without worrying about unexpected side effects.
  • It would be obviously wrong if someone tried to use the resource for something else. (Such as another type of control or place within the UI.)
  • You wouldn't waste time in an unnecessarily complicated and hard-to-maintain codebase.


In software development, how you name things matters. Yes, even in XAML.


Give resources names that indicate what they are for and where they should be used.

This will give you XAML code that is easier to write, review, understand, and change.


Yes, this might mean you end up creating multiple resources with the same contents/value but different names. No, the current tooling does not do a lot to help with creating similar resources. But that doesn't mean you shouldn't do it.


But, the "best practice" examples do the bad thing? - Yes, and we (I) shall (will) change them!


Sunday, March 13, 2022

Some notes for people giving technical talks

At a point last year (that I'm not going to identify) I got very frustrated with the way many people were giving technical talks. 

This is a list I put together at that time with some important things for anyone giving a technical talk to remember.


It's not about the speaker. We don't need to hear their biography or life story.

Tell a story. Or, at the very least, give the impression that you have a structure and it's not an arbitrary collection of things in an arbitrary order.

The talk is not an abstract thing. Don't talk about the presentation you're giving as an external thing. Especially not as something you have no control or influence over. It's YOU giving a talk. The talk doesn't exist separately from you.  

Don't give a talk anyone can give. Let others do that. Give the talk that only you can do.

No excuses: rehearse, rehearse, rehearse & get timings right. If you run out of time it shows you weren't prepared and therefore are happy to waste everyone's time and not give them what you promised.

If online - look at the camera. If you've chosen to have a camera on you, look at it. We don't want to see you looking above, below, or beside the camera. We really don't want to see the side of your head. 

If in-person - look at the audience. Not at the screen. Not at your laptop. Not at the floor. Not at the ceiling.

Put key names and terms on the screen (not just logos.) The audience might not recognize a logo or a term you mention. If they want to look up more about it later, it really helps if they know how to spell it. 

Share the resources. Don't say you will and then not do it. And make sure you share them everywhere the talk is made available. If you don't know then ask the organizers where it will be.

Have short versions for all URLs. Don't put a long URL on the screen and hope that someone in the audience will be able to read or accurately copy it.

Have a really clear title that matches the talk. Don't mislead an audience to attend by letting them think a talk is about one thing when it's actually about another. This includes using broad terms when you're actually going to be talking about something very focused. 

Do something specific to the audience/situation. No one wants the same thing repeated verbatim somewhere else.


whining over ;)

Friday, March 11, 2022

Matt's Template Studio for UWP, WPF, & WinUI (C#)

 

list of installed extensions

I'm still busy migrating my experimental port of Windows Template Studio of Visual Studio 2022 to the official repo. (See the progress here.)

In the meantime, today I've released updates for my versions of the extensions. Please download and try them out. Help me find any last bugs or issues. These latest versions include lots of bug fixes and improvements. My versions will be where I try out new ideas and provide features and options that aren't likely to ever end up in the official Windows/Microsoft version.

To help distinguish my versions from the soon-to-be-released official versions, I've renamed my versions. Hopefully, this will avoid any possible confusion and help more people realize the effort I've put into this. (Lots of people think I'm being paid by Microsoft to do this.)


These wonderful people helped make this possible. Will you consider joining them?

GitHub sponsor images



Wednesday, March 09, 2022

What's wrong with this WinUI code?

Here's some simple code.

There are probably many ways you can see that it could be different, but there's one major potential problem with this code. Can you see what it is?


private async Task OpenInBrowserAsync(Uri uri)
{
    await Windows.System.Launcher.LaunchUriAsync(uri);
}


The problem with this code is that it contains an unprotected "seam" between the code that's part of this codebase/application and code that isn't.


I'm going to use "your code" to mean the code that's written by you and "other code" for everything else.

Anywhere there's a place where your code meets other code, it creates a seam. A seam is where there's a potential weakness. When two things are connected or joined together, there's a potential for problems. It's where defects or issues are most likely to surface.


Let's go back to the code above. When LaunchUriAsync() is called, three things could happen.

  1. The URI could be opened in the default browser. This is what is expected and will hopefully occur most of the time.
  2. There could be an exception thrown by the other code.
  3. Something could happen that means the provided URI can't be opened, but no exception is thrown.

(Yes, other possibilities exist but won't be covered here.)


Scenario 1 is the easiest to think about. Everything works as expected, so we don't need to discuss this further.

Scenario 2 is vital to consider as it's likely to have a significant negative impact on the people using your app. Hopefully, it'll never happen, but it's always a possibility. You don't want other code to cause your app to crash. The people using it will still blame you. That an exception has its source in other code doesn't matter. That any potential exception is also coming from async code has the potential to increase the possibility that the exception could crash the whole app. Handling the potential for other code to throw an exception is essential!

Scenario 3 is easy to overlook. That code "works" or throws an exception is easy to appreciate. That there's a middle ground--where it "works" but not as expected--can be harder to appreciate and allow for. However, it's equally important for the people using the app that you allow for this as it is to handle exceptions.
If someone uses an app and when they do something, it causes the app to unexpectedly crash, it's easy to know something is wrong. But what happens when the person does something (click a button, select an option, whatever..) and nothing happens? The thing they wanted or expected doesn't happen, but neither does anything else? Do they try and do the action again? Could that have separate, adverse side effects? Is it being slow? Or slower than usual/previously? Did they do something wrong? Is the app broken?
Creating uncertainty in the minds of the people using your app is not something you want to do. It does not lead to happy users. Unhappy users lead to less usage, fewer sales, fewer recommendations, bad reviews. Nothing good.

As per the documentation, the method returns a boolean value to indicate if the URI could be launched.
Ignoring responses is not always wise and is certainly not a "best practice."

In programming, it's usually good to call a method and trust it will do the thing that's asked of it. However, a function that returns an indication of whether it could do the thing that was asked of it is not to be ignored.
Imagine asking someone if they can do something for you but sticking your fingers in your ears when they say if they can do it or not. You could assume that they can and will do it for you. But, if they say they can't, and you carry on assuming they can (and will), whose fault is it when it doesn't get done?

Do you like ambiguous code? Do you like using software where you can't be sure if it's working as expected? Do the people using your app like software that works like this? If not, don't give it to them.


Connected with the above is the issue of testability.

As the above is a small snippet of a larger codebase, it's not clear how (or if) it is tested. But we can assume it probably isn't.

It might be appropriate to abstract the interaction with the other code to make it possible to easily (and automatically?) test different actions and responses. Such tests can be more important than tests related to your own code, as it's often harder (or impossible) to forcibly get every possible response from other code so it can be tested.



So, what should the above method actually look like?

Well, here I might disappoint you. There is no single "best practice" way to write a version of the above method.

Instead, a "leading practice" would be to handle the response from the method, acknowledge that an exception is possible, and make it possible to test for such scenarios.
Maybe I'll show you an example of such code in the future. Let me know if you want to see it.



The above doesn't only apply to code that is part of the WinUI API.

This applies to any code that you didn't write. It might be an API from the platform, or it could be any library you're using. Any "seam" between code you write and code you didn't needs to be protected.


This doesn't (hopefully-obviously) only apply to WinUI related code, but I found a variation of the above amongst code that was supposed to be an example of "best practices." In lots of ways, it's definitely not a good example but I called this out in relation to WinUI as developers building with it deserve the best help I can give.


Wednesday, March 02, 2022

Are you still using Visual Studio 2017?

Visual Studio 2017 icon/logo

Are you still using Visual Studio 2017?

If so, what for?

Seriously, I want to know.

I ask because support for it officially ends next month! (12th April 2022) but I know that some users of my extensions are still using it.

If you are dependent upon any of my extensions continuing to work on VS2017 now or in the future, please reach out to find out how/if I can support you.

Or just update to VS2022.


For reference, there is more on support lifecycles in this post on the Visual Studio blog.