Wednesday, January 24, 2024

Lessons from mobile notifications applied to IDE Extensions

TLDR: If you want to prompt "the user" to do something, let them get value from what you provide first.

Dog with pleading eyes

Photo by Jennifer Latuperisa-Andresen on Unsplash

Mobile apps want ratings and reviews. These are also valuable for open-source projects. This applies in a marketplace/store or as libraries/bundles/packages for download.

Increasingly, in the open-source world, the issue of sustainability is also a consideration.

Among other things that contribute to sustainability is financial support.

I use a variety of approaches across my open-source projects to try and encourage such compensation via Google Sponsors.

It doesn't make a massive impact on my finances but it has been enough to make a difference and was also the only way I could afford an unexpected tax bill when I was out of work during the pandemic!
Regardless of the amount or duration, I will always be grateful to those who have (and still are) sponsoring me. Thank YOU!

Yes, I ignore the amount and duration. All my sponsors get access to the same benefits, be it a one-off amount of $1 or recurring amounts of much more. (I previously did more analysis of these differing durations and amounts.)


Anyway, one of the approaches I use to encourage people to consider becoming a sponsor is a message displayed in the output window asking them. (If they do, I also tell them how to make such a message not appear.--No phoning home. No personal data is collected. 😉)

This approach isn't appreciated by everyone but seems to be very effective, as I have more people sponsoring me than most other people I'm aware of who casually contribute to open-source projects.

My approach to monetizing my projects is very much inspired by donationware, and most software made available in this way doesn't hold back from asking for donations. 

I had been following this approach but wondered if a different technique might be more effective.

In the latest update to my C# Inline Color Vizualizer extension, I changed the behavior determining how visible the encouragement to become a sponsor is.

Instead of always displaying the message to the user, it loads it in the background but only actively shows it once it is at least 7 days since it was first used and it has been used to annotate at least 100 files. The goal is to let the person become familiar with seeing the benefits of using the tool before asking anything of them. The theory is that they will then be more inclined to respond to the message.

I must have said and heard the same advice applied to mobile notifications hundreds of times but had somehow overlooked its wider application.

If you're not familiar with the extension, it adds examples of the. Like this. Why not give it a try?

Partial screenshot of the VS editor showing what the specified color looks like.

I plan to add similar changes of behavior to my other Visual Studio extensions as is appropriate to the way they work and what they do. I'll share details here if it proves effective or I learn anything insightful.


It's not only about the money I get. I like that this encourages more people to think about the sustainability of their tools. I think such messages do this, and the fact that most of my sponsors aren't sponsoring other people (yet) makes me hope that I'm just the first of many or that they find other ways to support the software they use (if not rely on.)


Sunday, January 14, 2024

Wednesday, December 20, 2023

Never delete tests

Ok, not never, but only delete them when they really serve no value and the codebase is improved by their removal.

As an example, I was recently told this test (well, one very like it) should be deleted.


[TestMethod]
public void CanCreateInstance_WithoutError()
{
        var sut = new MyCoolClass();
   Assert.IsNotNull(sut);
}


I was told this provides no value, and the code path will be covered by one of the [many] other tests [that exist and will remain] so I should delete it.


Firstly, I originally created this test before `MyCoolClass` existed, and the test only threw a NotImplementedException. I created test cases for what I knew I would want to implement. This allowed me to have Test Drive the Development and tell me when I was complete--because all the tests passed.


Ok, but that was then. The class has now been implemented, and many passing tests exist. Is it still worth keeping? 

Secondly, I think there is long-term value in having such a test:

  • It documents that it needs to support a parameterless constructor. If an additional constructor was added, this test shows that there's logic dependent on supporting a parameterless constructor.
    Yes, the compiler will complain if other code depended on such a constructor and one no longer exists, but part of the purpose of the test is to document the design intent behind the code. Here; the test documents that a parameterless constructor was intended and an instance can be created without any dependencies. If we start adding dependencies [or injecting them?] into the constructor, then this test tells us (hopefully) that we may need to revisit the expectations and wider implications of such a change. We may be able to get the code to compile after such changes, but are we breaking some subtle design intentions and decisions that were part of the original design and which may have implications that aren't immediately obvious?
    If there is logic in the constructor, this is especially important.--Even if there isn't any logic there now, it's good to have the test ready in case some is added.
  • It sets an example for other developers on the team that you want to encourage to write more tests. Having an example that shows all of the TDD process is better than only showing the part at the end. 
  • (Probably most importantly) This is useful when/if something fundamental breaks in the class. While technically, this functionality is exercised as part of all(?) the other tests, if everything else starts failing, but this doesn't, then we know the issue is not in the constructor. If this test fails, we know there's a problem in the constructor. We don't have to work it out from the other tests.
    Tests don't only tell us when something is wrong. They help us identify what is wrong when something breaks.
    I want to be able to look at the names of tests and see what they test. I don't want to have to work it out by looking at the data.  Looking at the names of the tests that are passing and/or failing should give me a clue where the problem is.


Why might I delete a test like this?

  • If we needed to support a constructor that does take parameters. (Although I may just modify this one.)
  • If this test was slow and the total execution time of all tests was an issue. Because this code is technically "tested" by other tests, I may accept that overall execution time is more important. In this case, it's not. This test runs in under 1 millisecond and is part of a larger suite that runs in a few seconds.


Finally, does debating whether to keep or delete this test constitute a good use of everyone's time?

There are many factors to consider:

  • How long does the test take to run? (Multiplied by every time it is run)
  • How much time is spent debating whether to remove it?
  • How long does the CI on the PR to remove the test take to run?
  • How long does it take someone reviewing test results to skip past (assuming they pass successfully) the results for this test?
  • How much time is spent navigating and reviewing the test while maintaining the code base? 

No, there often aren't easy answers to the above questions.

My default position on removing tests is to only do it when the test is wrong or is so slow that it has negative consequences on the development process/workflow (and so harms the business.)

I've never been in a position where the best use of anyone's time was to discuss and remove tests. (I even wrote this post in my own time.)

I can also only think of a handful of occasions where the execution time of a test (over the lifetime of a project) was more than the execution time of the CI on a PR to remove it. Even in most of those situations, the original test was kept, but moved to be less frequently run.


Sunday, December 10, 2023

Why I'm a [statistically] bad but also valuable developer

On one of the teams I've recently been working with, we have a channel in Teams for asking questions if you get stuck.

Officially, the guidance is that you shouldn't stay stuck for more than 15 minutes without asking for help.

The intention is that no one should be stuck for long without making progress.

I noticed that I was posting there more than most.

A quick review of the posts there in the last two months, and yes, I post there, asking for help, more than 15 times as often as other team members.

Some people manage to go months without asking for (or needing?) help!

I actually post on that channel more than I might. In starting to write about what I've already tried, I often come up with other ideas to try, and they sometimes pay off. (Maybe it would be more appropriate to rename it the "rubber duck" channel.)

15 minutes isn't very long to get stuck on something. Some of the things I've been working on recently are highly complex and can take hours to try the likely possible solutions I can think of.

But, as soon as I get stuck I ask for help.

There's no point asking for help when I haven't tried the things that are likely to be suggested. But once they're exhausted (and documented if I need to share them with someone who might be able to help) I ask for assistance.

When working as part of a distributed team, asking for help, even asynchronously, as soon as it's needed, is an important and valuable skill.

You don't want to be that person who waits to be asked if they're stuck or struggle for days without making progress. Such people are rarely good for a team.

We'd all love to never need help, but we can't all know everything. 
If you're doing new things (which may be most of the time) then you're bound to encounter things you don't know and need assistance with.

Asking for help isn't (or shouldn't be) a problem. It's the people who don't ask for help that you need to watch more closely. They may be brilliant. They may be struggling.


Yes, my initial statistic doesn't allow for anyone asking other people for help directly. I can't measure that. But I do know that in large, distributed teams, it's hard (impossible?) to know who is the expert on every topic and who will be able to provide the quickest response due to time zones and flexible working patterns.




"Why an MVP shouldn't use their extensions to hijack a developers GeneralOutputPane in Visual Studio to solicit sponsorship and to use it as their own personal advertising space via a class called SponsorRequestHelper.cs."

And the answer is...because entire teams will no longer use the extensions. Because its not cute, it's distracting. Because it's not witty, it's inappropriate. And because I don't believe that blackmailing developers with removal of the nag once they sponsor you is very becoming of an MVP. While I was looking forward to using some of your extensions after a MAUI webinar I attended the other day, sadly, our team will not be using any of your extensions. And hijacking these special method handling attributes is irresponsible, because Im pretty sure Microsoft wont agree that your desire for sponsorship is in any way SPECIAL or more important than the runtime in which you are hijacking.