Over the years, I have observed the fact that some /many things about GUI is/are just unnecessarily stupid.
See Jgoodies-binding, and more recently elm-lang and htmx for unexamples of this.
btw, rip
These days, there's a lot more boilerplate in most languages. C# was a horrible downgrade.
If you can put up with the documentation, Lazarus/Free Pascal works amazingly well, and is almost as easy as it used to be.
There is also something to be said about how organization structure their teams to build UIs and APIs, which is to scale productivity by adding and leveraging more people, as oppose to leveraging more powerful code. This is probably more true on the frontend as it is typically not as critical to the business as the backend, so it is not surprising to see UIs constantly break or lack any proper standardization.
Try doing something in Vue, Flutter, or SvelteKit or something... it will probably be significantly easier than what you expect!
The hardness comes from the fact that you're basically writing bookkeeping two way state sync code, and if you don't have a reactive framework you're doing it by hand.
Plus, there are so many GUI libraries, and the easy ones are big. I suspect GUI haters choose low level stuff, then complain about how hard it is.
Or, they might use language without that great of GUI bindings. They might also start out with a specific vision of how it should work, whereas people who find GUIs easy are picking a big popular framework and not really thinking of any ideas outside it's language.
I generally don't even start thinking about architecture or code until after I've already chosen my tools, I want to do designs that fit the tools I'm using, not design something and than choose a language specifically for the design.
Plus, you cannot build a nice GUI on top of a UNIXY backend without lots of work. For some reason I don't understand, programmers seem to think of computers as devices that take input and produce an output, and build stuff that looks like a compiler tool chain.
With GUI, you're not computing an output, you're updating a state based on user input. You generally never want to be in a place where you do things in a sequence, and can't edit previous steps without starting all over.
You also can't always have a logical model for what to include.
Usually GUIs are not general purpose like raw code is. You're not using a blank canvas to build another blank canvas, which is what most devs seem to like, you're building a curated workflow.
You might have "Edit this transaction" buttons and "Export report for the month" buttons. If you're used to thinking in logical layers that build on each other, you might not enjoy essentially recreating some illogical business process in code.
It seems like programmers are really code-philosophers and code is like a meditation for a lot of them, and they seem to get depressed and discouraged working on things that don't have any beauty or logic or reason and are just reflections of some messy illogical real world process full of extra stuff to deal with untrained users.
Take fading in sidebars, for example, which influence how the main content frame is rendered and are different in size on mobiles (e.g. icons, icons with labels, bigger icons, bigger labels or even a more nested structure on desktop, with a burger icon for fade-in and fade-out).
This is almost impossible to implement if you only have horizontal and vertical box layouting at your disposal, which is the most common denominator among frameworks and their renderer concepts (GTK, Springs, QT, etc).
Flexbox and tweenable properties like transparency, rounded corners, margins, paddings, colors and z-indexes are almost a hard requirement these days in terms of UI design. And those features are very hard to implement without a renderer pipeline that can influence both fragment and vertex shaders.
And this is not talking about state management of the UI elements, which is a whole world of problems on its own. React Native's approach, for example, is utterly broken on older mobiles due to how they re-flush the whole state tree on every single frame (which makes old mobiles lag due to them not being able to keep up with the amount of FPS required).
Getting the reflow right with e.g. calendar widgets and calendar popup views which are rendered by native APIs is a nightmare. As there is no unified way to do it across platforms, there eventually will be a lot of redundancies that are implemented, and a whole lot of quirky code behavior.
The trouble is that a UI element usually calls the framework and gets called by the framework so you wind up needing mocks and such. Or you have something like React which is just batshit crazy (e.g. do I wait until the component renders 7321 times or 7322 times before I can check that it is in the state it is supposed to be in?)
As for web development frameworks, GUI and otherwise, they are like nuclear fusion, only in this case a new framework is announced every two weeks that is claimed will solve every development problem.
The reason frontend dev sucks so much, essentially, is humans.
Have you met humans? They are opinionated, dogmatic, and hidebound; their habits and preferences are arbitrary, and, collectively, comprise their horison the way the bars of a cage do a bird's.
A slight user preference for something inconsequential, like, say, a realtime indication of password strength, or an absence of loading delays, means that if your product does not "meet user expectations" for some some incredibly inane value of `$EXPECTATION`, you can reliably expect your competitors to consume not just some, but eventually, all of your sales, and is therefore strongly selected against.
Recalling that you are yourself substrated upon a human, you know exactly what I'm talking about. Be honest. Are all of your preferences rational? What even is 'rational', anyway? I'm not just doing a lazy gesture to pseudo-philosophy here, I'm actively asking you to define practical rationality (not just logic, mind you) in a way that has no reference to common practice, intuition, or habit. Good luck :) (Note: I wasted my twenties in a philosophy PhD programme, so I know enough to advise you that this is a honeytrap that you can literally spend decades unsuccessfully trying to answer. I know I did.)
Anyway, because of user expectations -- expectations like: bespoke widget sets, branding, performance, speed, similarity with native app, cross-browser compat, etc. -- we -- us engineers -- built elaborate tooling back in the day to accomplish all this, for 2008-ish values of 'back in the day'. Some of these, like jQuery, eventually crystallized into new browser APIs, but a lot of them involved various kinds of either stack lock-in, or conceptual lock-in. (React does both.)
Remember how I said humans were creatures of habit? Well, horribly enough, engineers are humans too, and we can't get good (really good) at things until we've put in 10k hours or so. So, if you've put in 10k hours into React, you're going to be at a competitive disadvantage if you move everyone to a saner alternative, such as htmx. This, too, is strongly selected against.
So, there you have it. Frontend development is terrible because it's the part that touches the most humans.
Coordinating large asynchronous systems by hand is challenging. There can be surprising interactions. For example, tapping a button again in the middle of an animation is an example of something where the complexity often leads to the program getting in a bad state or even crashing.
This is one reason that structured concurrency and asynchronicity primitives have been added to many programming languages - to make reasoning about and managing such state machines easier.
Some modern GUI frameworks attempt to simplify this by instead let you declare the UI and its relationship with your data, the interdependencies there, and may have sophisticated means to monitor data changes. This breaks you from having to maintain most GUI state within your program - you respond to user actions by updating your program state, and the GUI responds to those updates.
I looked into Flutter & Dart as it seems thatr everybody loves it. But then apparently even for Flutter you need another framework for state management. I thought state management is the core issues these frameworks want to solve?
I would've thought game engines have solved the problem of complex state management though. There are games with reactive in-game browsers/websites which work better than most real websites!
So far the easiest for me to grasp and getting started was actually react / nextjs:
- Extremely well documented
- Little boilerplate (especially compared to Flutter - I find that StatefulWidget really ugly)
- Combined with a component library it's incredible easy to get started
The best GUI toolkits I've used are either heavily abstracted (tkinter/pygame) or absurdly full-featured (GTK/Qt).
Except stable APIs and documentation.
One of the answers does mention FLTK, which I should have included above.
Gui has a lot of conceptually complex properties :
- it has both synchronous and asynchronous behaviors ( drag vs trigger an animation or loading data for the next screen )
- it has i/o (the screen)
- it is interactive (a user can click at any point in time, interrupting the process)
- it has transactional properties (you sometimes want a whole set of changes to be performed atomically, with nothing interrupting it).
-it is working both in diff mode ( change just the color of that button) and in whole state change (load a new screen)
all of this make the problem quite challenging.
Decades ago, I used zApp, wxWidgets, Java Swing, Qt and Python GTK bindings. None of those were particularly hard.
What blows my mind is how long it took the web to get flexbox (Java GridBagLayout, IIRC).
And UI programming is hard because of the user you are interfacing with :-) and the fact you are not forcing them to have amazing working memory needed to deal with a CLI.
* Parts are fundamentally declarative and parts are fundamentally imperative. This applies both to the abstract problem, and the approach taken in particular toolkits, and the border is not in the same place.
* The state involved is stored in at least 2 places which are distant from each other, and that opens all sorts of logic and timing problems.
* The same GUI framework gets used both for "wait for an event" UIs and "update every frame" UIs.
Abstraction is hard.
I rarely program GUIs but when I do I use React and it works quite well quite easily.
People who are indifferent and uninterested in non UI logic, will speak with confidence and criticize UI.
It's just that all of the tooling is beyond exceptionally bad.
There is just so much potential close at hand but out of reach. E.g. Steam Controller.