I don't believe people like the author of the OP are just over-generalizing for clicks. Based on the other responses on this thread, they're expressing something that many of us feel, and that they presumably feel themselves. I'm not sure what to do about that feeling, though. The tools coming out of the Handmade network, at least the ones I've seen, tend to go too far, throwing out necessary things like accessibility. Some of the complexity of modern software is truly necessary.
It's easy for those of us who have used computers for a while, or have read some computer history, to look back at earlier feats of software compactness and efficiency, such as an IDE starting and running fast on a processor with a tiny fraction of the power of current CPUs (90s Delphi as raised by a sibling comment), a MIDI sequencer with karaoke-style lyrics display in less than 48 KB of machine code (Diversi-Tune on the Apple IIGS in 1988), or a BASIC interpreter in less than 4 KB (the original Altair BASIC from Microsoft). It's easy to look at these things, compare them to the size of a hello-world program produced by a modern toolchain, and conclude that modern software is indeed terrible. But of course, we look back on those past artifacts with rose-colored glasses. I don't doubt that, for some subset of modern software, bloat is indeed out of control. I'm not sure what to do about it though, or if we can ever get back to those feats of efficiency that tend to impress us in retrospect.
Right. But I think it's easy to misremember the past. Delphi 5 might start quickly if you run it on today's hardware, but back then it took a good 10-15 seconds to start. I remember staring at those splash screens and listening to the endless frustrating clicking of the HDD head far too well.
I think a good reality check is to try and bring splash screens to mind. How many splash screens can you think of for modern apps? I can only think of Slack. Back in the Delphi era every app had a splash screen. Office apps, IDEs, html editors, even web browsers! One reason IE4 won over Netscape 4 is the latter showed you a big splash of a lighthouse where the former didn't need one. SSDs are truly miraculous devices but also software engineering just got better. Like, modern client devs know to keep slow operations off the GUI thread whereas back then very few apps were threaded so hung windows that wouldn't repaint were standard.
Software is definitely bigger now, but that's mostly because hardware is vastly bigger so nobody cares. Like, on an average Swiss internet connection the bottleneck for installing an app is now the decompression and GateKeeper hashing/anti-malware checks, not the download itself! Why optimize for size when users don't complain about it?
> Why optimize for size when users don't complain about it?
Pride in one's work? Especially when comparing it to the "good old days". Maybe we'd have better morale if we were allowed (or allowed ourselves, for those of us running our own companies) to indulge in such "useless" optimizations. But no, the pressure is on to keep cranking out more, more, more, with no time for quality that we can be proud of.
Edit to add: You're right though that splash screens are less common now.
I'd argue that the primary source of bloat in software today is when there's a mismatch between what developers need and what the underlying platform provides. The most extreme case of this can be found in desktop operating systems where relatively few apps now use the underlying platform at all, largely because nobody wants to write GUI code multiple times. What developers want and what the underlying operating systems provide are almost entirely different so everyone ships a giant runtime to replace what's the OS comes with. Modern mobile platforms have a similar problem, where things like Swift UI or jetpack tend to be bundled with the app or at least were historically up until recently, and that yields bloated packages too.
In contrast people get the vapors if a web app is more than 5 MB of download and this happens because the web platform is significantly more responsive to what developers really want even though it has poor technical foundations. Where bloat does occur in web apps, it's because the browser makers have not responded quickly enough or at all to growing mismatches, for example the vdom diffing algorithm react uses could be implemented in the browser itself, but isn't, so lots of apps ship it bundled.
Apps for Windows 95 didn't seem bloated, but that's because they relied so heavily on what already came with the computer when you bought it. People just had a suck up the fact that the underlying API was pretty bad, because they couldn't afford to ship anything better and till the CD era came along and then you did start to see apps leave behind the underlying platform and get more bloated.
A truly next-gen operating system would make it very difficult to talk precisely about bloat, I think, because if you really went beyond the Unix or NT designs your operating system would download and manage code modules automatically, a little bit like browsers did before the introduction of cache partitioning. How bloated an app appears to be then would depend entirely on what you had previously used and downloaded, and thus to some extent would be in the eye of the beholder. After all, if you measured all of the code paged in by running a native Mac app, or even something like delphi, versus the amount of code paged in by an electron app, it probably wouldn't be that different. It's just that the Mac app is accessing code that is shared with other operating system utilities.
Thanks for steering the discussion in a more constructive direction than where I was going.
There are C++ UI toolkits wrapping Win32, Cocoa, etc., and sometimes such toolkits are statically linked into the executable, meaning that dead code elimination should be able to minimize the executable size. I'm thinking in particular of wxWidgets. But even with C++ and static linking, it's easy for a library to be designed in such a way that you end up paying for stuff you don't use. The last time I did a project in C++ with wx, 10+ years ago, my program itself was quite trivial, and it only used very basic widgets, but the statically linked Win32 executable still ended up being ~2.5 MB. I didn't get very deep into why, but my understanding is that code for a bunch of features (printing, drag and drop, etc.) got linked in, even though my application wasn't using it, because the window procedure had to handle messages related to those features regardless. I guess, to optimize for minimal executable size, the API would have to be designed to require the application developer to explicitly initialize only the features they need, and in this case, the message handler would have to be more dynamic.
Sometime I'd like to design a new toolkit inspired by wx and SWT, in either Rust or Java. In the latter case, I'd use the new Foreign Function and Memory API to call the native APIs. I'd be interested to see how much GraalVM Native Image could optimize an application written with such a toolkit; I recall you've said good things about how much Native Image can optimize program initialization in particular.
There seems to be a broad consensus, though, that Win32 in particular isn't suitable for "modern" desktop apps, and so, by extension, neither are wx and SWT. Certainly in the Rust GUI space, all the popular toolkits are drawing their own widgets, and even reimplementing lower layers of the stack like text rendering, thus contributing to the trend you talked about where applications bring along their own runtime. I assume there are actual deficiencies in OS-supplied APIs that are leading to this, though a sufficiently jaded person might argue that developers are in fact wasting their time on unnecessary things for one reason or another. Anyway, if the classic Win32 widgets are still good enough for some parts of the OS itself (although, admittedly, fewer and fewer with each release), then presumably they're good enough for some subset of applications as well, and if the developers of those applications had a convenient and lightweight abstraction layer over those classic widgets, then we could start to reduce bloat.
Yeah, static DCE is a limited optimization. I've personally given up on it. It's not useless, but it's a big lift for developers and the app size will keep growing anyway. These days I'm more interested in dynamic code paging systems as a way forward, but, at the moment I'm not able to work on that right now. Think about how "bloated" an app like Google Maps or Search is, yet it still feels fast because it downloads what it needs on demand. Everything should work that way.
The Conveyor installer is a native Win32 app written in C++. It's pretty trivial, it mostly just invokes some Windows APIs, works around bugs in those and ties the results to a progress bar. It's a classic app that uses HWNDs, window messages and the like. The bulk of the logic is in one file. Despite that it's about a 500kb EXE. Where do the bytes come from? Partly, it's C++. Native code is verbose and template instantiation creates a lot of it. You don't need to instantiate many std::vectors before suddenly binary size has crept up on you. The widespread adoption of C++ increased binary sizes significantly over C, because suddenly you were using value typed containers everywhere instead of a single shared linked list utility library.
GraalVM native executables start extremely fast, but they aren't small. Partly this is the mismatch problem again: operating systems don't provide stuff like built in garbage collectors, even though GC is a fundamental service nearly every app needs, so, programs ship their own. Partly it's because GraalVM makes apps start fast by pre-initializing the app, so the binaries come with a heap image that it just mmaps and starts using. But then you have to download and store the initial heap instead of recomputing it at startup. It's a disk space/startup time tradeoff. And partly it's because a static analysis must be inherently conservative. Anything that could potentially run has to be included, even if in reality it never does (think exception handlers, assert messages...)
So IMO the right fix is to just assume an always-on internet connection and then stream code in as it's needed, with pre-emptive paging in the background to get apps into a state where you can disconnect. The system would aggressively deduplicate data to avoid redownloading stuff you already have. A smarter version of what browsers do. This lets you have apps that are hundreds of gigabytes in size but they still start fast and sip resources on the client side where you're most constrained.
I think modern languages ignore Win32 partly because it sucks and partly because virtually no working developers have ever used it. Many modern devs won't even realize it's there. How many devs that slung window messages in the 90s are still coding? I think a lot have retired or moved into management roles, or were hired to work on Chrome. There are Microsoft security engineers, working on Edge, who announced in a blog post that they had no idea how to use COM and had to rely on AI to write the needed code for them! [1] And a lot of devs aren't developing on Windows, they're probably writing these Rust toolkits on macOS or Linux.
Anyway, Win32 UI is something I'd not recommend using. It's been unmaintained for decades. Conveyor uses it because, well, it's an installer that downloads the rest of the app, so size matters more than anything else, and the UI needs are simple. The moment you want table stakes stuff like High DPI support, responsive layout, dark mode, vector icon support, data binding etc, you end up needing big frameworks that sit on top and/or you end up in a nightmare of twisty barely documented APIs.
> Like, modern client devs know to keep slow operations off the GUI thread whereas back then very few apps were threaded so hung windows that wouldn't repaint were standard.
Maybe things are going great among people who write native software that uses native UI libraries. In games, users of the most popular game framework have been asking the framework developers for the ability to poll inputs on a thread that isn't also the render thread for 14 years and counting.
It's easy for those of us who have used computers for a while, or have read some computer history, to look back at earlier feats of software compactness and efficiency, such as an IDE starting and running fast on a processor with a tiny fraction of the power of current CPUs (90s Delphi as raised by a sibling comment), a MIDI sequencer with karaoke-style lyrics display in less than 48 KB of machine code (Diversi-Tune on the Apple IIGS in 1988), or a BASIC interpreter in less than 4 KB (the original Altair BASIC from Microsoft). It's easy to look at these things, compare them to the size of a hello-world program produced by a modern toolchain, and conclude that modern software is indeed terrible. But of course, we look back on those past artifacts with rose-colored glasses. I don't doubt that, for some subset of modern software, bloat is indeed out of control. I'm not sure what to do about it though, or if we can ever get back to those feats of efficiency that tend to impress us in retrospect.