<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>✨ bram dingelstad&#x27;s works 🤝</title>
    <subtitle>👋 here you&#x27;ll find my links, blogs, photos, videos and other media ✨</subtitle>
    <link rel="self" type="application/atom+xml" href="https://bram.dingelstad.works/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://bram.dingelstad.works"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-04-03T00:00:00+00:00</updated>
    <id>https://bram.dingelstad.works/atom.xml</id>
    <entry xml:lang="en">
        <title>Making syrinx, my media proxy</title>
        <published>2025-04-03T00:00:00+00:00</published>
        <updated>2025-04-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/videos/making-syrinx-my-media-proxy/"/>
        <id>https://bram.dingelstad.works/videos/making-syrinx-my-media-proxy/</id>
        
        <summary type="html">&lt;p&gt;I needed to make a media proxy to safe money on serving videos like these! Let&#x27;s talk about why I made this, and go through the code together! ✨&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Making a mini server cluster</title>
        <published>2025-04-02T00:00:00+00:00</published>
        <updated>2025-04-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/videos/making-a-mini-server-cluster/"/>
        <id>https://bram.dingelstad.works/videos/making-a-mini-server-cluster/</id>
        
        <summary type="html">&lt;p&gt;I decided to get rid of my reliance on cloud subscriptions and services, so I decided to make a little server cluster ✨&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>my tiny &amp; hyper specific computer: the typewriter</title>
        <published>2024-04-14T00:00:00+00:00</published>
        <updated>2024-04-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/my-tiny-and-hyper-specific-computer-the-typewriter/"/>
        <id>https://bram.dingelstad.works/blog/my-tiny-and-hyper-specific-computer-the-typewriter/</id>
        
        <summary type="html">&lt;p&gt;A few weeks ago I had ordered a few too many small SBCs (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Single-board_computer&quot;&gt;single-board computer&lt;&#x2F;a&gt;). I was starting to struggle to use them all. I had waste, especially e-waste, so I wanted to put these little boards to good use. I also had a small desk in my living room that I wanted to use more for non-work related things like writing. My brain subconsciously did a 1+1 and I ended up with the idea to take on of the SBCs and &lt;em&gt;compose a small linux distribution that&#x27;s focussed on &lt;strong&gt;&lt;strong&gt;just&lt;&#x2F;strong&gt;&lt;&#x2F;strong&gt; writing&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>i make €500+&#x2F;month with a rust SaaS designed &amp; written for resilience</title>
        <published>2023-09-29T00:00:00+00:00</published>
        <updated>2023-09-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/500-eur-mrr-because-tech-is-bullshit/"/>
        <id>https://bram.dingelstad.works/blog/500-eur-mrr-because-tech-is-bullshit/</id>
        
        <content type="html" xml:base="https://bram.dingelstad.works/blog/500-eur-mrr-because-tech-is-bullshit/">&lt;!--
I think everyone knows at this point that there are limits to the idea that the tech industry will solve all of our problems as humans. People obviously found out when Elon Musk decided to “revolutionize” twitter, or when Unity decided to charge developers 20 cents for every install. Under capitalism, the tech industry (like any other) is just there to exploit value out of whatever resource it can acquire. I, and many other people, don’t like that aspect of our world. I just want to be creative and make video games all day, but I am forced to sell my labour in order to afford a living (*I am very lucky to work in the video game industry, so I’m technically already in the space I want to be in*). But I’m a programmer! What if we could appropriate one of the most toxic aspects of our society (the tech sector) to work in our favour? What if we could design an application that generates us money in such a way that we could just sustain ourselves, effectively giving us a little money tree, freeing our tie and giving us the privilege of boundless creative pursuit. --&gt;
&lt;!--## Some theory

I’ve always been interested in how socialists or social movements can appropriate key pillars of capitalism for the good of the all humans. Marx’s predictions of a machine that can produce value without wearing down is interesting in the light of contemporary tech. This hypothetical machine does some operation on some resource &amp; creates value, with the only input being energy rather than labour itself. It’s very clear that in our society these machines are software, applications or “apps”. Unfortunately for us, most of our favourite pieces of software being exploited by the capitalist class. A machine that doesn’t wear down and makes a hypothetical *infinite* amount of money is appealing to capitalists and their bottom line, which is why they own most of the software companies out there. This is why venture capitalism is almost part of the tech industry itself. But what if we seize these means of production for ourselves? What if we make a tiny money machine to sustain my creative goals? It’s a highly individualistic approach that is in nowhere near the revolution that brings about the dictatorship of the proletariat, but it will have to do for now. We’re not here to change the world, we’re here to get the *means* to change the world. If we can get enough money out of this to sustain ourselves, we can potentially make culture that fundamentally questions and attacks the pillars on which neoliberalist capitalism stands✨ (ideally, obviously that’s a bit of a reach)--&gt;
&lt;h2 id=&quot;all-software-ever-is-just-excel-with-extra-steps&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#all-software-ever-is-just-excel-with-extra-steps&quot; aria-label=&quot;Anchor link for: all-software-ever-is-just-excel-with-extra-steps&quot;&gt;All software ever, is just Excel with extra steps&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;!--I worked in the tech industry for several years, it&#x27;s depressing. The worst part is not even the managers, CEOs, sales people or the relentless alcoholism of everyone involved in order to just cope with the outright boring nature of our work.--&gt;
&lt;p&gt;The worst part, is that no matter what company you work for, you’re essentially making Microsoft Excel with extra steps. What do I mean by that? Very simple, the foundation of any application is to take some data and do some kind of operation on it. We usually do this by having some code run on a computer, accessing a database and then operating on some data, returning some and&#x2F;or storing some results. However, most computing that needs to be done by people on a day-to-day basis, doesn’t need “the cloud” in order to be effective. Most apps are just some data that the user wants to do some operation on and see the result. I found this out when I pitched the entire software package I was making to an artist friend of mine. He just responded, “I can do all of this with Excel though, why would I pay 100 euro a month for that?”. He was right. After that, every single software company I worked for was evaluated by this measurement. I soon realized that yes, even though the sheer magnitude of some computing and amount of data might require some custom solution sometimes, the software itself could almost always be easily replaced by Excel. This is a hilariously stupid oversimplification, but in an alternate world where Microsoft somehow gained a 100% monopoly on computing, they would’ve probably found a way to use Excel in these contexts anyway. Obviously, there is enough to be said for the need for custom applications that do things their own ways, for one, I would hate people to have to deal with the UX of Excel when they just want to order some food through Uber Eats. Doesn’t change the inherent depressing thought of “making another Excel” every single time I worked for another software company.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;making-notion-cafe&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#making-notion-cafe&quot; aria-label=&quot;Anchor link for: making-notion-cafe&quot;&gt;Making Notion Cafe&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;So, in October of last year, I really wanted to solve a problem I was having. I’m writing a lot of my notes and tasks in Notion. If you don’t know what Notion is, it’s basically your favourite parts of Evernote combined with Trello. It is &lt;em&gt;amazing&lt;&#x2F;em&gt; software if you need to process information and plan a lot of work. Here’s the problem with Notion. It has a really cool way to show your tasks, pages and appointments in a Calendar view. However, there is &lt;strong&gt;no&lt;&#x2F;strong&gt; way to sync this calendar with something like your phone, smartwatch or calendar on your laptop. I was an avid user of my calendar app on my different devices as well, but I wanted to make sure that I didn’t have to write all of my appointments &lt;em&gt;twice&lt;&#x2F;em&gt;. What if I used Notion’s API to write a small script that converts it into iCal format? 🤔✨&lt;&#x2F;p&gt;
&lt;p&gt;So I did! And guess what, when I initially did some googling to find a solution for this problem, I found out that &lt;em&gt;loads&lt;&#x2F;em&gt; of other people were having problems with this as well. What if I offered this script to them as a service? I mean, most people that use Notion have some form of a disposable income and are willing to spend money to improve their productivity, right? They did.&lt;&#x2F;p&gt;
&lt;p&gt;So I started with a public integration with Notion that provided the Notion-appointment-to-calendar-appointment converter and went to all the Reddit threads where people were asking for a solution to the problem.&lt;&#x2F;p&gt;
&lt;p&gt;Over the span of the last 11 months, more than 15 thousand people have found their way to the integration. The platform has about 6 thousand daily active users and is going towards 200 paying users! This all comes down to more than 500 EUR monthly recurring revenue. I spend this money on things that make me happy and invest it into community projects like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gamedev.lgbt&#x2F;&quot;&gt;gamedev.lgbt&lt;&#x2F;a&gt; 🏳️‍🌈✨&lt;&#x2F;p&gt;
&lt;h2 id=&quot;designing-my-software-based-money-tree&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#designing-my-software-based-money-tree&quot; aria-label=&quot;Anchor link for: designing-my-software-based-money-tree&quot;&gt;Designing my software based money tree&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So how do we appropriate tech into something that we can build our life on? There is a Venn diagram of different things to come together for the application that can do that.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It needs to have a demographic willing to pay some amount of money&lt;&#x2F;li&gt;
&lt;li&gt;It needs to be manageable by 1 individual (me)&lt;&#x2F;li&gt;
&lt;li&gt;It needs to be low-to-zero cost in order to operate&lt;&#x2F;li&gt;
&lt;li&gt;The software shouldn’t fundamentally &lt;em&gt;hurt&lt;&#x2F;em&gt; anyone&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;it-needs-to-have-a-demographic-willing-to-pay-some-amount-of-money&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-needs-to-have-a-demographic-willing-to-pay-some-amount-of-money&quot; aria-label=&quot;Anchor link for: it-needs-to-have-a-demographic-willing-to-pay-some-amount-of-money&quot;&gt;It needs to have a demographic willing to pay some amount of money&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Why start a small business for a demographic that &lt;em&gt;isn&#x27;t&lt;&#x2F;em&gt; willing to pay money for your service, but a lot of contemporary apps seem to be focussed on solving problems that people might have no need to pay for, or where the last resort is always selling data. I didn’t want that, I wanted to provide a service where people just pay upfront for the service rather than having to do shady data practices (which I don’t even know how to do).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;it-needs-to-be-manageable-by-1-individual-me&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-needs-to-be-manageable-by-1-individual-me&quot; aria-label=&quot;Anchor link for: it-needs-to-be-manageable-by-1-individual-me&quot;&gt;It needs to be manageable by 1 individual (me)&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Manageability sounds rather simple, but a lot of people might get it wrong. The solution to the ever-increasing complexity of DevOps &amp;amp; tech in general isn’t to automate it, but to eliminate the need for the complexity to begin with. If you are designing your application to be run in the simplest &amp;amp; least complex way, it will make it more manageable. It also means that you design your code not for cleanness, structure or aestheticism but rather for manageability over the long run. You’ll see this come back several times as some of my code seems counter-intuitive to what most people would consider “correct”. The end goal should be for the project to be manageable, not for it to be “perfect”.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;it-needs-to-be-low-to-zero-cost-in-order-to-operate&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-needs-to-be-low-to-zero-cost-in-order-to-operate&quot; aria-label=&quot;Anchor link for: it-needs-to-be-low-to-zero-cost-in-order-to-operate&quot;&gt;It needs to be low-to-zero cost in order to operate&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is mostly about not having scalability problems, either now or down the line. If an application like this requires costs that outweigh the revenue, then we’re designing a money pit rather than a sustainable business. If we keep our costs low for hosting, deployment &amp;amp; operation, we can extract the most profit per paying user, therefore making it easier to get to our goal: being able to pursue creative freedom!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-software-shouldn-t-fundamentally-hurt-anyone&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-software-shouldn-t-fundamentally-hurt-anyone&quot; aria-label=&quot;Anchor link for: the-software-shouldn-t-fundamentally-hurt-anyone&quot;&gt;The software shouldn’t fundamentally &lt;em&gt;&lt;strong&gt;&lt;strong&gt;hurt&lt;&#x2F;strong&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; anyone&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The last point is one that I think should be evaluated more in tech. One could easily make another platform with some bullshit in combination with AI, or doing something to automate a task done by a secretary. My problem with these types of software solutions is that they make working class people obsolete rather than enhancing their (work)lives. My first job at a startup was mostly characterized by a story that the CEO once told me. He was giving a demo of our software to a medium-sized company. Our software was rather simple, it was the combination of a customer management system, with human resources and billing. These tasks were given to a secretary within the company, a secretary that was sat in the same room as my boss while he was giving the demo. When it was time for a round of questions nearing the end, she raised her hands and asked, “But if all of this is automated, what work is there for me to do?”. And this is where my boss really likes to share the punchline of his story: he told her “Well, I hope your boss really likes you enough to come up with some work for you!”. It’s absolutely disgusting, and it made me forever try and design applications that don’t have the opportunity to destroy people’s livelihoods.&lt;&#x2F;p&gt;
&lt;p&gt;The result of all of these design decisions together? A manageably small, cheap to operate &amp;amp; profitable platform. An interesting side effect is that the profit margins of this service are big enough for me to run it by myself and maybe make a living, but would be &lt;em&gt;completely&lt;&#x2F;em&gt; impossible to run a larger business on. This means that this niche is not easily taken over by competitors with bigger engineering teams. The only risk realistically is that &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@mark_kelly&#x2F;the-curious-case-of-sherlocking-229446a5824f&quot;&gt;Notion decides to sherlock&lt;&#x2F;a&gt; the feature and give users access, completely shutting down my business 😕&lt;&#x2F;p&gt;
&lt;h1 id=&quot;implementing-the-software&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#implementing-the-software&quot; aria-label=&quot;Anchor link for: implementing-the-software&quot;&gt;Implementing the software&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;em&gt;You can skip this if you&#x27;re not a programmer&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The application I landed on is a simple web service written in Rust with SQLite &amp;amp; Litestream as a database. I deploy using &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;fly.io&lt;&#x2F;a&gt;, and I use Stripe as a payment gateway. By leveraging Rust, I have safety guarantees that make sure I don’t have to wake up in the middle of the night to an inbox full of emails telling me that the service doesn’t work because of a trivial software error. By using &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;fly.io&lt;&#x2F;a&gt; I don’t have to worry about the hellscape that is deployment, I just need to make sure that it compiles on their remote builder server, and it should just be fine. Using Stripe, I don’t have to worry about all the different payment methods, and I can just launch the service globally without having to get into the nitty-gritty of setting up payment stuff for every single country.&lt;&#x2F;p&gt;
&lt;p&gt;I also decided to run Rust in the frontend, so I didn’t have to deal with a new JavaScript framework (again) and I could have the same safety guarantees in the frontend as I do in the backend. Also, because Rust is such a low-level language with great community libraries, it was easy to implement a service that is hyperefficient in computing and memory footprint. I used to run a 256MB RAM with one shared CPU core for the majority of the software’s life. I recently had to up it to 1GB RAM because Litestream is a bit RAM hungry, but I’m working on removing that in the future.&lt;&#x2F;p&gt;
&lt;p&gt;The only bottleneck that the platform has is my bad implementation of things &amp;amp; the speed between my service and the Notion API. This means that with the speed of Rust’s data processing, I can process thousands of calendars a second without having to break the bank for that computing. I don’t even need to serve the calendar as quickly as possible, because the client that the user uses, is calendar software that was built to work with archaic calendar servers that are Way slower. This means 50ms+ response times aren’t bad, since the user usually isn’t aware of their calendar updating.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-resulting-platform&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-resulting-platform&quot; aria-label=&quot;Anchor link for: the-resulting-platform&quot;&gt;The resulting platform&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I am able to service more than 6 thousand daily active users and more than 300k+ requests a day, &lt;em&gt;without&lt;&#x2F;em&gt; having to spend a penny: the resource use is so low that the total costs of running my service is less than $5, which &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;fly.io&lt;&#x2F;a&gt; will just give you for free!&lt;&#x2F;p&gt;
&lt;p&gt;But then how do I make money? Like most services out there, there’s a free version of the service and a Premium version with more features. I really don&#x27;t like the way that other services lock a majority of their features behind a Premium model, so I decided to make most features available for free, but just with the limit of having 1 calendar, which is enough for most users. When the user is a power user that is likely to pay for Notion already &amp;amp; makes use of advanced Notion features, a Premium plan is more relevant. I didn’t want to make it too expensive either, it shouldn’t be more expensive than Notion itself, that would be silly.&lt;&#x2F;p&gt;
&lt;p&gt;I settled on 3 EUR a month, which after Stripe fees I keep 2.75 EUR, after taxes, about 2,27 per user per month. At the time of writing, I have 197 paid users, which Stripe tells me gives me 541,75 EUR a month. But there’s a small twist that usually makes this number a lot higher. I give users the option to pay for an entire year, giving them a small discount, but giving me a lot more money upfront. A yearly plan is 30 EUR, which loses me a bit of money, but gives a big boost on a monthly basis. This month (September 2023) I’m making about 851,46 EUR!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-future&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-future&quot; aria-label=&quot;Anchor link for: the-future&quot;&gt;The future&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The low maintenance nature of the project makes it nice to let it just sit for a bit, but with the increase of scale, there are definitely some things that need to change for the future of the platform to be equally fruitful. Right now, I’m running against the limit of what SQLite can do for me, safely. It&#x27;s okay with the total amount of transaction being below 1 million, but once you get above the marker, it’s more spicy to guarantee data integrity in some situations. I’m currently rewriting the platform to use an external database server to guarantee data safety.&lt;&#x2F;p&gt;
&lt;p&gt;Next thing is to do a whole load of caching of user appointments, so I don’t have to reload all the users’ appointments, but rather only the appointments that are new or updated. Once that is done, I’m going to move on to implementing one of the most often requested features: making it possible to change and add appointments in your calendar software and have those changes reflected inside of Notion. I have a strong suspicion that the reason why a lot of users don&#x27;t end up paying or using the service, is because they expect this to be a feature. Once I release the feature, I expect the conversion from (inactive&#x2F;free) users to paying user should increase significantly, potentially doubling my revenue, without increasing operating costs by a lot.&lt;&#x2F;p&gt;
&lt;!--What am I going to do with this new-found livelihood? I’m not going to quit working, I love my job as a technical artist at an indie studio. But rather, when my work on their title is done, I can freely dedicate my time as I see fit. Usually I have to evaluate my creative projects by their ability to pay my bills in some way, completely eliminating some avenues of creativity. With my little money tree, it’s all of a sudden a possibility to these things. I can be my own investor: an investor that wants to see more anti-capitalist culture to fundamentally question the world around us. --&gt;
&lt;p&gt;Maybe Notion pulls the plug on my integration before I can get anything true financial stability out of it, who knows. Regardless, I enjoyed this as an exercise in engineering.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I LOVE RUST&#x27;S ERROR HANDLING 🦀✨</title>
        <published>2022-08-23T00:00:00+00:00</published>
        <updated>2022-08-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/i-love-rusts-error-handling/"/>
        <id>https://bram.dingelstad.works/blog/i-love-rusts-error-handling/</id>
        
        <content type="html" xml:base="https://bram.dingelstad.works/blog/i-love-rusts-error-handling/">&lt;p&gt;so i&#x27;ve had the chance to try out some Rust programming for &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;notion-to-calendar.fly.dev&#x2F;&quot;&gt;a project i&#x27;m working on&lt;&#x2F;a&gt;.
first of all: it comes as no surprise that Rust is one of the most loved languages in most polls i come across. it&#x27;s awesome and i &lt;em&gt;love&lt;&#x2F;em&gt; it. i&#x27;ve never been sceptical of Rust, so it&#x27;s hardly been a conversion for me, but i&#x27;ve found low-level languages quite hard to understand. besides most language&#x27;s inaccessibility towards beginners or self-taught developers, most languages also have (relatively) poor communities &amp;amp; packages compared to more modern interpreted languages imo, but i&#x27;d digress. 😩&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;ERROR HANDLING&lt;&#x2F;strong&gt;. i hate it most of the time. it&#x27;s scary. i never quite learned how to do it properly, i usually let someone else architect how it works and then follow their style. not with Rust, the language actually has really good defaults for error handling that i &lt;em&gt;love&lt;&#x2F;em&gt;✨&lt;&#x2F;p&gt;
&lt;p&gt;so im gonna try to explain this and get you up to speed to how certain things work with Rust. i&#x27;m assuming you know some basic programming terms.&lt;&#x2F;p&gt;
&lt;p&gt;errors happen, usually when you don&#x27;t want to, sometimes because you didn&#x27;t see it coming.
in most cases, functions where something &lt;em&gt;can&lt;&#x2F;em&gt; go wrong return a thing called a &lt;code&gt;Result&lt;&#x2F;code&gt;.
a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;result&#x2F;enum.Result.html&quot;&gt;&lt;code&gt;Result&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is an enum which represents something that went fine with &lt;code&gt;Ok&lt;&#x2F;code&gt; or something that went wrong with &lt;code&gt;Err&lt;&#x2F;code&gt;. kind of a more complicated &lt;code&gt;true&lt;&#x2F;code&gt; or &lt;code&gt;false&lt;&#x2F;code&gt; representing if a function worked succesfully or not.&lt;&#x2F;p&gt;
&lt;p&gt;these enums also hold the value they&#x27;re representing like so:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#31333d;color:#ffffffc4;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; both these functions return a Result&amp;lt;u32, ParseError&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; works &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; returns Ok(4)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; does_not_work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; returns Err(ParseError)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;the program doesn&#x27;t crash when there&#x27;s an error, but Rust does expect you to handle the situation. you either have the choice of simply calling &lt;code&gt;.unwrap()&lt;&#x2F;code&gt; on the &lt;code&gt;Result&lt;&#x2F;code&gt; to crash in case it doesn&#x27;t work (or use &lt;code&gt;.expect(&quot;&amp;lt;message&amp;gt;&quot;)&lt;&#x2F;code&gt; to be a bit more explicit).&lt;&#x2F;p&gt;
&lt;p&gt;your other option would be to &lt;code&gt;match&lt;&#x2F;code&gt; the &lt;code&gt;Result&lt;&#x2F;code&gt; to its &lt;code&gt;Ok&lt;&#x2F;code&gt; and &lt;code&gt;Err&lt;&#x2F;code&gt; parts and handle the cases there.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#31333d;color:#ffffffc4;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; crashes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;hello&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; crashes
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; crashes_but_nicer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;hello&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;()
&lt;&#x2F;span&gt;&lt;span&gt;     .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;A number to parse.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; crashes with a message
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; has_number_when_correct_exits_function_if_not &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= match &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;foobar&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(number) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; number,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(error) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;=&amp;gt; return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(error)
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;the next best thing that i learned? that last statement with the match, has a shortcut that&#x27;s &lt;em&gt;so nice&lt;&#x2F;em&gt; to write. the &lt;code&gt;?&lt;&#x2F;code&gt; operator. its simply expands to the above &lt;code&gt;match&lt;&#x2F;code&gt; statement. you can also use it in between accessors!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#31333d;color:#ffffffc4;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F;this is the same as the previous `match` statement
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; failed_parse_but_shorter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;foobar&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; bubbles up Err(ParseError)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; two_the_power_of_four &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;pow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; returns 16
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;small caveat: you can only use the &lt;code&gt;?&lt;&#x2F;code&gt; operator if the function&#x2F;method signature is the same as the error that you&#x27;re bubbling upwards.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#31333d;color:#ffffffc4;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; this function will compile
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;parse_and_add_two&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;some_value&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;, ParseError&amp;gt; { 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; parse_value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; some_value.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  parse_value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;+ 2
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; an example enum
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span&gt;HttpError {
&lt;&#x2F;span&gt;&lt;span&gt;   ConnectionProblem
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F;this function will not compile
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;get_http_number_and_add_two&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;, HttpError&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= do_http_request&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; returns a number
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; parse_value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; result.parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; will not compile
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; return type &amp;lt;u32, HttpError&amp;gt; != &amp;lt;u32, ParseError&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;here comes the greatest part: you can &lt;em&gt;convert&lt;&#x2F;em&gt; one error to the other type so the code &lt;em&gt;does&lt;&#x2F;em&gt; compile! you just need to convert the &lt;code&gt;ParseError&lt;&#x2F;code&gt; to an &lt;code&gt;HttpError&lt;&#x2F;code&gt; and you&#x27;re done!
with the &lt;code&gt;std::convert::From&lt;&#x2F;code&gt; trait you can do this easily like so:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#31333d;color:#ffffffc4;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::convert::From;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span&gt;HttpError {
&lt;&#x2F;span&gt;&lt;span&gt;    ConnectionProblem,
&lt;&#x2F;span&gt;&lt;span&gt;    ParsingNumberProblem(ParseError)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;From&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;ParseError&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;HttpError {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;: ParseError) -&amp;gt; HttpError {
&lt;&#x2F;span&gt;&lt;span&gt;        HttpError::ParsingNumberProblem(error)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;these things combine to be an &lt;em&gt;awesome&lt;&#x2F;em&gt; language that literally has a compiler &lt;strong&gt;dedicated&lt;&#x2F;strong&gt; to making sure you cover all these cases. i eliminated SO many edge cases and error cases because of this design decision on a LANGUAGE LEVEL. &lt;em&gt;i love it&lt;&#x2F;em&gt;✨&lt;&#x2F;p&gt;
&lt;p&gt;anyway, if that rambling made any sense to you, feel free to follow me on here or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dingelstad.xyz&#x2F;&quot;&gt;stalk me on other places on the internet&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Making &quot;Duifje&quot;, my helpful friend for sharing fun things</title>
        <published>2021-11-05T00:00:00+00:00</published>
        <updated>2021-11-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/making-duifje-my-helpful-friend-for-sharing-fun-things/"/>
        <id>https://bram.dingelstad.works/blog/making-duifje-my-helpful-friend-for-sharing-fun-things/</id>
        
        <content type="html" xml:base="https://bram.dingelstad.works/blog/making-duifje-my-helpful-friend-for-sharing-fun-things/">&lt;p&gt;&lt;em&gt;In this article I quickly go through some of the ideas, code and implementation of this little project to maybe inspire you to do something similar with your Notion setup.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-it-all-started&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-it-all-started&quot; aria-label=&quot;Anchor link for: where-it-all-started&quot;&gt;Where it all started&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I tend to write from time to time and a thing I really hate about having a website or a place to post my thoughts is the UI or the workflow I have to go through in order to post something. Most editors that I&#x27;ve used on platforms like Wordpress or Ghost don&#x27;t come close to a good user experience. When I started pre-writing my articles on Notion however, I really liked the UX of just writing an article in that software. When I read that the platform was gonna receive an API, I knew that I was gonna make something to centralise all the content I make once and for all! I made &lt;em&gt;Duifje&lt;&#x2F;em&gt; (which means little pigeon&#x2F;dove in Dutch). &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=DNBcTqVZY8Q&quot;&gt;A little inspiration I took from the classic musical film &quot;Ja Zuster, Nee Zuster&quot;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-does-it-do&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-does-it-do&quot; aria-label=&quot;Anchor link for: what-does-it-do&quot;&gt;What does it do?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It simply automates the most frustrating part of writing content: publishing. My eventual use cases include: writing&#x2F;publishing blog posts, writing tweets with content to engage with my game&#x27;s audience and more! All with automatic scheduling for the posts and tweets!&lt;&#x2F;p&gt;
&lt;p&gt;How does it work exactly? It takes a Notion page in a database (regardless of view) and looks for which articles are marked &quot;Ready to Release&quot; in their status. Then it checks what type they are and get&#x27;s the appropriate piece of code to publish it on the appropriate platform. The prototype of this project included two content &quot;destinations&quot;: my personal blog &amp;amp; a BBCode text file for posting on forums. This was useful for creating the devlog for my game &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ClairvoyageGame&quot;&gt;&quot;Clarice Clairvoyage&quot;&lt;&#x2F;a&gt;. This way I could just write the devlog once and have the content ready for these different platforms.&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;brams-bucket.s3.nl-ams.scw.cloud&amp;#x2F;pigeon&amp;#x2F;29100d4de71b785e728ef188c664b57e2ed05a84.png&quot;  &#x2F;&gt;
    
&lt;&#x2F;figure&gt;
&lt;p&gt;After going through the release cycle, it would automatically publish the article on my website through the Github API and upload a text file with BBCode text to Notion. Then Duifje puts the article in the &quot;Released&quot; status, which lets me know that it&#x27;s been released! When I refresh my website the article is live and when I look at the Notion page I can see the BBCode text uploaded!&lt;&#x2F;p&gt;
&lt;p&gt;In the new version I wrote last weekend I added support for Tweets by adding a Twitter type to my Notion database. After writing a Twitter module for Duifje, I can now tweet from my Notion workspace! From now on I can just create a backlog of content for &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;bram_dingelstad&quot;&gt;my Twitter&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ClairvoyageGame&quot;&gt;Clarice Clairvoyage&#x27;s Twitter&lt;&#x2F;a&gt; without any issues! No more early posting when I don&#x27;t have to! I&#x27;m also freed from the absolutely &lt;em&gt;horrible&lt;&#x2F;em&gt; Twitter tweet schedule planner.&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;brams-bucket.s3.nl-ams.scw.cloud&amp;#x2F;pigeon&amp;#x2F;1773ada96ae160cbe0b449ac060f6a21c587a103.png&quot;  &#x2F;&gt;
    
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;closer-look-at-some-code&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#closer-look-at-some-code&quot; aria-label=&quot;Anchor link for: closer-look-at-some-code&quot;&gt;Closer look at some code&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;Feel free to skip this&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;All of the code we&#x27;re going through is somewhat simplified and most of the error catching and more defensive code pieces are omitted. With that out of the way, first I get all of the pages that I put in the &quot;Ready to be released&quot; column of my content. The Notion SDK makes the really easy, which I use the Deno version of. The Node version seems to be pretty good as well.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#31333d;color:#ffffffc4;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;ready_pages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;notion&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;databases&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    database_id&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;NOTION_DATABASE_ID&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    filter&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        property&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;Status&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        select&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            equals&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;Ready to be released&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next up, we&#x27;re gonna have to do the bulk of the functionality in one go, so bear with me.
We&#x27;re gonna get the page blocks that hold our content, check if the publish date is correct and see what modules we want to use to publish. All of these things are determined by properties that I just set from Notion. So I don&#x27;t need to interact with the program at all as a user, it&#x27;s all data driven!&lt;&#x2F;p&gt;
&lt;p&gt;After that we go through all of these modules and render the content (We&#x27;ll get into one of these modules soon enough). After that we simply mark the page we published as &quot;Released&quot; and we&#x27;re done!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#31333d;color:#ffffffc4;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;ready_pages&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;results&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; Get more detailed block information and enrich with children
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;notion&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;blocks&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;retrieve&lt;&#x2F;span&gt;&lt;span&gt;({ block_id&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.id })
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;children &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;utils&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;get_tree&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;notion&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; Check if the article can be released based on date
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;date &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;Publish Date&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;date
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;date &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;&amp;amp;&amp;amp; new &lt;&#x2F;span&gt;&lt;span&gt;Date(&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span&gt;.start).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;getTime&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;now&lt;&#x2F;span&gt;&lt;span&gt;())) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;continue
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; Use the &amp;quot;Type&amp;quot; attribute to for which module its meant for
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;modules &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;blog&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;twitter&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;bbcode&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.tags.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;tag &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;multi_select&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;.name).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;indexOf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;tag&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;!== -1
&lt;&#x2F;span&gt;&lt;span&gt;            ).length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;!= 0
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; Go through all the available modules, render &amp;amp; publish
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;modules&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3cbe3;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;content &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;children&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e7e7e7;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;publish&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#707180;&quot;&gt;&#x2F;&#x2F; Put the article under the &amp;quot;Released&amp;quot; column!
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#db7c6d;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;notion&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2ba43;&quot;&gt;update&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;        page_id&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#a3cbe3;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.id,
&lt;&#x2F;span&gt;&lt;span&gt;        properties&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            Status&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                select&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                    name&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#db7c6d;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;Released&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#dbbb3d;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now for some more magic: the modules. To make sure that I can put all different kinds of content in one place and that it&#x27;ll all publish correctly I use a module pattern. These modules are just objects with a &lt;code&gt;render&lt;&#x2F;code&gt; and &lt;code&gt;publish&lt;&#x2F;code&gt; method in them. It could be shorter in one step, but I separated the two to leave some room for dry running and stuff.&lt;&#x2F;p&gt;
&lt;p&gt;In between the blocks in a page (aka the children mentioned before) get parsed and formatted to work for the platform I&#x27;m publishing in. For my blog, this means a little Notion-to-markdown conversion. For Twitter, this means a simple flat text with some checks to make sure it stays under 280 characters. It also makes it easy to make your own little modules. Just write a thing that parses the Notion page information and a small function that interfaces with another platform and you&#x27;re done! Some ideas could include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A Mailchimp integration that automatically sends out a mailinglist based on a page&lt;&#x2F;li&gt;
&lt;li&gt;A Twilio integration that sends out a text to a group of people&lt;&#x2F;li&gt;
&lt;li&gt;Other social media integrations for sharing media&lt;&#x2F;li&gt;
&lt;li&gt;Running the content through a text-to-speech program for accessibility&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;some-small-additions-future-plans&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#some-small-additions-future-plans&quot; aria-label=&quot;Anchor link for: some-small-additions-future-plans&quot;&gt;Some small additions + future plans&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Since I initially started on this article and release I&#x27;ve already a myriad of different modules and features. I&#x27;ve made a little markdown output for sharing my posts on Markdown supported platforms that don&#x27;t have an API. I also added a web frontend that&#x27;s only accessible through Notion.&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;brams-bucket.s3.nl-ams.scw.cloud&amp;#x2F;pigeon&amp;#x2F;2025563a89882d11c5e08185a4f1f700de984233.png&quot;  &#x2F;&gt;
    
&lt;&#x2F;figure&gt;
&lt;p&gt;This way I can activate the program right from my Notion page. I can also see when the program last ran. This combined with an automatic execution every 15 minutes makes for the best experience.&lt;&#x2F;p&gt;
&lt;p&gt;In the future I might add more specific options such as Twitter accounts (for tweeting on behalf of my indiegame&#x27;s account) or automatic scheduling of tweets if they don&#x27;t have a date assigned.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bram-dingelstad&#x2F;duifje&quot;&gt;The code is available on Github&lt;&#x2F;a&gt; if you want to take a look! Let me know what you think!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Moving to zero- dollar and upkeep setup</title>
        <published>2021-09-21T00:00:00+00:00</published>
        <updated>2021-09-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/moving-to-zero-dollar-and-upkeep-setup/"/>
        <id>https://bram.dingelstad.works/blog/moving-to-zero-dollar-and-upkeep-setup/</id>
        
        <content type="html" xml:base="https://bram.dingelstad.works/blog/moving-to-zero-dollar-and-upkeep-setup/">&lt;p&gt;I&#x27;m back from a quite long hiatus of my weekly blog posts. For now, I&#x27;ll try to do them semi-regular again, maybe even on a monthly, bi-weekly or whenever-I-feel-like-it basis. Thank you if you&#x27;ve been waiting to read my new post (although I doubt it). Also, this article has &lt;strong&gt;a lot&lt;&#x2F;strong&gt; of personal backstory in the beginning, if you just want to know the &quot;free&quot; and useful part of this article, feel free to skip that part.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;growin-up-with-linux&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#growin-up-with-linux&quot; aria-label=&quot;Anchor link for: growin-up-with-linux&quot;&gt;Growin&#x27; up with Linux&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;One vacation back when I was in highschool as a 13 year old, I grew tired of playing Minecraft (which was the bomb at the time as well) and decided I wanted to do something a little bit more productive. Back then I had convinced a bunch of my friends that I was a &quot;hacker&#x27;, even though I knew nothing about anything related to pen-testing or programming. I knew a lot about computers, but mainly just how to do silly stuff on Windows. Being as bored as I was, I decided to take a look at this &quot;Linux&quot; thing I had experimented installing sometime before. I had seen it installed on a computer in some weird humid basement of my mom&#x27;s acquaintance, but never had I really dabbled in it further.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&quot;It was about time I started learning something&quot;&lt;&#x2F;em&gt; I must&#x27;ve thought to myself as I pushed the DVD (yes, those were the prime way of installing OSes back then) with the Ubuntu install in my computer. Over the next few years I hadn&#x27;t only mastered the CLI and Linux it&#x27;s most basic form. I had started converting left over computers and laptops into home servers that I could use to deploy and run &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bram-dingelstad&#x2F;rooster.io&quot;&gt;a student scheduling website&lt;&#x2F;a&gt;. In my final year of highschool &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bram-dingelstad&#x2F;hoewerkt&quot;&gt;I even wrote an essay on its inner workings&lt;&#x2F;a&gt; and used it to pass my final year exams! All of this is what got me into programming and development in the first place. It &lt;em&gt;even&lt;&#x2F;em&gt; landed my first job at a tech company ~3 months later.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;but-i-over-did-it-and-it-wasn-t-gonna-last&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#but-i-over-did-it-and-it-wasn-t-gonna-last&quot; aria-label=&quot;Anchor link for: but-i-over-did-it-and-it-wasn-t-gonna-last&quot;&gt;But i over did it and it wasn&#x27;t gonna last&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Over the next 2~3 years I went through all sorts of server admin and personal linux setups. I went from Ubuntu to Ubuntu-based distros like Xubuntu, going as far and deep as Arch Linux and finally settling for something like Elementary OS. But as my years managing the complexity of Linux began to grow, it felt more and more like a chore. I did most of my work because I found it fun and I had learned as much as I could from Linux.&lt;&#x2F;p&gt;
&lt;p&gt;The industry was changing in favor of larger ways of managing container-based solutions like Kubernetes. All of the sudden, it became a lot more frustrating if you just wanted to do something on a computer and you were hindered by something that wasn&#x27;t a big problem to fix, but a problem nonetheless. I want to make absolutely clear that I don&#x27;t think differently of Linux the distros I used. Nor am I implying using Linux for a server or Desktop is a bad idea. I just merely felt my personal preference for computing started to change. And in the last year or so, I started shutting down all my VPSes and other hosted forms of Linux in favor of something else: low-upkeep and low-cost services.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;service-to-the-rescue&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#service-to-the-rescue&quot; aria-label=&quot;Anchor link for: service-to-the-rescue&quot;&gt;Service to the rescue!&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let me say this: I don&#x27;t like most of startup&#x2F;tech businesses. I specifically despise &quot;services&quot; planning to &quot;revolutionise&quot; something for you or your business. What I &lt;em&gt;can&lt;&#x2F;em&gt; appreciate, is the effort to make something more efficient or more fun to do. Services that are made to take away the painstaking steps of a process or replacing it with a better process are worth a lot. Services that as easy to use as well designed furniture or home products. Basically, services with great UX that get out of the way of what you&#x27;re trying to do.&lt;&#x2F;p&gt;
&lt;p&gt;A person that learned me a lot about this is Berco Beute. Working with him and learning is philosophy of &quot;designing &amp;amp; building from user intention&quot; has been largely responsible for my change of feelings on this topic. Although his vision of computing and tech is incredibly underrepresented and under-appreciated with such a small quote, I&#x27;ll refrain from explaining most of his philosophy (or my interpretation) for the sake of keeping this article readable for now.&lt;&#x2F;p&gt;
&lt;p&gt;I started looking for services that are cheap and deliver on this promise. I wanted something to replace my blogging-, code repo-, computing- and database setup all together. No one service is gonna be perfect for all of this. But I did find a combination that feels really good up until now, and are &lt;em&gt;completely free&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Vercel (for deploying frontends &amp;amp; my blog)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;Fly.io&lt;&#x2F;a&gt; (for generic computing or hosting bigger projects)&lt;&#x2F;li&gt;
&lt;li&gt;Notion (for CMS and generic data storage)&lt;&#x2F;li&gt;
&lt;li&gt;Litestream + Scaleway (for more extreme versions of data storage)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;vercel&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#vercel&quot; aria-label=&quot;Anchor link for: vercel&quot;&gt;Vercel&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Having a way to publish things with low effort is great! Although usually, this means getting something like Squarespace, Wix or (the worst of the worst) Wordpress. I run this blog with Hugo (the project, not the brand nor my super cool Portuguese colleague) and I was looking for a way to not manually run a weird bash-based webhook thing to build and deploy it with nginx on my Linux VPS.&lt;&#x2F;p&gt;
&lt;p&gt;Vercel is just &lt;em&gt;super&lt;&#x2F;em&gt; easy to use. You just set it up to compile Hugo and it &lt;em&gt;just does&lt;&#x2F;em&gt;. Every time I commit to my repo it just automatically compiles and pushes it to this blog: &lt;em&gt;&lt;strong&gt;amazing!&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; It also has some really cool serverless functionality if your computation needs are smaller. Saves me a ton of hassle. The true value of this isn&#x27;t that it&#x27;s automated, but that it&#x27;s automated without me having to worry about it. You can get up and running with Vercel in less than 30 minutes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fly-io&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fly-io&quot; aria-label=&quot;Anchor link for: fly-io&quot;&gt;Fly.io&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This service is really nice. If you&#x27;ve had to use Docker containers in your work, this service shouldn&#x27;t be too hard to use. Even for beginners of container tech this should be do-able. If you&#x27;re newer to CLI in general this might be a little bit too much however. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;Fly.io&lt;&#x2F;a&gt; is an amazing service that takes a docker container, publishes it in a datacenter and giving you access through a subdomain and IP. Most (if not all) of the interaction is done through their CLI tool which makes it easy to integrate into your workflow. It also has a free offering for people trying the service out or using it for personal use! You can have three small services of your own running for exactly $0! I use this service to run a small project named &quot;Pigeon&quot; to automatically convert my Notion pages to markdown and publish it on my blog through Vercel! (If there&#x27;s enough interest in Pigeon I can write a post about it as well)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;notion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#notion&quot; aria-label=&quot;Anchor link for: notion&quot;&gt;Notion&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Okay, so I said earlier on in the article that I hate services that claim to &quot;revolutionise&quot; something. Well... This service actually &lt;em&gt;did&lt;&#x2F;em&gt; revolutionise the way I keep notes or keep a knowledge base for myself and project&#x2F;organisations I work with! I even write these articles out on Notion before using my aforementioned combination of Vercel &amp;amp; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;Fly.io&lt;&#x2F;a&gt; to publish them. This is the &quot;CRM&quot; part I mentioned in the list. You can easily write large pieces of text with pretty pictures or other forms of content. I also use it to convert my devlogs of Clarice Clairvoyage to the blog format and a format I use to post to different forums like TIGSource or the PICO-8 forums.&lt;&#x2F;p&gt;
&lt;p&gt;Another way to use Notion is through it&#x27;s (somewhat recently announced) API! I used &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jamalex&#x2F;notion-py&quot;&gt;&lt;code&gt;notion-py&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; before the API came out and haven&#x27;t gotten to switching yet. However, my ideas with the API have always been in a way of storing information where you&#x27;d normally use a small service or database. An example: Using it as a simple way to do analytics on game prototypes. Have the prototype call when a certain event happens so you can see how many times players interact in a certain way with your game. No need to build weird platforms to see your data, just make a cool Notion page! There&#x27;s a point at which this obviously doesn&#x27;t scale anymore, so use that idea with caution.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;litestream-scaleway&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#litestream-scaleway&quot; aria-label=&quot;Anchor link for: litestream-scaleway&quot;&gt;Litestream + Scaleway&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Now, if you need a large amount of storage for a project, you would want something a little bit more SQL-y. Well, how about SQLite mixed with some auto-syncing and object storage? It&#x27;s everything you&#x27;re used to with SQLite, but having a way to sync it and make it more robust. Litestream is just a small program that copies backups of your SQLite database periodically to an S3-like object storage of your choice. My choice? Scaleway! Scaleway actually &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.scaleway.com&#x2F;en&#x2F;pricing&#x2F;#object-storage&quot;&gt;has an awesome &quot;first 75GB for free&quot; thing going on!&lt;&#x2F;a&gt;  Meaning: you have cool redundant storage without too much effort. Host that application of Litestream on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;Fly.io&lt;&#x2F;a&gt; and you&#x27;re set for a 100% free way of running whatever you need to run!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;closing-thoughts&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#closing-thoughts&quot; aria-label=&quot;Anchor link for: closing-thoughts&quot;&gt;Closing thoughts&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;None of the things I shared are particularly going into detail about how I set these things up. I just wanted to drop some inspiration on you to try and figure out your perfect combination of free services to run what it is you need to run. I found most of these services and ideas through keeping my eyes peeled on Hackernews. I&#x27;ve been using &quot;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;free-for.dev&#x2F;#&#x2F;&quot;&gt;Free for Dev&lt;&#x2F;a&gt;&quot; as a good alternative though. There&#x27;s a bunch of people that keep their eyes out for free deals for developers. You don&#x27;t have to be a developer to benefit from this though, as you can use most of these services for personal use. They do usually rely on some understanding of tech or for you to have some technical&#x2F;computing skills.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for reading my post! 👋✨&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dynamic GDDs with Notion &amp; Godot</title>
        <published>2020-08-17T00:00:00+00:00</published>
        <updated>2020-08-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/dynamic-gdd-with-notion-and-godot/"/>
        <id>https://bram.dingelstad.works/blog/dynamic-gdd-with-notion-and-godot/</id>
        
        <content type="html" xml:base="https://bram.dingelstad.works/blog/dynamic-gdd-with-notion-and-godot/">&lt;p&gt;&lt;em&gt;EDIT: This integration doesn&#x27;t work anymore and I need to port it to the new API of Notion. I&#x27;ll notify y&#x27;all by updating this post when it happens._Until then you can check out my latest project with Notion: Notion to Calendar 📆&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Shortly after I start with a Game design document, I’m hit with the very
sudden and scary realization that I need to upkeep the document as well. By combining the power of Notion and Godot I hope to make it slightly easier. Let me explain to you how you could do the same!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;All gamedevs have to either write or read a GDD from time to time. These
beautifully complex documents take time to digest, even by the people writing
them. You tend to go back a lot of times to revise a part so that they
represent the project or prototype that you’re working on.&lt;&#x2F;p&gt;
&lt;p&gt;When it comes to making these documents I found that
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;notion.so&#x2F;&quot;&gt;Notion&lt;&#x2F;a&gt; is not only a great tool, but an essential one as
well. Doubling both as a wiki and a document at the same time, it&#x27;s easier to
write things down and have it be part of your knowledge base. The way you can
search through these ever updating documents is great! Instead of sifting
through emails with attachments or 3 cloud based sync things, me and my team
can just write in the same place.&lt;&#x2F;p&gt;
&lt;p&gt;However, it can be difficult to go back and forth and to update the parameters
with which your game runs. What if you want to increase the amount of enemies
in that one level? What about the movement speed of the player or the name of
that character? What if part of your prototype dialogue is in the document and
you want to keep it in one place instead of having to copy all the time?&lt;&#x2F;p&gt;
&lt;p&gt;What if we were to combine the power of Notion with our Game Engine? I&#x2F;we use
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;godotengine.org&#x2F;&quot;&gt;Godot&lt;&#x2F;a&gt; for most of our games and when quickly
iterating in the first stages of the game, there’s a lot of communication
between whoever’s designing and programming the game. When the designer has
found out the proper parameters through paper prototyping, the programmer has
to usually adjust these values in the engine and create a new build. I was
looking for a solution for this and when I saw that Splitbee had made an API
for Notion, I knew I found it.&lt;&#x2F;p&gt;
&lt;p&gt;I’m going to take a recent game as an example: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;oap_bram&#x2F;status&#x2F;1294742423852273669&quot;&gt;Ant
game&lt;&#x2F;a&gt;. This game was
a small fun prototype I made over a few weeks when becoming obsessed with ants
after seeing a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=cqECNYmM23A&quot;&gt;Kurzgesagt video&lt;&#x2F;a&gt;.
When writing down some of the ideas in Notion, I also began noting down
parameters at which the game runs. Think of things like: amount of starting
ants, the amount of resources needed to x etc. Using &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;splitbee&#x2F;notion-api-worker&quot;&gt;Splitbee’s API wrapper
around Notion&lt;&#x2F;a&gt;, I was able to
extract these values. Then, I load in these values from the table with
parameters and put them into the game. What you’re left with, is a game that
automatically updates based on what you fill into your GDD. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;oap-bram&#x2F;NotionGodot&quot;&gt;I made a custom
node to make it easier for you to do as
well&lt;&#x2F;a&gt;. I hope this tool will allow us
to quickly update parts of our design without recompiling the game. You might
say: can’t this problem be solved with any CMS? Yes, but being able to do it
all in the same tool where you write the bulk of your design is &lt;em&gt;even better.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I’ve found that once you’ve got a working prototype, it&#x27;s easier to iterate on
pure gameplay with this method. It also makes the GDD “alive” by having the
document directly influence the gameplay. It&#x27;s easier to see when the document
is flawed when the game doesn’t play well. The possibilities for this are close
to endless, similar to any system hooked up to a CMS. Because engines like
Godot and Unity are data-driven engines, hooking up a CMS is nothing more than
changing where the “data” in data-driven comes from. What would be even cooler
is to have the ability to post to tables or pages in Notion, that way there’s
an easy way to keep track of feedback and testing sessions. Other
things worth trying are: dialogue, story structures, downloading images from
Notion as textures, downloading locale strings and menu messages.&lt;&#x2F;p&gt;
&lt;p&gt;I’m looking forward to using and innovating with this tool. If you like to try
it out, check out the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;oap-bram&#x2F;NotionGodot&#x2F;&quot;&gt;Github&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for reading 👋&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Finding the right PaaS for k8s</title>
        <published>2020-03-30T00:00:00+00:00</published>
        <updated>2020-03-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/finding-the-right-paas-for-k8s/"/>
        <id>https://bram.dingelstad.works/blog/finding-the-right-paas-for-k8s/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;EDIT: 👋 Hey there, this is quite an old article. Since I have found &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;fly.io&lt;&#x2F;a&gt; to be the one-stop-shop for all my PaaS related needs. Their free pricing tier is quite nice, the docs are thorough and their pricing scales quite nicely. Be sure to check them out!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;It starts off small, just trying to nudge the team into the direction of a new tech or small module in a project and before you know it, you&#x27;re neck deep in fancy docs of some weird project that solves your business case in just 3 easy steps! Trying to find a way to do that trivial thing you want to however, seems to take a lot more steps.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Introducing simple APIs with Neh</title>
        <published>2020-03-23T00:00:00+00:00</published>
        <updated>2020-03-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/introducing-neh/"/>
        <id>https://bram.dingelstad.works/blog/introducing-neh/</id>
        
        <summary type="html">&lt;p&gt;&lt;em&gt;EDIT: So, I posted this article on &lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=22669424&quot;&gt;&lt;em&gt;HackerNews&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;em&gt; and the first thing people were kind enough to point out was that I could just use CGI. Good point!
I&#x27;ll take a look at that. However, I still had fun working on this side project, even if it ended up a small CGI clone in OpenResty! Thank you for reading!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Everyone encounters a moment in which they&#x27;re held back by a limitation of a certain thing they use. Whether that&#x27;s your morning citrus juicer that doesn&#x27;t seem to catch &lt;em&gt;all&lt;&#x2F;em&gt; the pits in your wake-up juice.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Welcome to my blog</title>
        <published>2020-03-14T00:00:00+00:00</published>
        <updated>2020-03-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Bram Dingelstad
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bram.dingelstad.works/blog/welcome-to-my-blog/"/>
        <id>https://bram.dingelstad.works/blog/welcome-to-my-blog/</id>
        
        <summary type="html">&lt;p&gt;Hey there! 👋&lt;&#x2F;p&gt;
&lt;p&gt;My name is Bram Dingelstad. For those who don&#x27;t know me -- highly likely, none of my friends know of this website -- I&#x27;m a developer, entrepeneur, activist and aspiring game designer.&amp;hellip;
&lt;&#x2F;p&gt;
</summary>
        
    </entry>
</feed>
