AKA the death of the multi-targeted package.
As we herald the arrival of Umbraco 10, on .NET 6, the package development community faces a conundrum.
It’s not a new issue, we faced similar with the initial shift to .NET 5 with the release of Umbraco 9, only this time round there’s really only one sensible solution.
For me, and for Plumber, Umbraco 9 meant two diverging package codebases, to manage legacy installs on Umbraco 8 and .NET Framework, and new modern installs on Umbraco 9 and .NET Core.
Like many other package devs, I released a single, multi-targeted package. My codebase was updated and littered with pre-processor directives, to compile two DLL versions - one each for Umbraco 8 and 9.
That worked because I knew that any .NET Framework install MUST be Umbraco 8, and any .NET Core install MUST be Umbraco 9.
However, Umbraco 10 breaks that confidence (through no fault of it’s own, but through the limitations of the same pre-processor directives that saved us when managing 8 and 9).
Let me attempt to illustrate…
#if NETCOREAPP // this only applies to Umbraco 9 #else // this must be Umbraco 8, since it's not .NET Core #endif
All good, we have a clean, logical separation between versions and can target effectively and confidently.
Introduce Umbraco 10 and .NET 6, and we have issues.
#if NETCOREAPP // is this Umbraco 9 or 10 #else // this is still Umbraco 8 #endif
Why can’t we do this?
#if NET6 // this must be Umbraco 10 #else if NET5 // this must be Umbraco 9 #else // this must be Umbraco 8 #endif
Because Umbraco 9 can run perfectly happily on .NET 6, we now have no way of distinguishing Umbraco 9 and 10 via pre-processor directives.
.NET 6 could be Umbraco 9 or Umbraco 10. We could add runtime checks to determine the Umbraco version, but will still hit issues with interface and method signature changes which would break compilation. We could add facades and new implementations to manage those changes, but that’s a losing battle.
Umbraco 11 will arrive in six months, with its own breaking changes, which would also need to be managed.
Long story short, without creating a huge amount of maintenance burden and writing a stack of normalisation code, multi-targeting is no longer a realistic option. Or, more accurately, multi-targeting Umbraco 9 and 10 isn’t realistic.
So what does that mean for Plumber?
My intention (at time of writing), is to align major releases with Umbraco’s release cadence. Those majors may only contain minor changes, but should be in sync with Umbraco’s releases.
That also means no more multi-targeting. Plumber 10 targets Umbraco 10. The Plumber v2 branch will continue to run on Umbraco 8 and 9, and any relevant bug/security fixes will be cherry-picked from the current release branch back into a v2 minor. However, expect to be encouraged to upgrade Umbraco.
New features won’t land in Plumber v2. It’s no longer viable to offer new goodies for 8 and 9, as well as 10. Or 8, 9, 10 and 11. And so on. You get the picture.
It’s also an incentive to keep up with Umbraco releases. A major Umbraco release is a different beast today than it was when jumping from 7 to 8, or 8 to 9. It’s a much simpler task, and should come with benefits like access to latest and greatest package features.
We should all be looking forward, not backwards. There’s always effort involved in an upgrade of any shape, and I’m sure selling the benefits of any upgrade to clients must be difficult. But much like the struggles to get users off old browsers (cough cough IE 9/10/11), it’s absolutely worth it in the end - clients get to use an improved product, and developers get to enjoy working with modern tooling.
I’d intended publishing an RC today to mark the end of the 2022 financial year (and because I had a few minutes to do so), but something went screwy in my pipeline, and the version was tagged
So, bonus, Plumber for Umbraco 10 is available now. It comes with some breaking changes, which will only be an issue if you’re extending the package. Anyone using it straight OOTB, which I expect is most installs, will have no problems.
Breaking changes are explained (briefly) in the readme.