I think there is yet another option we both didn't state, but an option that is also in use to this day. Some apps simply have multiple entries in the current AppStore model, and the version you currently have is very much separate from a possible 'future' upgrade. I'm not sure how they handle major upgrades on the back-end (perhaps tied to an in-app purchase or transferable purchase based on some sort of online account?), but this is a more 'hard' distinction between actual versions.
I suspect that this model has some downsides considering good security practise would prevent the apps from sharing their namespace contents. And for end-users they might end with "MyCoolApp 1" at first, but then "MyCoolApp 2, MyCoolApp 3, MyCoolApp 4" unless they delete the old one. I think some app vendors even use the year as the release distinction but in true software engineering fashion those years and the actual release date tend to be out-of-sync (just like MS Office).
Based on the wording comment it sounds like there may be additional ways I haven't seen but you didn't really say what they were as much as dismiss the point assuming everyone knew what apps or release models you are familiar with.
I agree an in App Store solution like the one you describe would be the preferred answer for most users. Sideloading is more useful as a catch-all for anything Apple doesn't have a good first party answer for. Once non policy related use cases for the sideloading exist it's going to be easier for most to have a first party solution for those use cases.