The .NET Standard Migration Challenge
The original Pushover.NET library was built for .NET Framework. It was a solid library, but locked to Windows and unable to run on .NET Core, Xamarin, or the modern cross-platform .NET runtime. As more projects moved to .NET Core and beyond, we needed a Pushover client that worked everywhere. Rather than writing a new library from scratch, we forked the original and ported it to .NET Standard 2.0.
Targeting .NET Standard 2.0 was a deliberate choice. It provides the widest possible compatibility: .NET Framework 4.6.1+, .NET Core 2.0+, Xamarin, Unity, and every version of modern .NET from 5 through 9. This means a single package runs on all of these platforms without conditional compilation or multi-targeting.
What Changed in the Port
The migration involved replacing .NET Framework-specific APIs with their .NET Standard equivalents. HttpWebRequest became HttpClient. Configuration that relied on System.Configuration moved to a constructor-injection model. Assembly attributes and project structure were updated from the legacy packages.config format to the SDK-style project format with PackageReference.
The public API surface remained largely unchanged to make migration easy for existing users. You create a PushoverClient with your application token, then set the recipient, message, priority, and optional parameters such as sound and HTML content. Finally, you call SendAsync. The library handles serialisation, HTTP communication, and response parsing.
We also added proper async support throughout. The original library used synchronous HTTP calls, which block threads. This was acceptable in .NET Framework desktop apps, but problematic in ASP.NET Core, where thread pool starvation can kill performance under load. The ported version is fully async with cancellation token support.
Publishing to NuGet
The ported library is published as PushoverNET.Standard on NuGet, installable with a single command. The package includes XML documentation so consumers get full IntelliSense in Visual Studio, Rider, and VS Code.
One lesson from publishing was the importance of clear naming. The original package was Pushover.NET; ours is PushoverNET.Standard. The ".Standard" suffix immediately tells developers this is the cross-platform version, avoiding confusion with the original .NET Framework package.
The library supports all core Pushover features: sending notifications to individual users or groups, setting priority levels from lowest to emergency, and choosing from the full catalogue of notification sounds. It also supports HTML formatting in message bodies and attaching URLs and URL titles. Everything that works in the Pushover API is accessible through a clean C# interface.
When to Port vs. Rewrite
This project reinforced an important principle: porting is almost always better than rewriting when the original code is well-structured. The original Pushover.NET library had clean abstractions and sensible separation of concerns, which made the migration straightforward. The bulk of the work was updating infrastructure (project format, NuGet references, async patterns) rather than rewriting business logic.
If the original code had been tightly coupled to Framework-specific APIs or had significant architectural issues, a rewrite might have been justified. However, in this case, a port preserved the battle-tested API surface, maintained backward compatibility for existing users, and took a fraction of the time a ground-up rewrite would have required.