In essentially all of my software projects, the user interface (often abbreviated UI) has been my least favorite part to work on. Creating a user interface often feels like the process is dominated by fiddling around with small things, either to make the interface look more appealing, or to make it easier to use, or any number of other reasons. No matter how simple the job of the software, the user interface seems to remain a complex problem.
User interfaces, in their essence, are translators. Their job is to translate requests from one side and present it to the other, then translate the response as well. It is useful to recognize that this job is bidirectional. The human is going to have requests for the software, but the software is also going to have requests for the human. As a translator, the UI needs to understand the complexity of both agents. While software can be made simpler, humans can only be appealed to. That's why, in a lot of cases, the UI part of software ends up being more complex than the main logical components.
So what actually defines good user interface? It's the same thing that makes a good translator: the computer gets data in its preferred format, and the human gets data in their preferred format. On the software side, that means that the eventual requests received are easy to parse, contain all the necessary data, and so on. On the human side, that means that it feels easy to understand what is wanted from them, to respond with the appropriate information, and to request something from the software.
The human side is invariably the hard part of this interaction. Once you've worked with the software side enough, it tends to become clear what sort of input structure is easy to work with and leads to simple code. But that has no bearing on what the user thinks is simple. Nearly all of the work of building a user interface is figuring out what it is that the user thinks is simple, and then accommodating for those beliefs whether they are accurate or not.
You can try to cheat your way out of building a UI, and I sort of took that approach with MultiTwitch. If the software has a simple enough purpose, then you can put the burden of translation on the human: "Put the streams you want in the URL separated by slashes." For MultiTwitch it was enough to get by, but I have seen many people ask "Can someone make a multitwitch for these two streams?" which indicates at least a partial failure of the UI.
For that particular instance, I decided that it wasn't worth investing my time in finding a UI that worked for those users. Usually someone is kind enough to provide them a link (since once you know what to do it's not very difficult), and it ends up not being a big issue in practice. But I did have to make UI changes, because people wanted to be able to do things that they couldn't do with the URL interface.
Imagine you're watching three live streams, and then one of them goes offline. You're stuck with an offline image taking up 1/3 of your screen real estate. No problem, you think, just remove that stream from the URL and then reload the page. Then you realize the problem. Loading streams takes a few seconds, and that means that adding or removing streams from the set that you're watching interrupts the viewing experience on all of them. That's not pleasant at all, so I ended up needing to build a way to adjust the stream set that didn't involve reloading the page.
As much as I'd love to stay far away from the user interfaces and work purely on software that interacts with other software, it seems to be a valuable skill to be able to at least make something bearable to use. When I look back at my personal projects nearly all of them end up dying at the stage where I "just" need to make a user interface, because it wasn't a problem that I wanted to tackle. That's a weakness that I want to address, and a big reason why one of my goals right now is to push projects all the way to completion.