<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>evolved systems</title>
  <subtitle>This is the personal blog of Rafał Hirsz. Read about all the things I&#39;ve done and all the thoughts I had.</subtitle>
  <link href="https://evolved.systems/feed.xml" rel="self"/>
  <link href="https://evolved.systems/"/>
  <updated>2021-06-05T19:35:39Z</updated>
  <id>https://evolved.systems/</id>
  <author>
    <name>Rafał Hirsz</name>
    <email>evol@evolved.systems</email>
  </author>
  
  <entry>
    <title>Making good GUIs is hard</title>
    <summary>After spending a lot of time building this blog, I decided to explore why I think it&#39;s hard to build good UIs. Also, I try not to sound discouraging.</summary>
    <link href="https://evolved.systems/making-good-guis-is-hard/"/>
    <updated>2021-06-05T19:35:39Z</updated>
    <id>https://evolved.systems/making-good-guis-is-hard/</id>
    <content type="html">
    <![CDATA[
      <p>I’ve been working on graphical user interfaces (GUIs) on the web, mobile, and desktop for over a decade now. Over time, one thing that really stood out to me is this notion that building UIs is somehow easy. I disagree.</p>
<p>But first, I'd like to separate the overall discipline of &quot;building GUIs&quot; from the tools and frameworks used to build them. While the tools can make the building process enjoyable, I don't want to discuss them in this post. Instead, I want to focus on what the core of building UIs is.</p>
<p>The thing is, the more I work on and with GUIs, the more I’m convinced that the opposite is true — GUIs are <em>inherently</em> hard to build. Let me explain why.</p>
<h2>It’s right in the name</h2>
<p>GUIs are graphical <strong>user</strong> interfaces. They’re the primary point of contact with an actual person. And the thing about people is that they’re way more complicated than computers.</p>
<p>To demonstrate, take another kind of interface — Application Programming Interfaces. You could say that APIs are also written for people and that computers don’t care. While this is true, it’s also true that APIs operate in a very constrained problem space:</p>
<ul>
<li>they are tightly defined in terms of inputs and outputs</li>
<li>the people dealing with them are (sort of) trained to do so</li>
</ul>
<p>In contrast, GUIs present surfaces for people to interact with. Theoretically, you don’t need much training to interact with GUIs thanks to familiar metaphors from the real world — buttons, knobs, sliders, forms, etc. Beyond that, nothing is really defined.</p>
<p>So actually, when you’re about to build a GUI, you really have a blank canvas in front of you that is supposed to eventually help people (who maybe don’t know what they’re doing) achieve their goals without getting lost.</p>
<p>That doesn’t sound easy, right?</p>
<h2>The devil is in the details</h2>
<p>To solve hard problems, you have to scope them down. And that’s another thing that makes it non-obvious — the sheer number of decisions you have to make.</p>
<p>Who’s your audience? In what situations are they going to use your thing? What about first-time users — do they interact with it differently than experienced users? How much effort do you want to put into this? What are the limitations of your platform?</p>
<p>Answers to those questions aren’t really specific to GUIs, really. They only draw a rough outline and influence further, more specific decisions. For example: Should your UI work on mobile devices? Does someone have to be online to use it? What if somebody is, say, colorblind — is it hard for them to use your thing? Do you want it to blend into the operating system well?</p>
<p>The rabbit hole of decisions can become pretty deep.</p>
<h2>A somewhat real-world example</h2>
<p>I feel like I spent much more time working on this blog’s layout and its post editor than on the underlying library to use <a href="https://evolved.systems/hosting-a-blog-on-matrix">use Matrix as a blog</a>, so I think they’re a good example. But first, to set the stage, let’s start with the library and the decisions that shaped it.</p>
<p><a href="https://github.com/evoL/matrix-blog">matrix-blog</a> is a library written in TypeScript to talk with a Matrix server as if it was a backend for a blog. It only has one dependency, <a href="https://www.npmjs.com/package/node-fetch">node-fetch</a>, and that’s mostly for convenience. It’s made of two layers — its own Matrix client and an API to interact with blogs and their posts. There are a few decisions I had to make to make this happen:</p>
<ul>
<li>I wrote a Matrix client because I wanted to learn more about the Matrix protocol and see if it’s hard to work with (it’s not).</li>
<li>The library is as stateless as possible to see if I can offload everything onto Matrix.</li>
<li>I wanted to use it on the web-based post editor, so this limited me to JavaScript and friends.</li>
<li>I picked TypeScript mostly because the Matrix API is typed — it’s better if the compiler prevents me from making errors.</li>
<li>I didn’t write any tests because it’s mostly an experiment and I wanted to iterate quickly. That said, the APIs are designed to be testable if I changed my mind.</li>
<li>There’s no <code>login</code> method because I didn’t want to think about session storage.</li>
<li>Each dependency incurs a cost of giving up understanding parts of your codebase. Sometimes the cost is worth the productivity boost, sometimes it’s not. I like to understand as much as possible.</li>
</ul>
<p>That’s pretty much it. Let’s contrast this with the decisions that shaped this website:</p>
<ul>
<li>I wanted the site to reflect my personal tastes, but also feel light and be legible, so I designed the theme from scratch. This alone involved:
<ul>
<li>picking colors and fonts,</li>
<li>figuring out type scales,</li>
<li>arranging the layout,</li>
<li>designing a favicon (it’s hard!),</li>
<li>deciding how much visual flair can I afford.</li>
</ul>
</li>
<li>The website should not just feel light, but also be light. Therefore, I:
<ul>
<li>used only 4 images as SVGs,</li>
<li>optimized them to be as small as possible using <a href="https://jakearchibald.github.io/svgomg/">SVGOMG</a>,</li>
<li>reduced the size of the heading webfont using subsetting,</li>
<li>used a static site generator (<a href="https://11ty.dev/">11ty</a>) to avoid hitting a server,</li>
<li>used as little HTML markup as I could,</li>
<li>don’t use client-side JavaScript aside from a &lt;1kb gzipped script for my <a href="https://plausible.io/">self-hosted analytics</a>.</li>
</ul>
</li>
<li>I wanted the website to work well on all major browsers and devices, so I:
<ul>
<li>used SVGs because they scale across sizes,</li>
<li>spent quite some time aligning the top image across screen sizes,</li>
<li>made sure that the browser features I use are widely available,</li>
<li>tried it on whatever browser I had at hand,</li>
<li>asked friends to check it on devices I didn’t have at hand.</li>
</ul>
</li>
<li>I don’t set any cookies because I care about privacy and I didn’t want to show an obnoxious cookie banner.</li>
</ul>
<p>That’s a much longer list for something that <em>just</em> displays content! And there's still a lot to improve!</p>
<h2>Everything is hard if you want to do it right</h2>
<p>Of course, you could argue that if you want to build things well, then it’s always going to take more time, energy, and decisions. That’s true. You could also decide that you don’t care about the looks and <a href="http://motherfuckingwebsite.com/">not do any of the things I've done</a>. That’s fine.</p>
<p>The point I tried to express in this blog post is that, on average, GUIs are hard to build because of the number of decisions you have to make. That's just how they are! But that shouldn't discourage anyone from building UIs! On the contrary, I hope people will treat them as any other challenging problem to solve. I also hope that it will lead to better user experiences for everyone.</p>

      <p><a href="https://matrix.to/#/#blog.making-good-guis-is-hard:evolved.systems" target="_blank">Discuss this post on Matrix</a></p>
    ]]>
    </content>
  </entry>
  
  <entry>
    <title>Hosting a blog on Matrix</title>
    <summary>If you&#39;re reading this, it means it works — this blog is hosted on Matrix. This post describes how I&#39;ve done it.</summary>
    <link href="https://evolved.systems/hosting-a-blog-on-matrix/"/>
    <updated>2021-07-30T21:16:38Z</updated>
    <id>https://evolved.systems/hosting-a-blog-on-matrix/</id>
    <content type="html">
    <![CDATA[
      <p>One thing about me is that I'm a fan of decentralized network protocols. Decentralized protocols — think DNS, SMTP+IMAP, HTTP — form a strong foundation of the Internet. Also, I have fond memories of using XMPP and its <a href="https://en.wikipedia.org/wiki/XMPP#Connecting_to_other_protocols">transports</a> to chat with my friends in the early 2010s. This is why I look forward to the future of <a href="https://matrix.org/">Matrix</a>, an open protocol for decentralized, real-time network communications.</p>
<p>Another thing about me is that I like to tinker with software and enjoy working on… call it unusual problems. I also like to read about people doing weird cool things with tech, so I figured that maybe it would be cool to have a blog.</p>
<p>Therefore it makes perfect sense that one time during a shower I've got an idea: what if I could combine the two and <em>host a blog on Matrix</em>?</p>
<h2>Why would you put a blog in a chat app?</h2>
<p>I guess this is likely the first question that comes to mind.
It's probably not the best way to host a blog. After all, you can use any hosted blog platform like <a href="https://wordpress.com/">WordPress</a> or write some Markdown in a Git repository and use a static site generator like <a href="https://www.11ty.dev/">Eleventy</a> or <a href="https://gohugo.io/">Hugo</a>. This would make your life easier.</p>
<p>Well, the true answer is really <em>because I can</em>. It turns out that Matrix is not just a chat protocol. It's more like a decentralized, time-ordered event store that synchronized between federating servers. In other words — if you can shape something as a series of events, you can put it in Matrix.</p>
<p>Of course, Matrix is still used primarily for chat, so I thought that it would be nice to be able to interact with the blog using regular Matrix clients like <a href="https://element.io/">Element</a>. This way, you could allow people to read the blog posts and comment on them directly in their chat clients. It's just a matter of mapping the blog to a compatible representation.</p>
<h2>Putting the pieces together</h2>
<p>What do we need to represent a blog? Aside from a list of posts, each post should have:</p>
<ul>
<li>a title</li>
<li>an optional summary</li>
<li>an URL-safe identifier (also known as a <em>slug</em>)</li>
<li>publication date</li>
<li>last edit date</li>
<li>the post content itself</li>
</ul>
<p>It turns out that the base protocol already has all the necessary pieces!</p>
<p>First, in Matrix, communication happens in <em>rooms</em> — just like on Slack or IRC. Because I wanted to potentially have discussions on every single blog posts, it makes sense to represent blog posts as rooms.</p>
<p>A room can have a user-friendly name that can represent a post title. It can also have a <em>topic</em>, which usually describes the current conversation topic, but could serve as a summary of the blog post.</p>
<p>Usually, each Matrix room is identified by a server-generated string that looks like this: <code>!xZzBOxfyJgfJbaiEGc:evolved.systems</code>. While this is perfectly fine to be used in an URL, it's not very readable and it's nice to have readable URLs. Fortunately, rooms can have <em>aliases</em> (called <em>addresses</em> in Element) that solve the readability problem. They look like this: <code>#matrix:matrix.org</code>. A room can have multiple aliases (e.g. on multiple servers), but there's only one canonical alias — and that's what I use to represent the slug. However, there's one caveat: because I also want to use rooms for other things than a blog, I add a prefix in front of the slug to prevent name clashes. This means that, for example, this blog post would have a canonical alias of <code>#blog.hosting-a-blog-on-matrix:evolved.systems</code>. Using aliases has a nice bonus of controlling if a post is visible — if there's no alias, it's not &quot;published&quot; and therefore not visible.</p>
<p>Finally, representing the post content sounds pretty easy — just write a message! We can say that the first message in a room will be the post content. Matrix supports plain text and <a href="https://matrix.org/docs/spec/client_server/r0.6.1#m-room-message-msgtypes">HTML-formatted</a> messages. Both types can be set side-by-side, so you can put Markdown in the plain text part and the rendered HTML in the formatted part, which is pretty convenient.</p>
<h2>Everything is an event</h2>
<p>So far it's all pretty simple, so I think this is a good time to add some color. It's easy to say &quot;just write a message&quot; or &quot;set a name&quot;, but what does this mean?</p>
<p>Well, as I mentioned before, Matrix is really a time-ordered event store. You could say that rooms actually are timelines of events. Furthermore, there are two kinds of events — message events and state events.</p>
<p>Message events are pretty self-explanatory — some content sent by somebody at a given time. On the other hand, state events are quite interesting, as they also allow to influence the state of the room itself. You could think of them as being building blocks of a key-value store that represents the room state (where &quot;key&quot; is &quot;event type&quot; + optional &quot;state key&quot;). The server has dedicated APIs to read individual state values and also to retrieve the entire state by fetching the most recent events for each &quot;key&quot;.</p>
<p>For example, the name of the room is really represented by the <code>m.room.name</code> event. To read the current name, you can ask for the value by <a href="https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey">sending a GET request for that specific event type</a>. You can also <a href="https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state">retrieve the entire state</a> and search for the <code>m.room.name</code> there. To change the name, you just send a <a href="https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid">new state event</a>.</p>
<p>This is how we find the timestamps we need for the post — since each event has a timestamp, we can pick the meaningful ones. I've decided that the moment I set a slug for a post is the moment when I consider the post &quot;published&quot; — so the publishing date is the time of the latest canonical alias event that actually sets (not clears) an alias.</p>
<p><strong>Edit:</strong> I mentioned above how the first message would be representing the post content. Turns out that getting the first message of a room <em>statelessly</em> is tricky. <a href="https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-messages">The endpoint used for retrieving room messages</a> requires specifying a token that indicates where events should be returned from. We don't have one without calling <a href="https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-sync"><code>/sync</code></a> before. To work around this, I introduced a custom state event named <code>co.hirsz.blog.post_content</code> that holds the event ID of the post message, e.g. <code>{&quot;event_id&quot;: &quot;$something&quot;}</code>. This makes it easy to fetch the event along with the rest of the state.</p>
<h2>Going beyond the stable protocol</h2>
<p>So far, everything I described is part of the stable <a href="https://matrix.org/docs/spec/client_server/r0.6.1">Matrix Client-Server API (r0.6.1)</a>. However, to have a functional blog we need more: we still can't edit messages and don't know how to get a list of posts, which is pretty basic functionality. Fortunately, the Matrix protocol is constantly evolving through Matrix Spec Changes (MSCs), some of which are already implemented in servers.</p>
<p>Editing messages is already implemented in Synapse, the reference server implementation, and is specified in <a href="https://github.com/matrix-org/matrix-doc/pull/2676">MSC2676</a>. Basically, editing works by sending a new message marking that it <em>replaces</em> the old one. The nice thing about the implementation is that you can still reference the event ID of the old message — it will automatically have the new content.</p>
<p>To represent a list of rooms, I've decided to use <a href="https://matrix.org/blog/2021/05/17/the-matrix-space-beta">Spaces</a> — the newest, shiniest feature of Matrix. Spaces are special kinds of rooms — they can contain other rooms. You can probably see how it's useful: since blog posts are rooms, we can have a single parent room that contains all the rooms for posts. Since rooms are linked to spaces using state events, we could just use that. However, I went with the dedicated <a href="https://github.com/matrix-org/matrix-doc/pull/2946">Spaces Summary API</a>, mostly to see how it works.</p>
<h2>Final thoughts</h2>
<p>Since it's not exactly fun to write blog posts by sending HTTP requests with cURL (although you could do that!), of course, I <em>had to</em> build the entire stack:</p>
<ul>
<li>a simple JS Matrix client to learn how the protocol works (yes I know <a href="https://github.com/matrix-org/matrix-js-sdk"><code>matrix-js-sdk</code></a> exists)</li>
<li>a JS library on top of the above to expose a blog-centric interface — <a href="https://github.com/evoL/matrix-blog">matrix-blog</a></li>
<li>an editor to browse and write blog posts using Markdown — <a href="https://github.com/evoL/matrix-blog-admin">matrix-blog-admin</a></li>
<li>the user-facing blog website itself</li>
</ul>
<p>All in all, this was a fun, if a bit lengthy journey in rediscovering modern front-end development. It took me about a month to get here, but it's finally more or less done. Of course, there's always work to do — for instance, image uploads would be nice — but that can be done in further iterations. Maybe I'll write about them in future posts.</p>
<p>If you want to take a look at the ugly code I've written that implements the above — <a href="https://github.com/evoL/matrix-blog">check out the GitHub repo</a>. For an example of how this works in practice, <a href="https://github.com/evoL/matrix-blog-admin">check out matrix-blog-admin on GitHub</a>, the post editor app I wrote.</p>

      <p><a href="https://matrix.to/#/#blog.hosting-a-blog-on-matrix:evolved.systems" target="_blank">Discuss this post on Matrix</a></p>
    ]]>
    </content>
  </entry>
</feed>