The idea behind digitalWrite would in a similar vien to the low level sockets API on a Linux machine. There are many different kinds of sockets many different things on the other end, but the sockets code abstracts this, so that all you need to know is how the sockets API works and you can work with any socket. That's one of thousands of abstractions in even GLibc.
The trade off is that to support a wide feature set, at compile time, or worse at runtime, abstractions have overhead. "DO we do this or that? Are we dealing with a thing or a duffer? Is the target this or that?".
In something like Linux it is mostly macro soup, up as far as "compile time" support switching. Runtime is handled by more layers and cooperation on agreed interfaces (Similar to the UART, SPI, layer. etc.)
But as you go up the stack towards the UI the permatation sky-rocket. Different browers, phones, accessibility. Different languages, timezones, character sets. Layer and layers of GUI utilities. But also layers and layers of seperation for security and scalability.
Where "performance" limited code exists abstractions are removed. However, to date increasing hardware size has been cheaper than the developer time to optiimise, not least because you need a good dev.
Oddly, this is one thing AI has been marked for. "Flattening." Where you give it a "human written" heavily isolated and abstracted software stack, broken up that way so that developers could understand it and "Flattening" out the abstractions when given a more narrow set of actual use cases.