I've learned this as "swallowing errors" and IMO it's a poor practice. Not only does it not solve the issue at hand (you cannot print on an xbox), but it actively hides how broken the software is, which makes bug discovery and testing much harder.
This is one thing I like about Go's panic. You're mostly not supposed to use it or recover from it at run time. It serves as a great vehicle to blare loud sirens at testing time that you (the programmer) screwed up (and that's ok, we all do), and it's time to figure out where and how to fix it :)
PS this analogy works in a lot of domains - If you have actors in a system actively trying to hide their flaws/errors it will be exponentially harder to root them out and solve the issues.
Error design depends on context. For most of the systems I work on, fail fast on request for anything out of spec is the correct design. B2B/Microservices/API focused systems. Windows however has focused on bending over backwards to provide compatibility (including memory patching popular software that was broken, like Simcity https://arstechnica.com/gadgets/2022/10/windows-95-went-the-... ). In the context of a user desktop where a user has no control of correcting the system code, the final experience is more important that correctness in many cases. It doesn't matter who is "in the wrong". Microsoft learned that it doesn't matter why the BSOD occurred, just that bad software/hardware was giving them the bad reputation.
So yeah, fail fast/halt catch fire on any invalid input/condition is my personal preferred design, but I can see the value in this approach in this environmental context. The important thing here is that context and not applying either dogmatically. Don't take Mr Chen's approach in reactor or HFT designs for example. Fantastic approach for game engines.
It's hard to overstate how hard Microsoft has worked to maintain backwards compatibility.
Recently I had to read an old Access file and where I work we still keep Office '97 around for this purpose and it is quite amazing that it installs and works just fine on Win 11, Clippy works just fine, in fact all the other lame-ass things Office '97 does to take over your desktop all still work even if they don't quite visually match the current desktop.
The thing is that Microsoft has a framework built into Windows where they can specify different system behaviors such as that Simcity case where the game didn't work with a new memory allocator so they put in a flag so an application that needs it can get the old memory allocator. They have a very systematic approach to the half-baked and haphazard process of patching the OS so you don't have to patch applications.
> It's hard to overstate how hard Microsoft has worked to maintain backwards compatibility.
Here's a pretty detailed list:
- It is possible to target Windows XP SP3 (released 2008, EOL 2014, 10 years ago) from Windows 11 and Visual Studio 2022[1] using C++23. Windows 2000 can be targeted with a little more setup[2]. Windows 2000 is 24 years old this year.
- It is possible to run a binary written for and compiled in Windows NT 3.1 x86 on a modern 2024 Intel/AMD PC running Windows 11 with absolutely no modifications to said binary whatsoever. Windows NT 3.1 is 31 years old this year.
- It is possible to write one binary and target multiple versions of Windows by simply choosing a Platform Toolset, which is paired with a Visual C/C++ Redistributable.
- Windows has a run-as mode to run programs in what is essentially a super-thin VM; like you mentioned, emulating different iterations' behaviour for a given program.
All four of these are nearly impossible on Linux. The fourth is essentially Docker, which is needed even to target an older glibc (i.e. the equivalent of the first situation). Windows has gone to extreme lengths to not only maintain API compatibility, but ABI compatibility as well. The fact that unmodified binaries from 20, 25, 30 years ago can run without a hitch is a testament to how important backwards/forwards compatibility still is on Windows.
Side tangent: all the criticisms levelled at Windows here and in many hacker fora are limited to its userspace—things like its shell and user-space programs, and trivial complaints like 'Candy Crush', or 'start bar', or 'extra right click', or even 'PowerShell aliased curl to Get-Content, grr'. The userspace changes so often because it can; it is a superficial part of Windows. To the haters: Try actually programming with NT OS primitives and even the (undocumented) NT syscalls. It is as enjoyable as UNIX system programming, and I daresay more productive.
I wish they included an 8086 emulator so that old software compiled for DOS would still run. It worked on 32 bit systems until 32 bit support was dropped, which only happened a few years ago. That was due to Intel's virtual 8086 mode, which is not available if your CPU is running in 64 bit (long) mode. Modern computers are fast enough for the emulation overhead to be negligible, even if you don't do any fancy JIT tricks and just go with a switch/case inside a while(true).
I would personally make use of this, I know of a 16-bit program whose latest version was released in the late XP days, so it's not even that old. The idea there was that it was always compatible with DOS, some users might presumably still want to run it on DOS, and there's no point in breaking that compatibility if modern Windows can run it just fine. Then development stopped, 64 bit systems got more popular, and a recompiled version was never released.
I guess the lesson there is that if you're keeping an API for backwards compatibility, some programmers will refuse to switch to a replacement to make their software work on older systems, making the API impossible to remove down the line without breaking modern programs.
At least you can still do this with third-party software like https://github.com/otya128/winevdm I guess? I imagine Microsoft doesn't see the returns for it to develop something they'll have to support for decades more…
This is great and all until you completely ignored the fact that wine exists and went on to call user space design questions by Microsoft “trivial”. I could also start listing ways that the Linux kernel maintains backwards compatibility with not just software but hardware that’s decades old but the list would get too long. No one is complaining that Microsoft has too much backward compatibility, it’s their utter disregard for user choices and privacy that drives away the hacker community to either Linux or even macOS.
Linux (kernel) has a userland ABI of its own. Which is pretty stable & rarely broken (Linus will probably breath fire @ any kernel developer who does break user space). So eg. 10y old statically compiled binaries will likely run fine on recent kernels.
But as you state: it's the OS-level ABI's (C library, desktop environments, middleware libraries, etc) that keep changing constantly. And thus, keep breaking pre-compiled binaries.
Source-level API vs. binary ABI stability. Kind of a philosophical debate imho. But sadly, even that source-level API isn't too stable in some circles.
application /= software. You're moving the (extremely ill-defined) goalposts. Next you'll be arguing that device drivers or the Linux VFS layer aren't "software".
The goal hasn't moved, we are talking about ABI compatibility for a full operating system across multiple decades and generations of operating systems releases, without requiring building those applications from source.
Not a kernel booting directly into an application doing straight syscalls.
Somehow, it feels like you don't actually care about the point I was making as much as putting me down. I misspoke...barely, yet you're so fixated on pointing it out in the most unhelpful manner. You keep replying so why not try using more than one sentence next time?
Now that you got there, which distribution released in 2023 allows me to run a GNU/Linux binary compiled in 2000, regardless of the distribution it was compiled on back in 2000?
If a binary from 2000 doesn't run, it's because of glibc ABI changes. I still have faint memories of glibc crap happening in the early 2000s. But if the binary from 2000 is statically linked, then the Linux kernel probably runs it fine today.
Which is weird considering the argument you had with others above about how "Linux (kernel) doesn't run software", was it a buildup to convince us that "GNU(glibc)/Linux" is really bad at running old binaries? Because your argument doesn't hold for the Linux kernel itself running statically linked binaries.
What am I chatgpt? Just make your point and tell us which 2000 binaries don't run and we can argue about whether or not that counts as a mark against backwards compatibility.
Also, is this a trick question about binaries that weren't patched for y2k or something?
This is impressive, but other parts of Windows are so dreary. Installs of apps that throw up all over the disk, Windows Updates that mysteriously fail in unrecoverable ways 87% of the way through and cryptic error codes and procedures to dig yourself out of the jam (before you must reinstall).
> Installs of apps that throw up all over the disk
This seems like a norm for most operating systems. Linux mixes files from all sorts of apps all over the place, for example, and a `make install` might put files anywhere.
> Windows has gone to extreme lengths to not only maintain API compatibility, but ABI compatibility as well.
Linux has a stable ABI as well. For a given architecture, like x86_64, syscall numbers and their arguments and their arguments/outputs are expected to be stable.
The stable API/ABI promise of Linux is the syscalls, because at the end of the day that is the interface between userspace applications and the kernel, and kernel devs take not breaking userspace very seriously.
And on Windows, the opposite is true. The stable interface is the Windows DLLs that you are supposed to use dynamically. If you hardcoded the syscalls, you cannot complain if your application breaks because windows only promises that the DLL will not break backwards compatibility, and they reserve the right to change the syscalls with any update.
So you can have a Linux binary that works across many Linux versions without breaking. You can also have a Windows binary that works on one version of Windows but not the next.
I have unfortunately run into exceptions. I tried to play Neverhood on my Windows install, and it wouldn't start up. Tried the various compatibility modes. No luck. I ended up running it under Wine in Win95 mode (or similar; I don't remember the exact version) on my Fedora desktop and it ran fine.
I haven't tried running too many old programs, though, so I have no sense for how common this might be.
"The car works great technically, people just have trivial complaints about the steering wheel being made of razor blades."
That is becoming less and less of an exaggeration as Windows is progressively further enshittified.
NT seems to be a nice OS at the core, but that's more about what it can do and how it is implemented than about how pleasant it is to use. Some of its syscalls are much more convoluted than the UNIX ones. Typical functions take many parameters, some of them complicated on their own, and typical invocations pass a bunch of NULL arguments (you still need to roughly understand what they mean).
It’s close to common sense, end users don’t care which monkey(s) threw in the wrench if they encounter an error, just that some entity did.
The only caveats I can think of are that it must prominently display that it’s running in a “compatibility mode” and that any encrypted subsystems can’t revert to a lower standard of encryption, which may render the application unusable anyways depending on how tightly integrated it is.
> all the other lame-ass things Office '97 does to take over your desktop all still work
I'm curious about this. What do you mean? I remember using Office '97 on a Windows '98 machine, but I don't remember Office trying to take over all my desktop.
> Microsoft learned that it doesn't matter why the BSOD occurred, just that bad software/hardware was giving them the bad reputation.
But the primary resolution to that problem was to force hardware vendors to write higher quality drivers (or for MS to write good enough default drivers that HW vendors wouldn't need their own), not to hide the BSOD. Technical details were only removed from them 2 decades after MS started fixing drivers.
Certifications have done a lot, but Windows now supports things like automatically restarting the graphics driver after it crashes instead of bringing down the system. I don't have inside information but from the outside, the HAL on Windows has evolved from freezing on a misbehaving USB device to just prompting you when you have exceeded the max number of USB devices supported. Anyone who remembers trying to dock a laptop back in the Windows 98 days remembers it being a dice roll if you would freeze the laptop or not.
The point of the article, in large part, is that when you’re designing an alternative runtime for a new platform, it is up to you what conditions are considered to be errors. Deciding to make a component of the runtime “inert” exists before and above the decision to make the implementation panic.
In this specific case: it is up to Microsoft to decide what the semantics of “printing on an Xbox” are. It could be an error; or it could be something that “could be supported in theory in the future, but for now has no way to actually accomplish it.” This is a design choice, not a development choice.
After all, in theory, you could print on an Xbox — plug in a USB printer, find a “game” that knows how to print (some enhanced port of a Gameboy game that had GB Printer support, maybe?), and tell it to do so. It’s not necessarily, fundamentally an error that a Xbox game is trying to print. You could define it as an error — but that’s a choice like any other, with trade-offs (in this case, to application portability.) You could define it as asking for the user to choose from no options, as the Xbox actually does. You could have the API lie and say it printed something. You could even actually support printing. These are all choices.
It’s only once you’ve made that choice, defining the semantics of “printing on an Xbox” as an error, that it becomes an implementation/debugging problem if that error isn’t thrown — i.e. gets “swallowed.”
> In this specific case: it is up to Microsoft to decide what the semantics of “printing on an Xbox” are. It could be an error; or it could be something that “could be supported in theory in the future, but for now has no way to actually accomplish it.” This is a design choice, not a development choice.
Anything wrong with "print to file"? There'd be no dead tree output but (file) output nonetheless & printing as a function should work.
> I've learned this as "swallowing errors" and IMO it's a poor practice
This is not swallowing errors. This, in the Linux parlance, is not breaking user-space.
There are two ways to handle the situation presented: error out because the machine can never have printers or return an empty list of printers because the machine can never have printers. They're both valid but only one of them doesn't break user-space.
>It serves as a great vehicle to blare loud sirens at testing time that you (the programmer) screwed up (and that's ok, we all do), and it's time to figure out where and how to fix it :)
Right, but if you read the article, the author is talking less about the developer experience and more about the user experience. "blare loud sirens" is great if you're a tester/developer, not so much if you're an end user. When it comes to the end user, "swallowing errors" is preferable to crashing.
If you read the article, this isn't swallowing errors, it's just returning more-backwards-compatible errors.
> ...when the app tries to print, it will ask the user to select a printer, and show an empty list. The user realizes, “Oh, there are no printers,” and cancels the printing request.
> To deal with apps that get fancy and say “Oh, you have no printers installed, let me help you install one,” the function for installing a printer can return immediately with a result code that means “The user cancelled the operation.”
> a documented return value is ERROR_CANCELLED to mean that the user cancelled the creation of the widget. Therefore, apps are already dealing with the possibility of widgets not being created due to conditions outside their control, so we can take advantage of that
I'm annoyed when "modern web apps" (or similar desktop apps) seemingly do nothing when there's some error, you don't know if you need to wait a bit, or click again (great fun when the UI jumps 1ms before your re-click), or full reload/restart ... luckily that's not at all what this article recommends!
I’m sorry, maybe the article used the wrong example but the main issue comes from the fact that an Xbox app is trying to print something. It should fail, not for the developer experience, but because to start, there is no way the user would want to print something on an Xbox. Something is already really wrong with your app if it tries to do things like this.
Also he says that apps are developed and tested on PCs and that they could print in this context. I don’t know a single thing about Xbox development but I hope you can run/debug them in the Xbox environment (or a simulation).
Let me hope that nobody is running their Xbox apps/games on Windows APIs at development time and releases them on Xbox without further testing.
The point is that the UWP allows running apps developed and tested on PCs on an Xbox. It's for the user's convenience (not having to wait on developers to port to Xbox) as much as the developer's (not having to port to Xbox).
If a user wants to run an app on their Xbox, telling them "no, the developer didn't test this on the Xbox, so I'm not going to let you do that because you might try to print and get confused about it" isn't what the user wants to hear.
When the app tries to print because the developer was "lazy" and didn't test on Xbox, telling them "I'm going to crash your app now because you clicked Print, even though I know you're on an Xbox and I could just ignore that" also isn't helpful to the user.
> The point is that the UWP allows running apps developed and tested on PCs on an Xbox. It's for the user's convenience (not having to wait on developers to port to Xbox) as much as the developer's (not having to port to Xbox).
It's interesting because that's true in some way (in the sense that PC and Xbox are different), and also not true in another (in the sense that a Xbox is in a way a PC, only with a different UI paradigm)
So in the latter sense UWP allows developing apps for that universal platform, and it only so happens that some apps are only designed, developed, and tested for one system (PC) these apps can run on.
In a way I can see working from the Xbox up being a better way to have a robust, secure, uniform platform than the Windows 8 attempt of slapping a secondary paradigm on top of the Windows 1.0 / OS/2 descendants.
I mean, the facility that underpins e.g WSL2 is exactly the same as the one that underpins Xbox game/apps segregation and Quick Resume. In a way the Xbox OS is very much like Qubes OS! In a way the OS UI we see on an Xbox device is a UI for the hypervisor itself.
I would certainly be interested in a "PC" that is so stable, restores state exactly upon updates, "it just works", allows to play games, allows to run a bunch of Linux VMs, with forever perfect backwards compatibility across hardware arch changes and OS evolution through virtualisation/emulation, and has a UI that allows many kinds of inputs and scales from big screen (gamepad, that accessibility input device I can't recall the name) to desktop (kb+mouse) and possibly laptop or even tablet/phone.
I mean it's not that far fetched (technically) that MS would announce tomorrow that an Xbox can run Linux, Windows 10, or even Windows 3.1 in a VM: all the facilities are there and you can even plug a keyboard + mouse today.
One may philosophically balk at the idea ("How dare you touch at my very open IBM PC! Where are my floating windows! Freedoooom!"), but I think it makes sense technically, and it makes sense as a product, and MS has all the bricks to make it happen.
> Something is already really wrong with your app if it tries to do things like this.
The base philosophy of "if it's wrong it should fail" is primarily for developpers, and shouldn't apply to generic customer products.
If it's dangerous, or will cost money, or will have severe ill adverse effects, I'd see the point. If a credit card transaction is wrong, make it fail.
But short of these extremes, the default should be graceful handling of exceptions and help the customer app keep going and deliver some value to the user even if it's poorly written, mishandling the context.
Part of the context here is that UWP (universal windows platform), is a target to write-once and run on any windows platform situation.
This made much more sense when Microsoft had multiple platforms running windows with just different sets of apis activated.
At it's peak, this was: PC, phone, hololens, Xbox.
SMS apis may only work on phone, spatial APIs may only work on hololens, printing may work on several, but not all targets. There are ways for developers to check which APIs are supported at runtime, but you can still call these APIs since they are part of the UWP surface.
Nothing is being compiled here; your customers aren't running debug builds of their apps, and the API code is part of the platform and isn't running the debug bits.
Even as an end user, I hate when my computer seems to be trying to hide something from me. Even if I can't do anything about it, I want to know it's happening. Don't worry, Microsoft. I'm a grown-up and can handle the bad news. If I'm a layman, maybe I just dismiss the dialog and try again. But if I'm a little more of a power user, maybe I'll look up the error message and see if I can start diagnosing or helping.
If you swallow the error message, I'll have zero idea that something is even going wrong! And almost just as bad: if you put up one of those useless infantilizing "oopsie doopsie computer made a poopsie" error messages, I still won't know what went wrong AND I'm being treated like a moron.
I worked for a software company once where our software basically crashed every 2-3 hours of continuous use due to a huge backlog of technical debt, memory leaks, and years of rushing. My manager's solution to this was not to fix the bugs--it was to build a separate "launcher" process that would detect that the application crashed, eat the error messages, and silently re-launch it hoping the user doesn't notice. Way to treat your users with respect...
There is no error message here - more often than not it's a straight up crash. No HRESULT, no popup, just NullPointerException, straight to jail.
In many cases like this, crashes like this are in app startup, so it's not like you learn not to hit a certain button - it's just that the app doesn't work, an awful UX.
As called out in the article, there are (and always should be) APIs like "IsPrintingEnabled" so that forward-thinking apps can show better UX. These practices aren't for those apps, they're for everyone else.
Also, if your app preserves state well enough that a keep-alive daemon can restore after a crash and the user doesn't notice, that is ABSOLUTELY an improvement in UX over just crashing. Sure, you should still fix the bugs, but don't let perfect be the enemy of good.
That is a dangerous assumption. Unless you know a great deal about every possible use case, you can't know the potential ramifications of incorrect output. Proceeding from invalid state (which would often be the result of swallowing errors) is essentially undefined behavior.
This isn't invalid state. They're not telling the app about a fake corrupt printer, they are using the API contact to represent the truth (there is no printer you can use) in a way the app already has to support
And I still think you're wrong. If incorrect input can't be handled gracefully in a way that you can be sure nothing bad will happen, it's possible that crashing is the best option.
But I think in most cases that just isn't what's going on. An unsupported API that makes a feature not work is just not a big deal. Lack of support, say, for a cryptographic primitive, could be a big deal, so you might choose to handle that case differently.
And would you treat an API endpoint that's down the same way? Just silently ignore that a feature that's part of the user's workflow isn't actually working, that maybe only half of what was supposed to happen when they pressed a button actually happened?
Yes, and in the case presented in the article -- trying to print on an Xbox -- we really do actually know the potential ramifications of trying to print and then being presented with no printers to print to. Simply: there are no ramifications worth worrying about.
On the other hand, we do know what will probably happen if an undocumented exception gets thrown: the app will crash, possibly causing the user to lose data.
This is something I kind of like about the Microsoft APIs. Their error codes aren't always perfect, but at least they give you some indication where things went wrong.
From MSDN:
> An HRESULT value consists of the following fields:
> A 1-bit code indicating severity, where zero represents success and 1 represents failure.
> A 4-bit reserved value.
> An 11-bit code indicating responsibility for the error or warning, also known as a facility code.
> A 16-bit code describing the error or warning.
The "reserved value" also includes a bit for non-Microsoft code (which driver vendors and other API producers can use, although I don't know how often they do)
As a regular user, you will see errors like 0x8ACEF00D, but if you decode them, you can get a sense of what part of the system ran into the failure. Compared to the "negative value indicates failure, look up the possible failures and what they mean for every function" approach many other APIs follow, that's a welcome change.
Of course there's no guarantee that Microsoft doesn't return some kind of meaningless internal E_SOMETHING_WENT_WRONG value, but for a lot of APIs, there are details hidden in plain sight. It won't tell you your ISP's fiber has snapped, but it'll tell you if the problem is within the driver, a security limitation, an HTTP error, or a generic network stack issue.
HRESULTs are well intentioned. They originated in OS/2; but Microsoft always seemed to use them half-heartedly.
Users should not be seeing "08CAE5D012" error messages. They should be seeing "Connection refused. (08CAE5D012)" messages, or "No connection. (83255321)" messages instead.
There's is a non-trivial protocol for translating HRESULTs into error text; but it is often not supported properly by Windows applications, or Windows OS components.
e.g. DirectX which returns generic 0x8004005 E_FAIL errors. Or the WinSock APIs, which return HRESULTs, but don't provide text message translations; or Microsoft Installers that report raw HRESULTs with no attempt to capture the associated error text.
With discipline, the entire system could have worked. But there wasn't, and it didn't.
You're making the assumption that it is all caused by hardware or software malfunction in the sending of an IP packet to the target server. However the first step is usually a DNS lookup. A list would look more like this:
1. DNS lookup fails because the DNS server address configured at the computer OR router is incorrect
2. DNS lookup fails because the configured DNS server is down
3. DNS lookup fails because some firewall is blocking requests to it
4. DNS lookup fails because the network was congested
5. DNS lookup fails because of interference in the Wifi channel from other Wifi networks
6. Etc. Etc.
So indeed, usually when the network is down you only get "DNS lookup failed". Because the actual reason may be complicated. Of course, usually it is due to your computer not being connected to a router so usually the error message hints at that (in layperson's terms: not connected to the internet). So that's why browsers hint at it being your connection, i.e. ethernet or wifi.
But there is no way to make sure. All we know is that DNS seems to fail. The reason it fails could be any part of its configuration which is spread out across physical systems and software components.
The best we can say: probably your ethernet/wifi connection. If we trust that none of the other components are failing then it must be your connection that is failing.
It's like trying to find out what's wrong in 1+2+32+4=10. Sure, it seems like the 32 should be a 3. But maybe the 10 should be a 39. There is no way to tell anymore. All we can do is make an educated guess.
Add a parallel step there, somewhere, for DNS. My raspberry pi runs pihole but the hardware is failing somehow so the server crashes so DNS lookups fail. All existing connections are fine, direct IPs are fine, locally cached results are fine, but new lookups fail. It is somewhat fun to watch it happen.
Windows has a button to diagnose failed internet connections, but it has never reported anything other that the internet connection doesn't work. It's pathetic.
It's similar to when you try to print something, and nothing happens.
1. is the printer turned on?
2. is the printer out of paper or ink?
3. does the cable have a loose connection?
4. is it the wrong printer driver?
5. do you need to reboot windows?
6. do you need to power cycle the printer?
No help anywhere. You just try things one by one, sacrifice a small animal, and then maybe the printer gods will smile on you and the printer will print.
As a Go programmer, how often do you check if fmt.Print returned an error? You get to assume that stdout and stderr exist for a start, even if your code is being run in an environment where that makes no sense. And what is the correct behavior for your software when IO errors start happening when you print? In a dev environment, probably crash. But in a production environment? A customers workstation?
The article isn't so much about swallowing exceptions, but more about designing the system so you don't have to raise exceptions.
It does actually. This approach ensures that old apps (whose authors never thought it would run on something called an Xbox) will seamlessly run and perform all functions properly except for printing, which isn't supported on Xbox. Panicking here would mean every older app has to update their code to support Xbox.
Not at all! It's like porting a program which expects there to be a TCP stack to a system which doesn't have one, and wiring up a component which responds to all HTTP requests with 404 instead of letting it hang on an infinite loop or crash. Say it uses a browser for rendering, but in the original you can also fetch websites, and the assumption is deeply baked into the code.
If your choice is between playing whack-a-mole with all parts of the system which might call out, or just issuing a 404 (after all, if there's no Internet, you're not going to find a web page on it), that's a reasonable way to solve the problem.
Nah I don't think this counts as swallowing errors.
There's nothing wrong or buggy about the printing API on Xbox returning a list of no printers instead of an exception.
It's essentially the Null Object design pattern. Even Go implements this in the way that a zero list can still be accessed an jsut behaves as an empty list.
Or in unix how /dev/null exists, which implements the file API but doesn't do anything. This is way nicer than fixing every UNIX program to handle stdout not existing sometimes.
Think of it like an emulator. The goal of DosBox is to provide Doom with the environment it wants so that it will run. That will necessitate some amount of lying.
This is something that Elixir nailed. The typical idioms for return types are `{:ok, ok_value} | {:error, error_value}`, for which callers can pattern match against and handle appropriately. If fail-fast is desired, many functions will also have a variant signaled by a postfix exclamation mark (e.g., some_function!(...)), that returns `ok_value` or raises an exception.
> This is something that Elixir nailed. The typical idioms for return types are `{:ok, ok_value} | {:error, error_value}`, for which callers can pattern match against and handle appropriately.
Agree this is a good pattern. Im not categorically against throwing errors but I like returning errors or the result and then having to check at the caller. Do this in typescript a lot.
Any typed language could use this pattern, though with Elixir specifically it was idiomized from the very beginning and all libraries use it, which makes error handling very consistent even when piping operations. The common option to fail fast is also handy when crashing the process is preferred and no matching is required to unwrap the value
> The idea here is to have the printing functions all behave in a manner perfectly consistent with printing being fully supported, yet mysteriously there is never a printer to print to.
That kind of thing intuitively sounds bad. But I guess of this is in a space shuttle I would take that back.
I wonder if Go's panic was inspired by Symbian OS User::Panic, an uncatchable sort of exception that was only used in situations when the programmer screwed up. It would kill the entire thread.
Completely disagree. Getting back at the programmer for making a mistake isn't what matters. Presenting the best experience possible to your users is all that matters.
If the user clicks on a print button in an app on the Xbox, and the app crashes (possibly losing some of the user's data), that's a bad experience for the user. Why do that to them just to stick it to the programmer?
And on top of that, now some developer at some company (often not even the person who initially made the bad assumptions about printing) has now been called in on a weekend by their boss to scramble to fix the issue and push out a new release. Why do that to people?
Regardless, also consider the API contract. If the printing API isn't documented to throw any exceptions, and you start doing that, you're breaking the contract. You can't blame the programmer for not considering platforms without printing support; you've documented that API as always returning something without blowing up.
But that's still irrelevant; don't break user code just because you can, or because it's more expedient for you to do so.
Your Go example is completely unrelated; you're talking about making things blow up at testing time, which I agree is the right thing to do. But that's also when you have full control over the code and the testing. If a third-party platform API starts behaving in ways you didn't expect it to behave, that's a whole other thing.
in my experience that's partly true (it's a judgement call and lies on a spectrum). Like a HTTP server should handle panics in handler code so as to not crash the other go routines handling requests. Or maybe a long running queue worker (eg reading off SQS) should recover from a single go routine handling a message (assuming it can recover and handle the next message successfully).
But it'd be way out of scope to put a recover block outside every function call just incase it did a runtime error: index out of range [1] with length . In this way its much different than try catch in languages that explicitly call out what they throw
This is the Microsoft way to do things, this is why their products like Windows are so crappy, full of bugs, unexpected behaviors and a big dump of shit of legacy behaviors expected on top of a lot other legacy behaviors on top of another 1989 legacy behavior that every forgot.
Much of the crappy legacy behavior that Microsoft maintains is the fault of other applications, not themselves.
The classic one is the error code for the file open function. Early DOS would return error codes of only 3, 4, 5, and no more. DOS programs would actually just indirect-jump using the error number as an index into a lookup table of addresses. When Microsoft tried to add any error codes (say 6), the program would jump out into hyperspace since 6 was beyond that lookup table and that memory word could be anything. So Microsoft was stuck folding every possible file open error into code 5, and to this day that's why just about any file error in Windows just says "5 Access Denied". And no, Microsoft couldn't add more error codes and just let the applications break, since then nobody would buy the new operating system versions that their programs wouldn't work on.
No, it's not. Like what is described in the original post, it is they spirit.
For example for the case with the printer and the xbox, they could have an explicit obvious error for the application.
Then it is the task of the application dev to validate its application for target it will run on. If it is does not work, it does not work.
Otherwise, you will still have dozens of bugs and unexpected behaviors, so a crappy application.
And new app developers will have to build upon that. Imagine the next app developer, you have to take into account that a platform pretends to support printers when it is not the case. So you will do things like: if there is a printer api but i don't see a printer available (/connected), pretend that we can't print and don't show the button... and so on...
> When Microsoft tried to add any error codes (say 6), the program would jump out into hyperspace since 6 was beyond that lookup table and that memory word could be anything. So Microsoft was stuck folding every possible file open error into code 5, and to this day that's why just about any file error in Windows just says "5 Access Denied". And no, Microsoft couldn't add more error codes and just let the applications break, since then nobody would buy the new operating system versions that their programs wouldn't work on.
2. There's file related error codes way above 5, eg. ERROR_FILE_EXISTS 80 (0x50)
The general gist of your comment is correct though. Suppose windows didn't have file locks before and they were adding it. Returning an error code like FILE_LOCKED or whatever would be much more descriptive, but would also require all existing code to handle this error case. With that in mind returning ACCESS_DENIED makes perfect sense, even if programs aren't using jump tables.
> Much of the crappy legacy behavior that Microsoft maintains is the fault of other applications, not themselves.
Well. I have two or three (I am not even sure because for one email address there are probably two accounts, but no way to distinguish between them) MS Teams accounts. There is no painless way to switch between them. Currently there are probably at least three different MS Teams apps installed on my machine, one of them self-installed without my consent (my PC is not managed by an org or domain, it's mine). Switching accounts involves several confusing errors, at least three password inputs, and several complaints by MS Teams that something is wrong, but it won't tell me how to fix it.
Use the browser version they said. Well for scheduled meetings it works, but for adhoc calls Firefox does not (there is no reason provided, but at least it does say outright), while Chrome seems to work but does not transmit my voice and camera. All other meeting software, of course, works.
I am not the youngest anymore and this is my lifelong experience with Microsoft. Its software works barely enough to sell to corporations; poor users have to endure it.
People are reacting negatively as expected, but stuff like this is exactly why you can click on a file that was written in Word '97 or a game that was compiled for MS-DOS three decades ago and it opens on your computer exactly as expected. Backwards compatibility is always messy. You either do it imperfectly or don't do it at all.
I hear that refrain often regarding Microsoft, but with games, I have not had good luck. I resort to using GOG, which itself ends up virtualizing the environment anyway. For example, I do not think anyone would have success installing the original Sim City on Windows 11.
Yeah, people frequently trot out the Sim City classic example, but the fact is that games in particular have borne the brunt of Windows API changes. If it didn't happen we'd have no need for wrappers like dgVoodoo: software once only meant to wrap Glide calls, but now used to get around deprecation to DirectX as well.
Not to mention the breaking changes to the audio stack that happened between 9x and Vista.
A file that was written in Word '97 did not even open exactly as expected on two different computers 25 years ago. Formatting depended on the installed printer drivers. Good luck with that document today.
My LaTeX files from 35 years ago, on the other hand - they just do fine.
Good luck uploading your CV in PDF. I tried that a few times and got moaned at a load that my CV must be in Word format. In the end I converted the PDF into images, and created a Word document in OpenOffice (this was a while ago) with one page image per page. Got moaned at for that too.
Heh. A buddy of mine once applied for a Linux job and sent his resume as a PDF. They called him back: "OMG, you're the only applicant who didn't send a Word file. We're not worthy."
Recruiters and large firms tend to ask for a Word file. I sent the plaintext file (minimal markdown) I used to generate the pdf to a recruiter recently. They directly asked for a docx instead.
Recruiters probably edit the file before passing it on to the actual business... Add some more buzzwords, remove direct contact information, obscure exact project details. Some recruiters play quite dirty business and likewise assume the worst of their customers.
That is exactly the complaint I got about my word-document-with-page-images. The recruiter couldn't work out how to edit it. I saw the CV they actually ended up sending to one of the workplaces I interviewed at. They had retyped it, and it looked awful.
> Recruiters and large firms tend to ask for a Word file.
Again, not my experience at all.
I think if a company didn't accept my PDF CV and asked for a Word file instead, I'd strongly reconsider whether I'm actually interested in working there.
They're not plain text, they're a text encoded structured file format. And the file format is stable.
A stable file format is the most important difference here, but the text encoding means that small incompatibilities can be solved by a human rather than needing to rely on an upstream maintainer to recognize and explicitly handle your unique situation.
If I recall correctly, the common phenomenon where you open a Word document, immediately close it, and are asked whether you want to save your changes (???) has something to do with printer settings.
A file from 1997? Maybe if you get lucky, but there's also a good chance that it will simply look wrong in any office application you try. With Word you can't even guarantee that reopening the file on the same computer & version won't change its formatting. That was the reason I learned TeX.
And games? Microsoft broke numerous games by shutting down GFWL. I had to pirate Dark Souls as my original copy wasn't playable until it got re-released on Steam.
Before the new '.docx`, '.xslx', etc. formats, when it was still just .doc, .xsl, etc., the document format was (as I've heard it told) essentially just a memory dump of Word/Excel's state for that document at the time you saved. And since it's easy to imagine that serializing/deserializing such a complex thing might not be always 100% perfectly idempotent, it would indeed happen that just the act of opening a file would change it in some subtle way.
Worse than subtle formatting changes was some kind of corruption that could slowly swallow bits of your document without you noticing. Sometimes going back before the corruption had visible effects was not good enough to stop it from manifesting again.
I remember back in school religiously saving new versions of Word documents so that if the newest version went bad I could start a fresh doc, then copy and paste sections from old versions via Notepad to cleanse the corruption. (Direct Word-to-Word could bring it across — I guess that tells us something about how they implemented copy+paste between documents, too!)
Difficult to prove considering that was a decade ago, but it did happen to me. Randomly changed font in a random paragraph, randomly repositioned images, etc. Maybe I accidentally broke the document in some way, I don't know, but Word certainly failed to restore it to the exact state it was saved in, and it was always on the same PC and software.
I don't know if anything's changed but for a long time a lot of Word's formatting was impacted by the printer you had installed and/or selected. So it was pretty easy to run into scenarios on a network where printers being taken offline/their capabilities changed resulted in documents getting reformatted.
I had the same with OpenOffice (before LibreOffice was a thing). I created a nice document while logged in remotely to a Linux computer over Xvnc. When I opened it on the native terminal, the layout was different. Same computer, same version of OpenOffice, different X server.
I wish this was actually truthful. I’ve had a gigantic headache getting every childhood game I kept working. In almost every case I gave up or re-bought from GOG, basically paying for the work done to sort out compatibility.
I think the reason this is getting a negative reaction is because he doesn't really clearly state the problem he's actually trying to solve until the end (and describes it as "bonus chatter" when it's actually just a much better explanation than he gives at the top of the article).
> To clear up some confusion: The idea here is that the printing API has always existed on desktop, where printing is supported, and the “get me the list of printers” function is documented not to throw an exception. If you want to port the printing API to Xbox, how do you do it in a way that allows existing desktop apps to continue to run on Xbox? The inert behavior is completely truthful: There are no printers on an Xbox. Nobody expects the answer to the question, “How many printers are there?” to be “How dare you ask me such a thing!”
If he just used the words "existing desktop apps" in the second paragraph of the article instead of the second to last, I don't think people would be reacting so negatively. Instead, when he talks about avoiding "gaslighting" by returning an invalid pointer by just gaslighting in a different way by lying about the error that occurred, it almost sounds unhinged.
Just flipping thru it I'm not seeing obvious issues. In terms of being usable for reference I think it's reasonable. The diagrams and tables look intelligible and not garbled. Layout isn't visible screwed-up.
Will it be a 1-to-1 with printed output from 1996? Probably not.
"Converting this document from its original format was a bit of a victory for open source software. And a lesson in how hard document preservation is."
So, opening this document was hard and that it finally worked was not because of Microsoft's alleged famous backwards compatibility- which is a myth in my book.
I doubt it because, as I said, Word did not even render the document the same on two different installations back in the day. Contrary to LaTeX, there is really no way to know what "opens on your computer exactly as expected" even means. We do not know what the original author intended it to look like, because we do not know how the document looked like to them.
As long as there is no retroactive interpretation, like it happened with RGB, for example, the "doubt it" will stand.
I assume the negative reactions are due to the large Apple user base on HN. Most of these people are likely so used to being told "this software was built for a version of your OS released 2 years ago so you can't use it anymore, tough shit", they've come to expect it.
You just created a scenario in your head that confirmed an existing bias in your head and then assumed that your made up scenario matched reality. Very weird to read.
nah, it's because all of this is just lip service. windows11 is getting worse and worse.
and cool that Mr Chen thinks about UX and compatibility, but MS as whole does not really. important business apps got special treatment, and that's it. (MS and its corporate entourage solved this mostly by providing Windows and "security" patches for it, for a lot of money.)
I find nothing quite as frustrating as UIs that suggest a device could exist, but it's not there right now. I then have to spend time to discover that these devices are not supported, and that screen was just some mock someone came up with.
Sure, but you'd be more frustrated if your app just crashed.
If the app is not prepared for printing not being supported, and printing just throws an exception, the app probably just crashes. That's bad.
If the app is prepared for printing not being supported, it should be calling the explicit API to check if printing is supported, and then not displaying the UI for printing if it isn't. The article is not about these apps, it's about what to do about apps that fail to check first.
The bug is with the app, but a good platform does its best to make bad apps work anyway, rather than have them just crash.
first of all, i certainly wouldn't be more frustrated if the app just crashed, but how about just letting the user know that the xbox can't print? why does everybody seem to think that's out of the question?
> but how about just letting the user know that the xbox can't print? why does everybody seem to think that's out of the question?
Because the situation described is about an application that was made for PC (which supports printing) and a user is trying to run it on an Xbox (which doesn't support printing) with the developer never even imagining someone will try to run the program on an Xbox in the first place. You can't implement a message about an error you don't even know will happen.
The Xbox can (and probably can do that while simulating the "user pressed cancel" dialog API for adding printers) but my understanding for what
yungporko wrote was that the program would be the thing that lets the user know the xbox can't print.
The original article is about how the Xbox implementation of the Windows printing subsystem should behave, from one of its designers. They considered making it throw an Exception, but decided to instead behave as if printing is supported but there are no printers, so that Windows apps would not crash.
My point was that, as an end user of such apps on Xbox, my preference would have been to have the Xbox OS tell me directly that printing is not supported on this system, even if the app doesn't know about this.
I do not have an Xbox to try but i do not see how these two are mutually exclusive. If you try to use an app to print you wont be able to do that because there are no printers. If you try to add a printer using the Windows API for adding printers (which AFAICT from the article is done via a GUI that Windows itself provides) then the Xbox OS could show an error message that Xbox does not have printer support and once you close it, have the API call the application made tell the application that the user cancelled the request (which is something, according to article, it already does - though i don't have an Xbox to tell if it displays an error message or silently returns the cancel code).
Xbox cannot tell you that there is no printer support before you try to do something related to printing because it doesn't know you may want that. An application that was made for Windows also can't do that because on Windows you can add printers (even fake ones that print to PDFs, no hardware needed).
An application asks the OS for the list of printers. The question is what should an OS with no support for printing do. The article says it should return an empty list. My point is that it should pop up a notification directly to the user telling them "hey, the app you're using is trying to access printing, but that's not supported on this machine" or some other language like this (it could then return an empty list).
Trying to enumerate printers doesn't imply that the application will use them, it could easily be part of the initialization of some framework, shared library, language runtime, or whatever. Unless there is some actual user interaction, there is no way for the OS to know what the user is trying to do. It is best to err on the side of not making assumptions.
To me, a better solution would be for the system to pop up a notification informing the user that printing is not supported on this platform, and then whatever other solution is OK.
> To me, a better solution would be for the system to pop up a notification informing the user that printing is not supported on this platform
That's the whole point of this article - when you can't control the various platforms that your application is run on, you want to try to do the "least bad" thing, even if that platform didn't anticipate the situation of, in this case, telling people that printing isn't supported.
The article is the other way around -- it's written from the perspective of a platform developer trying to deal with apps that didn't anticipate the situation.
Thanks for the clarification, you're correct. The nuance/complexity is that the platform developer must conform to a platform spec (in this case, the Windows API) that usually makes some underlying assumptions about the capabilities of the system that runs it, which may not always be correct.
Yes, I'd add to that by pointing out that the "spec" is a de facto spec subject to Hyrum's Law, that is, people have built their apps against other implementations of the platform and inevitably don't handle any behavior not seen on those other implementations.
I waffle whether I agree with you. On the one hand, it makes sense to tell the user that clicking that juicy print button isn't going to work. On the other, it's a popup they'll have to dismiss telling them some recoverable error they cannot do anything to fix.
The Xbox OS can very well pop up a system notification directly to the user if an unsupported API is being accessed. This is not Unix where you have to assume no UI may even exist - the Xbox OS knows full well how to draw its own dialog on the screen.
Perhaps the software author is to blame here then not Microsoft. Because they didnt handle the printer case. Where Microsoft comes in is that the software doesnt crash because the software provider didnt account for printers.
The specific thing the author is suggesting is correct (components should suffer before users do), but I take great offense to their framing. "There may be times where you need to make an API do nothing." "The wrong thing to do is to have the printing functions throw a NotSupportedException." No, no, absolutely no - what the author is describing is a hack to support a shitty client. Yes, you have to do this sometimes. No, it is not normal or generalizable advice.
The way they've worded this betrays the internalization of their suffering as a MS developer.
No, not a shitty client, just one that was written before you decided to make this change. If your API breaks it while it doesn't change, it's the API that's being shitty, not the client.
> No, not a shitty client, just one that was written before you decided to make this change.
Has the Xbox ever supported printing? Why even have these APIs? Just don’t offer them at all in the Xbox SDK. You shouldn’t be able to compile code that tries to call these when targeting Xbox.
But they are essentially removing parts of the Windows API, that’s what the entire article is about. Wouldn’t it make more sense AND be a lot less work to simply remove these APIs from the header files than to provide a stub implementation?
What existing apps could you possibly want to run on a game console without at least a few modifications to make it suitable to run with the completely different control scheme and user expectations of a console.
It seems like an enormous amount of effort for an extreme edge case. It’s this weird obsession Microsoft has with the flawed idea that you should be able to run anything on every device without modifications. It’s their unwillingness to acknowledge that different types of devices require different user experiences.
It depends. Did the API document that it could raise an exception if printing isn't supported? Is that unhandled in the client? Then yes, it's a shitty client.
If you're talking about making backwards-incompatible breaking changes to an API, that's another thing.
In the context of the article, no, the existing API never threw (or was documented as capable of throwing) NotSupportedException.
The article:
> The app that the user installed on the Xbox was probably tested primarily, if not exclusively, on a PC, where printing is always available.
I.e. there is no concept in the (desktop) Windows printing subsystem of printing as a feature not being available; on desktop Windows, printing as a feature is available by definition.
I think this is the critical piece that should determine how you handle the error. If the API, as used by the client, when the client was programmed, was expected to potentially throw NotSupportedException or return an error code, then doing so sounds like the right way to go. Throwing/erroring is part of the API contract and the client should handle NotSupportedException.
If the API was never expected to throw NotSupportedException or return an error code, then all of a sudden starting to throw in a future release breaks the API contract, and the client cannot be expected to handle it sensibly.
EDIT: Duh! My comment is useless. This was the exact conclusion that the article came to, too. So instead of reading this comment chain, just finish the article :)
The mention of NotSupportedException is a little confusing because that's a .NET thing, and the rest of the article is talking about Win32, which is a plain old C API where exceptions don't exist. I guess he's implicitly talking about a C# API built on top of Win32.
It's entirely correct to return error codes, and any client is expected to handle that.
Clients are not expected/required, however, to behave correctly in the face of unknown error codes that did not exist or have defined semantics at SDK-version-selection time.
Actual exceptions — in languages that have them — are a bit different, in that they “do” something by default: they propagate and unwind unless they’re caught. And so, new (unchecked, if in a language where that’s relevant) exceptions can be introduced after the fact under the presumption that the runtime’s default behavior for propagating unknown exceptions will kick in, and that this will trigger the desired result for applications that weren’t written in awareness of that particular exception.
Error codes aren’t like that; they have no default semantics as a class, only explicit default semantics per function and/or per algebraic error-code type, if-and-where documented as such. New error codes simply aren’t meant to be introduced in most APIs, unless those APIs have rules about e.g. generalized default handling for defined code ranges that new codes can be later slotted into.
Basically, error codes are weak enums. If you’re writing a dynamic-link library (or a runtime — same thing, basically), you’re not supposed to add new potential values to an enum used as a return value: client code will likely have been written to switch on the value with the known options as cases, and your new value won’t be in there.
> I guess he's implicitly talking about a C# API built on top of Win32.
Correct. The error `ERROR_CANCELLED` is defined in WinErr.h and it's translated via `HRESULT_FROM_WIN32` in the same[1]. It was this change pointed out in `CreateWidget` to get around returning the null pointer that he was suggesting would be better to make the API inert.
If you are building a software platform which has been adopted by more than a handful of developers then you're going to have some shitty apps, in which case you have to do this stuff. Yes, it is completely normal and generalizable to all platforms that have wide adoption and take backwards compatibility at all seriously.
Raymond is behind arguably the foremost exemplar of this (Windows) but the same thing happens in the Linux kernel ABI, glibc, the web platform, Java, and so on.
The issue is that sometimes you need to implement an API which existing clients are already using. You can't go back in time and re-compile, much less re-write the clients.
It all depends on how the clients are currently using the API. If the clients are already testing for NotSupportedException, then that's what you should return. But if not, you need a different approach.
well, that seems like an important piece of the contextual puzzle, because the Xbox printing example by itself seems extremely dumb. (issuing/publishing games on Xbox is not a wild wild west of unknown binaries, if someone cannot get the source code for their thing they are very unlikely to get their thing on Xbox. yes, there are probably binary blobs, but still, to me in this context it's ridiculous that instead of a an exception handling wrapper on the application side the platform does these fakes.)
> No, no, absolutely no - what the author is describing is a hack to support a shitty client.
If you were on the Windows team and I was your manager, I might fire you for that.
This is Windows. You do not break applications that users depend on. Ever, ever, ever. If you have a "shitty" or misbehaving client application, you use whatever workarounds, shims, and compatibility hacks it takes to make the application work as expected. Even if it means having special memory management code for SimCity so it will work.
I really appreciate this approach. It actually takes, in my humble opinion, much more discipline (and requires checking ego at the door) to implement this idea, compared to the “my code just throws exceptions or a 400 Bad Request anytime you aren’t following the most recent revision of our API” mindset. I work on Web apps which are much closer to that second approach usually, but if I were working on a platform (NT) which was 30 years old and will live another 30 years in all likelihood, you 100% have to take the approaches Raymond Chen is explaining.
“We’ve decided software that operates your 100 ten-million-dollar-each CNC machines is ‘shitty,’ so buy new equipment so you can get Windows 11” isn’t going to fly, it would get the whole company laughed out of the room.
Especially because what is allegedly making them shitty would just be someone’s failure to predict some minor detail about the future of the platform. APIs do have to change, and the creative part is thinking of how they can be safely rendered inoperable without damaging anything that didn’t predict that removal. Sure, you can’t print, but that’s in this example a deliberate design decision of the platform, probably for good reasons.
I also took issue with the Xbox example, and I think it's a context issue. When I read it, I interpreted it as an API for new apps/games to use. In that case, they should be testing on an actual Xbox before shipping! And why would the app be trying to print in the first place if it's designed to run on an Xbox?
But I suspect the situation this is being designed for is existing PC apps being ported to an Xbox. OK, in that case I can see fudging the API since there might be obscure corners that the devs didn't think about that try to print. But I'd still say the devs should be testing on an Xbox and make an effort not to attempt to do things when it doesn't make sense for the platform.
Just to add further context; this isn't apps being ported to an Xbox, they notionally need no porting; the Xbox is a "Windows" system. The only actor in this scenario is the platform developer figuring out how an existing API should behave if called in a context the authors very much didn't expect (who expects their Windows app to be run on an Xbox?)
I guess I'm confused then. Is the expectation that you can just run any Windows app on an Xbox, unmodified? I would think you'd have to at least change the UI
The "shitty client" was written long ago by a company that is out of business. YOUR customers rely on that client, and if it stops working because of a change you made they will blame you.
Broadly speaking I agree with you in principle - but in practice over time you come to realise that there are only shitty clients. They might not intend to be, they might not have started out that way, but time is a destructive thing...
> The way they've worded this betrays the internalization of their suffering as a MS developer.
While I feel less strongly about this Xbox/printing example, I remember Bill Gates saying "I reboot my computer every day" which is a similar mindset-- this culture has been forced to adopt a certain form of "hygiene" due to that same culture NOT adopting hygiene preemptively when they built their systems.
Everyone here is hung up on doing "nothing" and handling "errors" and I feel like the post does a poor job explaining how it is not just swallowing everything that's going wrong and going kumbaya. The actual context is that the author is doing emulation/compatibility for software that will not change, and that's very different from most contexts. This is especially confusing because Microsoft often blurs the line between the two themselves, and that's not actually what is being talked about here at all.
Really, emulation is all about telling lies. Your Xbox doesn't actually have a way to attach a printer. You're running a game on Linux, not Windows. Your fake iPhone is actually a ARM server in AWS. The point here is that if you're going to lie you better do so convincingly. That's because you need existing applications to still work! When Microsoft says you can port your existing Windows app to Xbox the promise for the Windows app was that it would be able to print, and so Xbox needs to lie for that to happen.
This is very different from a normal error. If a program does a use after free, and you silently pretend the bug doesn't exist by working around it, then you're not doing anything correctly. That's because nobody promises that your existing use after frees will be bug-for-bug compatible on a new platform.
> There may be times where you need to make an API do nothing. It’s important to have it do nothing in the correct way.
> For example, Windows has an extensive printing infrastructure. But that infrastructure does not exist on Xbox. What should happen if an app tries to print on an Xbox?
It says nowhere that the context is doing emulation for software that will not change. You just assume that because it supports the position you like. In fact, everything written applies to a program that I might write tomorrow for an XBox, and try to print something. There is nothing that designates this behavior only for old, unmaintained software.
If you write a program tomorrow, use the API correctly and this issue will not apply to you. This is for apps that aren't well written and don't check for the capabilities ahead of time.
Nothing Chen wrote motivates me to use the API correctly, that's the whole point. "Just don't do the bad thing" is naive. People will use APIs incorrectly, despite your admonition.
> "Just don't do the bad thing" is naive. People will use APIs incorrectly
That is the whole point! You don't have a choice for software that will not change - all you can do is try to keep it working anyway. Like they did here.
That you are not "motivated" to do things correctly is not a convincing argument otherwise.
If you don't want this behavior in your own apps, you don't have to invoke it. Using the API correctly sidesteps all of this but is not a choice when "doing emulation for software that will not change."
I generally advocate for things crashing early, for reference. I just happen to work on emulators and compatibility shims a lot, much like what Microsoft is offering here.
I love and hate this. On a visceral level I don't like dealing with issues via malicious compliance.
OTOH I absolutely agree this is a good call if your goal is for more users to be able to run more software on your platform, even if printing is broken.
What a weird thread of comments this was. There are clearly people who just don't like anything Microsoft does.
The point of the blog is to make the process of bringing apps (UWP) to XBOX be frictionless: no recompile required, no messy ifdefs, none of that extra work. The App would "just work". Then, it's up to the developers to start adjusting the app to fit the XBOX platform more. The important thing here is that the developer was able to bring the app with no effort to XBOX. This is good for both the platform and the developer.
To the user, it's a clear signal that they can't print since the list of printers are empty. But most importantly for them, the app did not crash or show the most hated error: "oops something went wrong. Try again later."
Is that message a better experience for the user? No.
Sure, the developers can recompile the app and add a more descriptive message but then we're back to square one.
The point the critics of this piece seem to be missing is that there is not an exceptional case here that needs an error to be thrown. There are NO printers attached to the device (in this case by definition), and an app should cleanly handle that case, not crash. You wouldn’t throw an exception on a laptop just because it couldn’t reach the printer.
If you want a clear, modern example of a popular non-Microsoft product doing printer availability wrong, it's gnome-control-center.
Add a printer in Settings. While the printer is installing, click over to the "Color Profiles" tab. If you time it right, gnome-control-center will crash. If you dive into the details, you see that it was trying to enumerate the available printers (it knows one should be there) but that info doesn't exist yet. So it just crashes.
Thankfully, the fix is to just wait a few seconds for GNOME to finish installing the printer and restart Settings. Still, it's the principal app responsible for making your desktop work correctly as an end user with some unknown computing background. In a perfect world, this app should never crash.
It's hard to think about edge cases. It's even harder to imagine your handling of edge cases has its own edge cases. Right or wrong, at least the author is thinking deeper than "bad app, needs to be rewritten to support Wayland"
Once upon a time it was considered a great strategy for browsers to make the best effort to display a page, even if the html code had errors. Just try to guess the author's intention as well as you can, and go ahead. Errors are bad. Users don't want errors.
I thought we learned from that experience. Apparently not.
Browsers still follow that strategy. The effort to replace it with strict validation (XHTML) failed. What succeeded instead was HTML5, which went back and explicitly defined how every possible invalid sequence should be interpreted, so that browsers would at least be consistent about it.
Browsers being permissive with what they accept is what made the modern web possible.
If early browsers displayed an error the first time it came across an <img> tag, instead of just best-effort rendering the page, then every new browser feature would have been smothered in the cradle.
And see the errors that now show up because of the wisdom we gained… wait, it still renders alright? I guess we still have much to learn then. Or, there might be something to gracefully handling certain classes of errors while crashing on exceptional cases.
That every single behavior that attempts to guess the users's intent becomes a relied upon part of the software, and this over time grows to an impossible amount of cruft.
> And then Google built Chrome, and Chrome used Webkit, and it was like Safari, and wanted pages built for Safari, and so pretended to be Safari. And thus Chrome used WebKit, and pretended to be Safari, and WebKit pretended to be KHTML, and KHTML pretended to be Gecko, and all browsers pretended to be Mozilla, and Chrome called itself Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13, and the user agent string was a complete mess, and near useless, and everyone pretended to be everyone else, and confusion abounded.
Meh. For all the silliness, it’s not that hard to parse one for the main purpose it should be used for, which is determining in aggregate what platforms your users use. As such, we still need things like OS in there anyway. So it wouldn’t get quite that short.
Also I’m amused checking mine that the user agents still say “Intel Mac OS X” even on Apple silicon. I guess they were afraid someone’s parser would think we are on PowerPC without that!
Wouldn't it be simpler to just create a "Save to PDF" printer and use that in the Xbox?
This way the API stays compatibile, and it actually makes it useful
The only reason an X-box doesn't support printing is because Microsoft has defined it as such. A x-box is just as capable to print as the Windows machine in the next room over from a hardware perspective.
Which means this "solution" to a stupid, self-created problem is stupid. I bet this workflow will lead at least a handful of people to ask "How do I add a printer to my X-box?" instead of "Fuck the app crashed, better not do that again."
Microsoft has so much stupid, corporate driven decision making. Can't wait for it to follow in IBM's footsteps.
This seems like a long way to go for “we know that this device is an Xbox and Xbox doesn’t support printing, let’s tell the user”
Only it doesn’t tell the user. It leads them down a path implying implementation is possible.
On the other hand, I now know why so many issues I’ve troubleshooted on MS products end in tears and complete confusion as to why they wouldn’t just say “this is not a feature you can use on this device.”
> This seems like a long way to go for “we know that this device is an Xbox and Xbox doesn’t support printing, let’s tell the user”
You're missing the fact that Xbox can run apps developed for (desktop) Windows, and that the scenario in the article is about the platform <-> end user relationship and does not involve the app developer at all.
I agree. The author's description of what is "reasonable" sounds like a blueprint for many of my late nights struggling with Windows:
"apps that assume that printing works will still behave in a reasonable manner: You’re just on a system that doesn’t have any printers and all attempts to install a printer are ineffective."
Maybe the underlying assumption is that the users will give up before spending an "unreasonable" amount of time on these tar pit features.
Microsoft’s MDM APIs will return a 500 (server error) on a VM if you try to get a list of wireless networks, instead of the saner approach of returning an empty list.
The same APIs will return a 418 (I’m a teapot) if you mistakenly try to add an already-existing setting, instead of just updating it as one might expect. They get points for originality, but don’t follow the advice in this article.
Microsoft has around 238000 employees according to Wikipedia. That they are not all strictly following Raymond Chen's latest blog post isn't surprising.
Hmmm I see and appreciate the idea behind this. That said, it seems confusing for the user to be shown an empty list of printers if no printers can be installed on the platform. In my opinion, it would be preferable to show the user a dialog that says "This platform does not support printing" and, once they dismiss the dialog, inform the program that the user cancelled the print action.
I agree, using the empty list to communicate to the user the situation is making an assumption about the user. A lot of users have never set up or dealt with printers ever. I can say for sure that an empty list of printers would confuse a lot of people in my household.
Exception safety guarantees have been around for decades and were originally defined by David Abrahams who was on the C++ standards board.
As the author is a Microsoft employee I'm a little surprised no attribution or reference to the original author of (the levels of) exception safety guarantees which his article partly describes.
> Well, the wrong thing to do is to have the printing functions throw a NotSupportedException. The app that the user installed on the Xbox was probably tested primarily, if not exclusively, on a PC, where printing is always available. When run on an Xbox, the exception will probably go unhandled, and the app will crash.
Exceptions are such a catastrophically bad idea in almost all cases.
It is absolutely infuriating as a programmer to call a function and not know if it can throw and, if it can, not know what it can throw. It’s a disaster.
I so desperately wish that C++ had Rust’s Result type and pattern matching. std::optional and std::expected are kinda sorta ok, but you really want compiler enforced pattern matching.
Granted, it may not have the annotation but still not throw in reality, but a Rust function can also declare a Result return type but never actually return an error.
> if it can, not know what it can throw
How do you square that with backward compatibility?
If you declare what you can throw, what do you or any of your transitive dependencies do when they want to change their implementation in a way that makes a new error a possibility? Either you shove it all into an UNKNOWN error that you hopefully declared from the get go to be future-proof, or you break all your downstream users every time. So either no compatibility, or over time your API converges to a meaningless UNKNOWN error code for almost everything and a bunch of legacy error codes that are never used.
I’ve never, ever seen a codebase that reliably used noexcept.
boost and Python are my arch-nemesi because they make heavy use of exception errors and it makes the APIs an absolutely miserable nightmare to use.
> How do you square that with backward compatibility?
Huh? The same way normal code handles changing function arguments or return types? I have absolutely never relied on exceptions to “future proof” return error types! Yikes.
The only thing I have ever used exceptions for is to escape bad input/data. It gets trapped internally in the “private” API and same result types are returned through the public API.
I would much rather have a return type of Result<T, Exception const*> than T but also maybe it throws or maybe not and you almost definitely forgot to check which is why people do filthy filthy hacks like the OP article.
> It is absolutely infuriating as a programmer to call a function and not know if it can throw and, if it can, not know what it can throw. It’s a disaster.
Every nontrivial function can fail (in C even a unary operation on an int), it's just a question of whether and how the caller is informed, how easily the failure is to ignore and continue processing in an undefined corrupted state, and what percentage of the code ends up being dead branches for impossible-but-unprovable error paths.
> I so desperately wish that C++ had Rust’s Result type and pattern matching.
I'm a fan of Rust too, but what do you think panic() is?
Rust has no concept of code that always succeeds. It's all-but impossible on current CPUs.
I wish the concept of dev mode and prod mode were baked deeper into all language runtimes/libraries.
e.g. before going to production with real users these cases should error as hard as possible, including potentially logging crashing the program to make the program errors super visible. Then in production it should largely log these things in error logs and keep chugging along.
And not like an environment variable with conditions. Something as first class as changing the standard exception and fatal calls behaviors.
This is also like Null Object or Special Case pattern. It simplifies error handling, and because error handling code is often poorly tested it ends up being a big source of catastrophic failures. Making illegal states unrepresentable, or as John Ousterhout puts it: define errors out of existence: https://wiki.tcl-lang.org/page/Define+Errors+Out+of+Existenc...
It's an x64 CPU running most of Windows with plentiful and fully supported USB ports. Why CAN'T I print from my Xbox? I can print with my phone, so why not my Xbox?
I'm sure there's something about "windows print subsystems are pretty terrible and we should excise them from anything we can" and "reduce code to reduce bugs" but like.... There's no technical reason it can't
You go to print a document. Your printer is temporarily unavailable. The print job hits the spooler and sits in a queue. You get bored and launch a game. The printer becomes available. The print driver has to convert the job to PCL or whatever proprietary format the printer uses. Your game hangs or even worse crashes because it's not really designed to run with background tasks on the same cores.
Who do you blame, especially since the usual diagnostic tools of a PC with a full operating system aren't available?
It's a feature very few people will use with lots of sharp edges that people accept on a PC but would find intolerable on a video game console.
It's not running "most of Windows". In particular, as far as I know (no insider knowledge), it's not running any of the Win32 APIs (USER, etc.). So that rules out any printers which require coordination with their desktop app (which, to be honest, to get full functionality is still a shockingly high number of them). It's highly unlikely the XBox hypervisor implements any of the printing APIs, even UWP because this is a video game console, and not only does almost no one own a networked printer, an even smaller group of essentially no one has a printer, opens documents on their video game console, and wants to print them.
Features aren't free. What Xbox feature would you drop to have an engineer go implement printing APIs on them? Is that the best use of their time?
Due to the input device most people will use (controller), apps that work best on Xbox are those used to consume content like photos or videos.
You might be viewing something and want to print it, but that's clearly not desirable enough for Microsoft to build print functionality into the Xbox UI.
I mean, it has USB ports. If I can plug it in, I'd like to be able to use it _somehow_ , even if that means installing another app that does direct port/device access or w/e . ie, it should be possible (but not necessarily super easy)
The explicit point of video game consoles is to be a locked down experience, not a PC. Hardware access is generally used for piracy, which game developers do not like.
There are widely used practices for API design and versioning that work well today. While this article's kludgy approach to backward compatibility may have been passable a few decades ago, this is a terrible way to approach the problem in 2024, specially with services & APIs being deployed & distributed across platforms.
> the function for installing a printer can return immediately with a result code that means “The user cancelled the operation.”
I hope this is never shown to the user, because one of the most infuriating things that can happen is when the computer tells me that I did something I actually didn't.
It would be unlikely that it would be, since to any properly implemented app, the fact that the user cancelled is not an error since it thinks you did it yourself on purpose.
On the other hand, I recall a few years ago (in the days of one of Apple’s least stable Mac OS [X] releases) seeing the message frequently after a random kernel panic “You restarted your computer because of a problem.” And I would be pretty ticked, saying “Nope! YOU restarted yourself. 0% of this was me. I was sitting here and you just died and rebooted.”
They changed it to use passive voice instead, a year or two after that!
Or more broadly, don't do something unbidden on my behalf then complain when it doesn't work. I swear 75% of the times I've daydreamed about a return to a single-tasking OS like DOS was on account of software trying to 'help' me.
This starts with an Xbox not being able to print, while being able to run computer applications. The correct solution is not to have that. There is no graceful way to recover otherwise, no matter how "inert" you are.
No, it's not - that's the point of the Universal Windows Platform - compile once, put in store, run everywhere.
This also isn't just about new platforms. If you have a desktop operating system and want to change the behavior of an API in a future version, you have to do this same process.
As a Platform Engineer who has to work with Azure on a daily basis, I am so tired of their APIs and services making exceptions left and right, sometimes for their own shitty clients. The result is a Patchwork product suite, services never work as expected but deviate from their documentation (or rather what they are claiming is their documentation).
> The idea here is to have the printing functions all behave in a manner perfectly consistent with printing being fully supported, yet mysteriously there is never a printer to print to.
What? Why would you ever do this? This post screams of "can't see the forest from the trees"
Sure, now you don't have your app crashing, and instead you have this weird state of seeming like you can print but ultimately can't, wasting users time and perhaps frustrating them even more.
Why not literally just show a dialog like "printing is not supported from an Xbox"? Easy to develop, easy to understand... everyone wins.
Windows headers like uppercase. They also like very explicit types, as far as the language accepts them, of course. For example, HRESULT really is just a 32 bit integer with special rules (top bits set subsystem that cause the error, bottom bits set error code, hence the conversion function for best practice).
Stripping out the Windows types and compile-time validation and the wrappers, and picking non-Windows error codes, you can turn the code into more Unix-like C++:
However, this code may not function if you're building for 32-bit Windows and it may not work on every compiler; it just assumes certain bit sizes that the API only guarantees in the form of typedefs.
What's crazy is that I didn't even realize it until you pointed it out. I've been doing Win32 for so long it's like reading the green symbols in The Matrix. This all looks "normal" to me.
Sometimes this kind of error handling is the cause of program slowdowns. So the API fails, but the program retries anyway, and so fails again endlessly. The user doesn't see anything from this happening, only that "the app is slow". Windows is plagued by this kind of behaviour. Its both what the article suggests can cause this, but throwing and ignoring exceptions can have this effect too.
The goal, IMO, would be to force the app not to try (or not to try once it failed) something that is bound to fail.
"The idea here is to have the printing functions all behave in a manner perfectly consistent with printing being fully supported, yet mysteriously there is never a printer to print to."
this is exactly the kind of stupid shit that makes me hate using anything from microsoft. on what planet is this desirable behaviour? is it beyond microsoft's capability to just show a message saying "Printing is not available on Xbox"?
How about an app originally designed for iPhone but is running on an iPad? iPhones can make calls, but iPads can't. Sure, a properly designed iPad app wouldn't try to use the phone functionality, but what if the developer is too lazy to develop a dedicated iPad app? Surely a crappy iPhone app running on iPad is better than no app at all?
There are plenty of apps already in the store. Apple wants to launch their new device and have tons of apps available. The best way to do that is for the apps to just run, not to require millions of developers (plenty of which no longer exist) to rebuild.
There are many forms of "crappy". Developer hygiene is one bar; user functionality is another.
"Crappy" here refers entirely to developer hygiene, which the user does not care about at all.
If the platform changed and Angry Birds crashed, the customer would not accept "but look at all of these other Angry Birds clone games still available" as an answer, they want the app they are familiar with.
The scenario is an app being run on a platform that doesn't meet its declared system requirements.
If an app requires Windows, and customer tries to run it instead on Xbox OS, then he should be surprised it even launches. He should not be surprised if it crashes on print.
> customer would not accept "but look at all of these other Angry Birds clone games still available" as an answer, they want the app they are familiar with.
Then he'll have to run that app on the OS it requires. He's free to sent to MS his complaint that Xbox does not run that OS.
When you have a platform, apps aren't always being rebuilt for it.
There is no "build" because the source code was lost when all of the company's assets were sold off in 2002.
There is no "build" because the developer made a game, put it on your app store, moved on to the next game and has no interest in supporting this game that isn't providing any significant new revenue.
There is no "build" because this is FooManager 6.0, the company that makes FooManger wants everyone to buy FooManager 7.0 and isn't making any further changes to 6.0.
It's an option, but it has its own downsides. It's a cross-platform API.
Someone wants to show some guests some of their photos and videos on their TV through their Xbox. Unfortunately they can't because their app (eg Samsung Gallery or iCloud or whatever) included a print button somewhere.
And with these inert functions, if Microsoft one day adds printing support to the Xbox, it'll just work in the photo app without any update.
Because then you're making every developer make a special build for every device. Most developers aren't going to care enough to make a dedicated Xbox or tablet build. You're just going to make the platform completely dead. It would be much better if all pre-existing apps could mostly run on them.
"The idea here is to have the printing functions all behave in a manner perfectly consistent with printing being fully supported, yet mysteriously there is never a printer to print to."
This must be satire. Otherwise I can't comprehend how something as infuriating as this could be presented as a good or smart thing to do UX-wise
No, this is a guy who has been one of the architects of Windows for more than 30 years giving you hints about how they achieved their reputation for amazing backwards compatibility. He really, really knows what he's talking about.
No it's not satire. It's a demonstration of the excellence in backward compatibility and cross-compatibility that Microsoft has always been known for, and that so-called modern players like Google and Apple don't take seriously.
Just because an XBox can't print doesn't mean that an app that prints shouldn't run on XBox. It should just mean that the printing function shouldn't run. How to achieve that is exactly what Raymond Chen, a Microsoft and Windows veteran, is trying to explain here.
If the two options are "this app can't run on your Xbox" or "this app runs on your Xbox, but the printing functions don't work and some of the messages are a little weird", which option do you think users would prefer?
So what, you crash the app? What if there's no API to say "there's no printing on this device"? What is the difference between a device where printing isn't supported and a device where a printer isn't set up yet? Showing an empty list of printers isn't a bad solution, frankly.
Perhaps you missed the context in which it was made clear that this imperfect solution was still less bad than all other options.
It seems the point resembled: 'this device doesn't support printing, don't return obscure, unhelpful or crashing errors; return something that makes sense so that the user can figure out and move on'. I.e.: Fail gracefully.
I wouldn't call creating mystery failing gracefully. As a user I would prefer a full crash to the application gas lighting me by suggesting there's something wrong with my setup (we can't find your printer vs you're not allowed to print).
If anything creating mystery about what the app is doing is the direct opposite of good UX.
User spends two hours creating content. User hits print.
1. App crashes back to the desktop/Home Screen/etc
2. App can’t find any printers. User cancels and saves the document, prints elsewhere.
You really think option 1 is better?
Also, none of this precludes the ability to display a message. He’s talking about the system calls themselves and how they should respond. I suppose if the call for the “Add Printer Wizard” is called, it could not only return the “user cancelled it” status but also display an error message. It depends on the implementation details and whether someone probably 20 years ago foresaw the need for a modal but not fatal system error message to be triggered by that API call. Which again is not in the today developer’s control.
It might return to the main loop if you throw a NoPrintingOnXboxException… if and only if the author of the app has decided to catch a broad enough exception in that exact place in the code, and handles it gracefully. Good practice to always handle any exception that might be thrown, but it’s not always obvious in the past where there might be a chance for an exception to be thrown. For instance, the printing subsystem itself can’t be missing, can it??
So anyway, the platform vendor can’t be sure the above situation is perfectly handled by every app, so the entire point of the article is “given you must technically comply with the published API specifications, how can you construct the least harmful and least disruptive response to every request sent to this fully-removed system component.”
This whole exercise is basically constructed in order to usually guarantee the outcome you’re suggesting: to cause the app to just return to its normal functioning without doing the impossible thing.
Because "I can't find any printers" suggests that printing is possible if only you set up your printer correctly, rather than suggesting the print function doesn't work.
By showing that no printers can be found the app is misleading the user to think there's an issue external to the app that they need to solve themselves.
The idea is to accommodate apps that just didn't handle the exception, or handled the exception by crashing, probably because they only tested the app on a PC, where printing was always available.
> Well, the wrong thing to do is to have the printing functions throw a NotSupportedException. The app that the user installed on the Xbox was probably tested primarily, if not exclusively, on a PC, where printing is always available. When run on an Xbox, the exception will probably go unhandled, and the app will crash. Even if the app tried to catch the exception, it would probably display a message like “Oops. That went badly. Call support and provide this incident code.”
> probably because they only tested the app on a PC
If they made an app only for PC, why would they test it on Xbox??
If they wanted their app to run on Xbox, why would they not make an Xbox version, and test it there??
RC's scenario is taking an app made for only Windows PC and running it on Xbox. I wonder if he has found even one example which permits this in the licence.
> RC's scenario is taking an app made for only Windows PC and running it on Xbox. I wonder if he has found even one example which permits this in the licence.
I don't know what program he would be talking about, but I think if Raymond Chen is writing about something, it's usually because it is a real problem that was encountered, even if he's writing about it as if it was hypothetical to avoid naming the software in question.
He generally avoids naming non-Microsoft software he's found bugs in.
Anyone who has spent some time with vintage or otherwise older software (and even some new stuff) should have experienced a "don't do this, it explodes" - often the cause is something akin to what Raymond is pointing out. The software asks for something, gets back an answer it can't handle, explodes.
Your ecosystem has gone so complex that you can't test it anymore. So instead of handling errors properly you suggest to implement a convoluted user flow that offers always failing actions (install a printer when there's none available).
If that's really the suggestion of product and engineering leadership at MSFT no wonder all their products...err...work as designed.
Not at all what he is saying. Microsoft has no problem testing their printer function across all platforms in their apps and removing the print button on platforms where it's not supported. Ideally, everyone would do this.
This is for other developers who are writing 3rd party apps for Microsoft's platforms and who don't always test perfectly. They may have written their app primarily for Windows and didn't consider what happens when someone installs it on their Xbox and clicks "print". In that case, the API should "just work", instead of crashing with an error message.
>Your ecosystem has gone so complex that you can't test it anymore.
An operating system with billion+ install base, and millions of developers tends to be complex. I'm not sure what you're proposing here, have your platform/software be more niche? Somehow get all of them to fall in line?
>So instead of handling errors properly you suggest to implement a convoluted user flow that offers always failing actions (install a printer when there's none available).
How are you going to "handle errors properly" if the publisher of the software in question went out of business?
What is "your ecosystem" here? Part of running a platform is that the code running on it is written by other people, not you, and that your customers will be relying on that code.
When the software your customers are relying on breaks because of a change you made, it doesn't matter whose "fault" it is. It's broken, it broke because of something you did, and you have burned customer trust - not the trust of whoever wrote the software you believe is "wrong" or "to blame", trust of your platform and company.
It is so obvious from reading these comments here who is not, and would never ever want to be, a platform engineer. It’s hard for some people to grasp that you need to not only interact with, but thoroughly accommodate, software written by others whom you don’t control (often due to the one-way direction of time flow).
From my experience in other MS orgs, this was the prevailing approach. There are masses of bandaids on top of systems that have organically evolved over decades. Leadership is generally promoted from within so have a blind spot to how organisation incentives lead to these technical outcomes.
Bad: good apps can't know what's supported or not.
What's missing from the article is adding a way for apps to detect and handle this behavior. (Using the printer example) There should also be some API that tells me "printing is not supported on this platform" somehow. If I want to be a good software developer I should hide the print button in the UI in a very predictable way.
It's great that if I actually try to print nothing happens as expected. It's bad if I can't detect that printing would never work, and I should be able to do that via at least some function call that doesn't magically noop.
> Now, you probably also want to add a function to check whether printing even works at all. Apps can use this function to hide the Print button from their UI if they are running on a system that doesn’t support printing at all.
Wrong, errors should not go unnoticed, let alone helping them to propagate. Cascading effects should be kept on a short leash. System takes one step in the wrong direction, kill it. The two most miserable things are, things not happening and there's no feedback on why, and, the other extreme, when things are overengineered and no one can predict where problems might cascade to.
It's hilarious, Chen has been doing this right - on the bleeping internets for all to see - for thirty years and we've greenhorns going "Nah, mate, you're doing it wrong."
I didn't say it's unethical to preserve "ABI compatability." It's the software equivalent of the Chinese government gaslighting everyone while they wait for the dancing grannies to get the hint and go home. It is, in fact, the exact same technique.
And not only that, we're fast approaching the day where we all power on our PCs to discover that you can login with any user you like. And it gives you the list of all users who have a one drive account.
It's nothing of the sort. They provide APIs to check if functionality is supported and encourage their use. Helping users deal with apps that weren't written to deal with them isn't lying to users.
I understand the logic here, and I'm aware Microsoft has a large number of convoluted backward-compatibility requirements, but this seems like drinking to solve your problems and just putting off the inevitable hangover. To be clear: what you're doing here is lying to the user and the developer. Maybe that's justified in isolation, but now this lie is one more bit of "hidden state" you have to keep track of in further development and integration testing. And just like in the real world, lies have a tendency to compound on themselves until you're completely lost in them and have no idea what reality is.
I have a feeling that "solutions" like this are part of why an increasing number of my computing problems take the form of, "I tried to take an action, nothing happened. No error, no activity, nothing.", and are impossible to debug or diagnose. UX designers made themselves terrified of ever showing an error code to a user, but they took that and replaced it with a world where your shit just doesn't work, and when you try to figure out why, all the OS does is shrug.
So what's your solution then? Break all apps simultaneously that do not have extensive tests for gracefully handling cases that were impossible when they were created?
This is not weird "hidden state" on the implementer's side. It's a straightforward dummy API and all you need to do to test it are a few straightforward asserts that it returns the correct dummy values and doesn't crash.
> I have a feeling that "solutions" like this are part of why an increasing number of my computing problems take the form of, "I tried to take an action, nothing happened. No error, no activity, nothing.", and are impossible to debug or diagnose.
That's precisely not what this is. The whole point of the article is to do nothing correctly. Presenting an empty list of printers is consistent with a PC that simply doesn't have any printers installed. A wrong thing to do would be, for example, presenting a dummy printer that accepts jobs but of course never prints anything.
You don't break the users of your API, period. That shouldn't be controversial. Unfortunately, too many people seem to think they need to do everything over every couple of years only to produce a solution that is no more extensible and resistant to bitrot than the one they're replacing.
That doesn't even make any sense. Where on earth would you insert a try...catch?
So many people commenting on this article seem to have fundamentally misunderstood what the situation/scenario even is, which is made more jarring by the fact that so many others seem to have gotten it just fine.
> So many people commenting on this article seem to have fundamentally misunderstood what the situation/scenario even is
Agreed 100%. Those people have overlooked that this is about attempting to run apps on an unsupported OS. And when printing, the OS attempting to gaslight the user into thinking the fail is his fault for not connecting a printer.
How do you somehow both have a strong opinion about "deceiving users" and completely fail to understand the difference between a platform/OS and a device?
As far as alternatives to what the article suggested go, I think the ideal solution would be to have a compile-time error, so the developers never even get as far as having their printing code try to run on the Xbox. And since it's a compile-time error, there doesn't need to be any kind of runtime error handling or cost either.
You could still offer the mocked APIs, but have them be opt-in. Also, having the compiler throw an error doesn't mean that an older, already compiled executable won't work.
Apps aren't being compiled here. Apps already exist and new platforms and platform functionalities are being made available.
Launching platforms is an eternal chicken and egg problem: You want to launch a new platform. Users want apps before they buy into the platform. Developers want users and a guarantee they will spend money before investing time and money building apps for the platform. If you're really really rich, you can literally pay developers to write apps for you, but ask Microsoft how that went with Windows Phone.
Instead, the most reliable way (I said most reliable, not reliable) is to leverage an existing group of apps onto your platform. Maybe you have a phone ecosystem and want to get into tablets. So you take the already existing phone apps in your store and let them run on your new platform.
There is no recompiling. There is no build.
There is no "build" because the source code was lost when all of the company's assets were sold off in 2002.
There is no "build" because the developer made a game, put it on your app store, moved on to the next game and has no interest in supporting this game that isn't providing any significant new revenue.
There is no "build" because this is FooManager 6.0, the company that makes FooManger wants everyone to buy FooManager 7.0 and isn't making any further changes to 6.0.
etc. etc. etc.
Binary compatibility is what matters. Nothing else is of particular significance.
In addition, if recompiling an app for your new platform yields compile errors and requires work (as opposed to targeting a new platform version in a manifest and just hitting build), developers on your platform are significantly less likely to spend the time to do so.
I have a suspicion that "number of crashes" got badly Goodharted inside major OS vendors. After all, you can "prevent" most crashes by just having a top-level "catch(Exception e) {}" handler, which of course just leads to the program doing nothing instead in an un-debuggable way, but hey: crashes went down! KPI achieved!
My experience is that macOS especially really REALLY absolutely hates when the Internet goes sideways - not actually down, but really bad packet loss; DNS starts being slow and failing sometimes but not completely ... then you enter hell.
Next time it happens try turning network connections all the way off.
I think there's a difference between an API that can be used to check if functionality is available, and an API that must be used before that functionality is offered to the application. E.g.
This is one thing I like about Go's panic. You're mostly not supposed to use it or recover from it at run time. It serves as a great vehicle to blare loud sirens at testing time that you (the programmer) screwed up (and that's ok, we all do), and it's time to figure out where and how to fix it :)
PS this analogy works in a lot of domains - If you have actors in a system actively trying to hide their flaws/errors it will be exponentially harder to root them out and solve the issues.