<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>michalvanko.dev latest posts</title><link>https://michalvanko.dev</link><description>Latest posts published on michalvanko.dev blog site</description><language>en</language><webMaster>michalvankosk@gmail.com</webMaster><pubDate>Fri, 28 Feb 2020 16:00:55 +0000</pubDate><lastBuildDate>Thu, 20 Aug 2026 20:17:12 +0000</lastBuildDate><item><title>Week with my-pi-agent</title><link>https://michalvanko.dev/blog/2026-04-01-week-with-my-pi-agent</link><description><![CDATA[<p>Last week I’ve set a goal for myself to go 100% without writing a single line of code manually. I’ve been trying agentic workflows for a year now. My first agentic tool I’ve tried was <a href="https://aider.chat/">aider</a> and it was pretty cool experience at that time. For the last few months I’ve been using <a href="https://opencode.ai/">OpenCode</a> but I haven’t been able to let it control 100% of my results. Mainly because it hasn’t been able to earn my trust. I’ve been watching different tools for a long time and decided that it was time to try out something minimal. Something that I can make work for me. Make it work with my workflows and tools I use during work.
<a href="https://pi.dev">Pi</a> is a <strong>bare bones CLI agent</strong> similar to Claude Code, Codex CLI and OpenCode.</p>
]]></description><enclosure url="/images/uploads/pi-logo.svg" length="" type="image/svg+xml"/><guid>https://michalvanko.dev/blog/2026-04-01-week-with-my-pi-agent</guid><pubDate>Wed, 1 Apr 2026 12:00:00 +0000</pubDate><content:encoded><![CDATA[<p>Last week I’ve set a goal for myself to go 100% without writing a single line of code manually. I’ve been trying agentic workflows for a year now. My first agentic tool I’ve tried was <a href="https://aider.chat/">aider</a> and it was pretty cool experience at that time. For the last few months I’ve been using <a href="https://opencode.ai/">OpenCode</a> but I haven’t been able to let it control 100% of my results. Mainly because it hasn’t been able to earn my trust. I’ve been watching different tools for a long time and decided that it was time to try out something minimal. Something that I can make work for me. Make it work with my workflows and tools I use during work.</p>
<h2 id="meet-pi">
Meet Pi</h2>
<!-- ![Pi agent logo](/images/uploads/pi-logo.svg 'Pi agent logo') -->
<figure>
  <img src="/images/uploads/pi-logo.svg" alt="Pi agent logo" class="max-w-[280px] h-[280px] bg-black">
</figure>
<p><a href="https://pi.dev">Pi</a> is a <strong>bare bones CLI agent</strong> similar to Claude Code, Codex CLI and OpenCode.
Pi itself is very simple. It might be a turnoff for anyone who wants to just download and start using it. You have to spend some time tinkering it.
There is a lot of <a href="https://pi.dev/packages">packages</a>, that can bring the functionality of other CLIs to Pi.
The community around Pi is very fresh and thriving. We are still in the early adopters era of agentic programming. I’d consider every <strong>package as a potential threat</strong> and <strong>cautiously review</strong> them before installing.</p>
<p>I discovered Pi when <a href="https://openclaw.ai/">OpenClaw</a> was released. I was curious about what is powering <em>OpenClaw</em>. Back then I wasn’t really interested in Pi.
But after few weeks with <a href="https://opencode.ai/">OpenCode</a>, I wasn’t feeling like the tool is working with my workflow.
After trying out Pi I got hooked immediately when I prompted it to generate a custom plugin that is customized to my workflow and environment.
I use a nerdish Desktop environment:</p>
<ul>
<li><a href="https://fedoraproject.org/">Fedora Linux</a></li>
<li><a href="https://noctalia.dev/">Noctalia shell</a></li>
<li><a href="https://niri-wm.github.io/niri/">Niri Window manager</a> (special kind of tiling window manager)</li>
<li><a href="https://fishshell.com/">fish</a> as a shell</li>
<li><a href="https://zellij.dev/">ZelliJ</a> as a terminal multiplexer</li>
</ul>
<p>I don’t know anyone who uses the same tools I do.</p>
<p>Other agentic CLIs are like taxi cars, you hop-in, you can tell the driver where you want to go. But it is out of your hands how you get there. You are often restricted in the capabilities of the CLI.
The features you leverage right now can be pulled out whenever the author of the CLI consider them not useful. With Pi you can <strong>inspect every single line of code</strong>. Why, and what gets executed and when.
With Pi, you build your own ride. Do you need to just get some mail from post office? You build a bicycle. Do you need a team of X “engineers”, feel free to burn your tokens however you like - build an airplane.
Want to go the to moon? Build yourself a rocketship. There are no limits.</p>
<p>With no other CLI agent I was ever <strong>motivated to create my own plugins, commands, workflows</strong>. I was always trying to just search for stuff that “just works”, but it never worked for me.</p>
<p>First plugin I’ve created was that I wanted to have a <a href="https://github.com/michalvankodev/my-pi-agent/blob/main/agent/extensions/commit.ts"><code>/commit</code> command</a>.</p>
<ul>
<li>This command should not do <code>git commit</code> for me.</li>
<li>It should just generate a commit message and open an editor in a new <em>ZelliJ</em> pane.</li>
<li>I can not only review but edit if I am not satisfied with the output. I can even cancel the commit if I opt to.</li>
</ul>
<p>One, two prompts and voilà - I have my first custom plugin. Nothing ground-breaking but it is working just how I like it.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/pi-screenshot_1250x699.png 1x"
    type="image/png"
>
    <img
    src="/generated_images/images/uploads/pi-screenshot_1250x699.png"
    width="1250"
    height="699"
    alt="My Pi says Hi!"
    
>
</picture>
<figcaption>
My Pi says Hi!</figcaption></figure></p>
<h2 id="what-works-for-me">
What works for me</h2>
<p>In my experience a simple approach turned out to have the best results considering getting stuff done, cheaper and reliable.
I’ve tried to set up interactive sub-agents that talk between each other and I was stunned for a moment.
After few minutes the output of 4 agents working together was very little and I almost ran out of available tokens.
Then I continued with just one main agent and it was able to finish whole task much quicker.
The main workflow for me (for backend tasks) is:</p>
<ol>
<li>Prompt agent with what are we going to do. “Gather information about existing code”. Write a specification file and split work into tasks.
We are going to update the specification after each task gets done.</li>
<li><a href="https://github.com/michalvankodev/my-pi-agent/blob/main/agent/prompts/clarify.md"><code>/clarify</code></a> This is the <strong>most powerful prompt</strong> in my workflow. It asks agent to ask clarifying questions. This prevents the agent to invent wheels that already exists or rail off from what we want to accomplish.</li>
<li>Start working on tasks one by one</li>
</ol>
<p>When the context-window gets too big, there is a decision to make. With <code>/tree</code> command you can traverse your conversation to a point (after the 2nd step for example) where the agent has enough context about all the tasks that you can continue without starting with fresh context window. Or you can create a <code>/new</code> session and prompt it to gather information about the tasks with the link to the spec.</p>
<p><code>/tree</code> is very useful feature. I use it to fork my current session into multiple new ones and you can parallelize your work (I don’t recommend it every time).</p>
<p>Two other features I use constantly:</p>
<ul>
<li>Steering - When you see the agent doing something you want to immediately fix, you steer it. Your message
arrives as soon as the current tool call finishes.</li>
<li>Follow up - When you want to tell the agent something or request additional work, you follow up. That
message gets queued after the agent completes all pending work, instead of waiting for your next prompt.</li>
</ul>
<p>There’s still more to learn. This is just the experience I’ve had with Pi during the last week.</p>
<p>For front-end task I’d choose a different workflow. I’d incorporate <code>agent-browser</code> into the loop. Having constant feedback loop is very powerful and allows the agent to run for longer time (do more without constant supervision)</p>
<p>Another pleasant use experience I had was with <strong>debugging memory issues</strong>. This is a task that if you don’t do very often during development, but from time to time it happens that unexpected errors happen due to different resource availability on production systems. I’ve said to the agent what happens and what I think was the issue (I was way way off).
It created multiple testing scenario applications in <code>/tmp</code> directory to just test off hypothesis.
He dug out every single call and investigated deep hierarchy of code deep down to every single dependency in the chain.
We haven’t been able to find the issue there. So I’ve used <a href="https://github.com/KDE/heaptrack"><code>heaptrack</code></a> to provide actual information about the memory allocations and then I just passed the output to the agent. It just came out with the actual issue and provided fix in few seconds.</p>
<p>It is able to create <code>bash</code> commands and execute them in a loop until it discovers significant information. It does so at such velocity that I have to admit I <strong>can’t imagine now working without it</strong>. Once you experience this kind of boost <strong>it’s unthinkable to go back</strong>.</p>
]]></content:encoded></item><item><title>Somewhere in the sky</title><link>https://michalvanko.dev/blog/2025-06-11-somewhere-in-the-sky</link><description><![CDATA[<p>What can you do on an airplane when you cannot move as your son is sleeping on your lap? You can <strong>write a new blog post</strong>, “obviously”. Why can’t I find more time to write more posts? I lack the motivation, to be honest. Now that anyone can just ask an LLM to spit out an original blog post on demand, who will search for original, honest content written by a human?
I’ve set expectations for my wife that this year we won’t be going on a vacation, and she has overruled and found a one-week trip to Mallorca. I can confirm that we will be <strong>coming back to Mallorca</strong> for further exploration. This week was very nice. I haven’t made any commitments on what I’m going to do with my career. I’ve already <strong>accomplished</strong> that since this year, <strong>I will work on Rust projects</strong>.</p>
]]></description><enclosure url="/images/uploads/dscf4399.jpg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2025-06-11-somewhere-in-the-sky</guid><pubDate>Wed, 11 Jun 2025 15:50:45 +0000</pubDate><content:encoded><![CDATA[<h2 id="realization">
Realization</h2>
<p>What can you do on an airplane when you cannot move as your son is sleeping on your lap? You can <strong>write a new blog post</strong>, “obviously”. Why can’t I find more time to write more posts? I lack the motivation, to be honest. Now that anyone can just ask an LLM to spit out an original blog post on demand, who will search for original, honest content written by a human?</p>
<p>I’ve set expectations for my wife that this year we won’t be going on a vacation, and she has overruled and found a one-week trip to Mallorca. I can confirm that we will be <strong>coming back to Mallorca</strong> for further exploration. This week was very nice. I haven’t made any commitments on what I’m going to do with my career. I’ve already <strong>accomplished</strong> that since this year, <strong>I will work on Rust projects</strong>.</p>
<h2 id="drift">
Drift</h2>
<p>In my latest vlog, I mentioned that I should talk more about burning out, and that I don’t feel like I am burning out. Since then, I stopped streaming and stopped producing videos. <strong>Did I jinx myself?</strong> I don’t know. Being active on social media is not something for me. I enjoy streaming, but I just had to put my family above my fulfillment. <strong>I enjoy putting my son to sleep</strong> much more. He is growing so fast. I expect that in a few years <strong>we will start streaming together</strong> as he explores the world of computers.</p>
<p>I expect to find <strong>more time for streaming</strong> when the sun falls in earlier hours.</p>
<p>You should’ve seen him. He was able to fill his bucket and pour it into a hole in the sand more than 100 times in a row. Where he finds the energy to do this is unbelievable. He is such an extraordinary individual.</p>
<h2 id="inertia">
Inertia</h2>
<p><em>What about the game progress?</em></p>
<p><strong>The game is in a terrible state</strong>. I realized that the <strong>whole animation system has to be rewritten</strong> as it is unreliable. And that’s almost everything that we have been working on. I’ve started to <strong>write the game design document</strong> so I don’t lose my thoughts on mechanics and game setting. I should find more time to code as well, but currently, <strong>I have just enough work and studies to keep me occupied</strong>.</p>
<h2 id="sight">
Sight</h2>
<p>Here are some photos from the trip:</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/dscf4379_1280x853.jpg 1x, /generated_images/images/uploads/dscf4379_1920x1279.jpg 1.5x, /generated_images/images/uploads/dscf4379_2560x1706.jpg 2x, /generated_images/images/uploads/dscf4379_3840x2559.jpg 3x, /generated_images/images/uploads/dscf4379_5120x3412.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/dscf4379_1280x853.jpg"
    width="1280"
    height="853"
    alt="My beautiful fam"
    
>
</picture>
<figcaption>
My beautiful family</figcaption></figure></p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_20250607_202440_645x860.jpg 1x, /generated_images/images/uploads/img_20250607_202440_967x1290.jpg 1.5x, /generated_images/images/uploads/img_20250607_202440_1290x1720.jpg 2x, /generated_images/images/uploads/img_20250607_202440_1935x2580.jpg 3x, /generated_images/images/uploads/img_20250607_202440_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_20250607_202440_645x860.jpg"
    width="645"
    height="860"
    alt="Chasing dreams"
    
>
</picture>
<figcaption>
Chasing dreams</figcaption></figure></p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/dscf4333_860x573.jpg 1x, /generated_images/images/uploads/dscf4333_1290x859.jpg 1.5x, /generated_images/images/uploads/dscf4333_1720x1146.jpg 2x, /generated_images/images/uploads/dscf4333_2580x1719.jpg 3x, /generated_images/images/uploads/dscf4333_3440x2292.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/dscf4333_860x573.jpg"
    width="573"
    height="860"
    alt="The sun for my moon"
    
>
</picture>
<figcaption>
The sun for my moon</figcaption></figure></p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/dscf4274_860x573.jpg 1x, /generated_images/images/uploads/dscf4274_1290x859.jpg 1.5x, /generated_images/images/uploads/dscf4274_1720x1146.jpg 2x, /generated_images/images/uploads/dscf4274_2580x1719.jpg 3x, /generated_images/images/uploads/dscf4274_3440x2292.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/dscf4274_860x573.jpg"
    width="573"
    height="860"
    alt="Stomp"
    
>
</picture>
<figcaption>
Stomp</figcaption></figure></p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/dscf4399_1280x853.jpg 1x, /generated_images/images/uploads/dscf4399_1920x1279.jpg 1.5x, /generated_images/images/uploads/dscf4399_2560x1706.jpg 2x, /generated_images/images/uploads/dscf4399_3840x2559.jpg 3x, /generated_images/images/uploads/dscf4399_5120x3412.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/dscf4399_1280x853.jpg"
    width="1280"
    height="853"
    alt="Lil bird"
    
>
</picture>
<figcaption>
Lil bird</figcaption></figure></p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_20250610_204627_645x860.jpg 1x, /generated_images/images/uploads/img_20250610_204627_967x1290.jpg 1.5x, /generated_images/images/uploads/img_20250610_204627_1290x1720.jpg 2x, /generated_images/images/uploads/img_20250610_204627_1935x2580.jpg 3x, /generated_images/images/uploads/img_20250610_204627_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_20250610_204627_645x860.jpg"
    width="645"
    height="860"
    alt="Fuji X-Michal Viltrox AF 23/1.4 XF"
    
>
</picture>
<figcaption>
Fuji X-Michal with Viltrox AF 23/1.4 XF</figcaption></figure></p>
<p>Some of them are taken with my <em>Fuji X-Michal</em> and I have to say I’ve fallen in love with the machine. I wish some features were more accessible, and I hope with the new Fuji X-E5 announcement that the <em>Film simulation recipes</em> will be included in a future firmware upgrade. The current configuration is very misleading and not user-friendly.</p>
<p>I’ve also ordered a new Sigma 16-300, hoping it will be delivered before the vacation. I have already <strong>been waiting for it for 2 months</strong>, and still no indication of when it will arrive. I am pretty new to the hobby, and it looks like when ordering a new gear, there is a <strong>guarantee of a long waiting time</strong>, or it is out of stock pretty quickly.</p>
<p>I’d like to <strong>post more pictures</strong> here on the blog. I’m planning on releasing albums. I don’t know if I should also make some changes to the visualization, or if the current blog page rendering system is fine as it is. <strong>Let me know if you have some cool ideas</strong>.</p>
<p>I’m going to keep it short. We’ve landed successfully. The vacation was amazing. We are definitely coming back to Mallorca. Expect more content (soon).</p>
<p>Happy summer.</p>
]]></content:encoded></item><item><title>Auto-commit &amp; push git repositories</title><link>https://michalvanko.dev/blog/2024-11-23-autocommit-push-git-repositories</link><description><![CDATA[<p>I’ve been <strong>writing my notes</strong> for over 4 years in <strong>markdown</strong>. I write my notes in similar style of a <a href="https://zettelkasten.de/introduction/">Zettelkasten method</a>. I was <strong>inspired by <a href="https://github.com/foambubble/foam">foambubble</a> project</strong> and was using it for quite some time. The goal of foambubble was to re-use as much <strong>free technology</strong> for writing and <strong>keeping second brain</strong>. The author <a href="https://jevakallio.dev/">Jani Eväkallio</a> has chosen to write <strong>a plugin for VSCode</strong> and <strong>utilize git workflow</strong>.
This approach as complicated for average human was very appealing to me as I was already user of certain tools.</p>
]]></description><enclosure url="/images/uploads/git-icon-logo-svg-vector.svg" length="" type="image/svg+xml"/><guid>https://michalvanko.dev/blog/2024-11-23-autocommit-push-git-repositories</guid><pubDate>Sat, 23 Nov 2024 15:23:00 +0000</pubDate><content:encoded><![CDATA[<h2 id="motivation">
Motivation</h2>
<p>I’ve been <strong>writing my notes</strong> for over 4 years in <strong>markdown</strong>. I write my notes in similar style of a <a href="https://zettelkasten.de/introduction/">Zettelkasten method</a>. I was <strong>inspired by <a href="https://github.com/foambubble/foam">foambubble</a> project</strong> and was using it for quite some time. The goal of foambubble was to re-use as much <strong>free technology</strong> for writing and <strong>keeping second brain</strong>. The author <a href="https://jevakallio.dev/">Jani Eväkallio</a> has chosen to write <strong>a plugin for VSCode</strong> and <strong>utilize git workflow</strong>.
This approach as complicated for average human was very appealing to me as I was already user of certain tools.</p>
<p>Since then, I stopped using VSCode as my code editor and therefore, as there wasn’t support for <em>foam</em> outside of VSCode I’ve <strong>migrated to a simpler workflow</strong> using <a href="https://github.com/zk-org/zk">zk</a>.</p>
<p>Since then, I have struggled a lot with <strong>forgetting to commit</strong> my changes as I often write updates, mark completed tasks, or write new TODOs throughout the day.
I use a computer and a laptop when on the road and it is miserable when you forget to push your changes. I’m still trying to find a perfect workflow for <strong>synchronizing notes with my mobile phone</strong>. I’ve used many different applications, but they <strong>all failed</strong> me as soon as there was a <strong>git conflict</strong>. I think I have a solution for this thought through.</p>
<p>A very easy solution would be to run a <a href="https://letmegpt.com/?q=I&#x27;d%20like%20to%20automatically%20commit%20and%20push%20to%20my%20git%20repository%20after%205%20minutes%20of%20inactivity%20on%20my%20changes.">script generated with GPT</a> to <strong>auto-commit</strong>. It has the downsides of always having to remember to start it and it would not be easy to <strong>share and apply to other repositories</strong>. So, I came up with another <strong>Rust crate</strong>.</p>
<h2 id="welcome-">
Welcome <code>git_afk</code></h2>
<p><code>git_afk</code> might stand for <em>get always forgotten knowledge</em>. It doesn’t. I’ve <strong>chosen Rust</strong> again. As I use it more frequently, my appreciation for it grows. I feel like Rust just makes you manage the state in the place where the state belongs. Working on this project taught me how to keep and share asynchronous callbacks in a way that makes sense but isn’t so obvious at first glance. The <strong>compiler is always very helpful</strong>. While in other languages you would need to keep <code>unsubscribe</code> handles all over the place to <strong>avoid memory leaks</strong>. Rust makes you think differently about how to structure the code and data around it.</p>
<p>The application is a <strong>CLI app</strong> that has a basic ability to add repositories to the watch list and then run <code>watch</code> command.
Anytime there is a change in the configuration file it will reload watchers and start watching all files in the repositories for changes. It uses <a href="https://www.man7.org/linux/man-pages/man7/inotify.7.html"><code>inotify</code></a> a kernel interface for watching. You can find the <a href="https://github.com/michalvankodev/git_afk">source code on GitHub</a>.</p>
<p>As the Rust ecosystem is so simple you can install <code>git_afk</code> with <a href="https://crates.io/"><code>cargo</code></a> <code>cargo install git_afk</code>. Submitting packages is easy. Installing them as well. <strong>Automatic documentation generation</strong> is awesome. Every package has the same style of documentation. Very easy to navigate. Rust is growing up on me. I’d very much like to use it as my main tool for my job.</p>
<p>Now I can safely <em>git away from keyboard</em>.</p>
]]></content:encoded></item><item><title>My first published Cargo crate</title><link>https://michalvanko.dev/blog/2024-11-08-my-first-published-cargo-crate</link><description><![CDATA[<p>I’ve become obsessed with cameras, during our Tenerife trip, where I got to use a borrowed mirrorless camera for the first time. After that, I researched which camera I’d buy for myself for five months. While watching all the reviews, I was interested in the <strong>exposure settings</strong> for each picture presented. I’ve displayed many photographs in our <a href="https://www.youtube.com/watch?v=tEpoVHQW4Qs&amp;list=PLjUl8tFKyR8rCsckLn93PAwQg6tf0cyBl">Tenerife vlog with my wife</a>. I’ve decided that I’d like to show those exposure settings in my other videos as they might <strong>inspire other photographers</strong>.
I’ve tried many image editors and tools, but not one was able to <strong>fulfill my demands</strong> on how I’d like to display those settings underneath each picture. Many <strong>tools supported</strong> some kind of <strong>framing</strong>, however it was always <strong>in a limited way</strong>.</p>
]]></description><enclosure url="/images/uploads/aperture-icon.svg" length="" type="image/svg+xml"/><guid>https://michalvanko.dev/blog/2024-11-08-my-first-published-cargo-crate</guid><pubDate>Fri, 8 Nov 2024 17:09:00 +0000</pubDate><content:encoded><![CDATA[<p>I’ve become obsessed with cameras, during our Tenerife trip, where I got to use a borrowed mirrorless camera for the first time. After that, I researched which camera I’d buy for myself for five months. While watching all the reviews, I was interested in the <strong>exposure settings</strong> for each picture presented. I’ve displayed many photographs in our <a href="https://www.youtube.com/watch?v=tEpoVHQW4Qs&amp;list=PLjUl8tFKyR8rCsckLn93PAwQg6tf0cyBl">Tenerife vlog with my wife</a>. I’ve decided that I’d like to show those exposure settings in my other videos as they might <strong>inspire other photographers</strong>.</p>
<p>I’ve tried many image editors and tools, but not one was able to <strong>fulfill my demands</strong> on how I’d like to display those settings underneath each picture. Many <strong>tools supported</strong> some kind of <strong>framing</strong>, however it was always <strong>in a limited way</strong>.</p>
<h2 id="hello-">
Hello <code>metaframer</code></h2>
<p>I knew straight away that this would be a cool and <strong>small side-project</strong> that I could create. I could use my <strong>favorite technologies</strong> and have good <strong>content for my streams</strong>.</p>
<p>At first, I thought, that I would have to <strong>render the text</strong> right <strong>into the image</strong>. It will however be in some aspect a <strong>deformation to the quality</strong> of the original image. There was also a question of <strong>how should the text be displayed</strong> in a way that for <strong>any image size</strong>, the text will be of a similar or determinable size.</p>
<p>Instead of <strong>rendering pixels</strong> into every different format I’ve opted for creating an <strong>SVG file</strong> that sits <strong>alongside the original image</strong>. This way the text also <strong>scales with the image</strong> itself. The complexity was lowered instantly. Rendering SVG is just like rendering HTML. I’ve used <a href="https://handlebarsjs.com/">handlebars template parser</a>. There are more favorable template parsers in Rust like <a href="https://djc.github.io/askama/askama.html">Askama</a>. But I wanted to allow users to <strong>create their own templates</strong> so the templates are not part of the executables and they can be stored in the user’s <code>.config</code> folder. I had to test the <strong>support of my video editing software</strong> (I use <a href="https://kdenlive.org/">Kdenlive</a> BTW) and the decision for SVG was set in stone (a.k.a. <a href="https://github.com/michalvankodev/metaframer">README</a>).</p>
<p>There were still complications along the way. <strong>SVG</strong> is still a much <strong>less capable format than HTML</strong> and <strong>positioning elements</strong> in a <code>space-around</code> fashion <strong>has to be calculated</strong>. There are also many different options and resolutions that I had to consider and I put all of them into the CLI arguments with <a href="https://docs.rs/clap/latest/clap/">clap</a>. <a href="https://docs.rs/clap/latest/clap/">Clap</a> is a fantastic <strong>command line argument parser</strong> for Rust. I had to do many calculations on the resolutions and whether the image was a portrait or not. In the end, It wasn’t as small of a side-project as I thought. I’m still very proud of it.</p>
<p>I got to experience the ease of publishing crates on <a href="https://crates.io/">crates.io</a> with <a href="https://doc.rust-lang.org/cargo/guide/">cargo</a>.</p>
<p>This week, I’ve <strong>automatized the <a href="https://github.com/michalvankodev/metaframer/blob/main/.github/workflows/release.yml">release process</a></strong> and published binaries for multiple platforms.</p>
<h2 id="on-to-the-next-one">
On to the next one</h2>
<p>I can’t wait to use <a href="https://github.com/michalvankodev/metaframer"><code>metaframer</code></a> in my new videos. I’ve already <strong>ordered my first mirrorless camera</strong> and it is on the way. <strong>Be ready for new content</strong>. What should be my next project?</p>
]]></content:encoded></item><item><title>Renaissance of Hypermedia Systems</title><link>https://michalvanko.dev/blog/2024-11-06-renaissance-of-hypermedia-systems</link><description><![CDATA[<p>My goal for this year was to make a presentation. I’ve told this to my friend Lukáš Orgován and he offered me an opportunity to come present at their workplace with him.  This was a great event and I’m so glad I could attend.
I’ve chosen a topic very close to my heart and wanted to show a different approach to building mobile and web applications.</p>
]]></description><enclosure url="/images/uploads/screenshot-from-2024-08-06-19-01-03.png" length="" type="image/png"/><guid>https://michalvanko.dev/blog/2024-11-06-renaissance-of-hypermedia-systems</guid><pubDate>Wed, 6 Nov 2024 16:20:00 +0000</pubDate><content:encoded><![CDATA[<p>My goal for this year was to make a presentation. I’ve told this to my friend Lukáš Orgován and he offered me an opportunity to come present at their workplace with him.  This was a great event and I’m so glad I could attend.</p>
<p>I’ve chosen a topic very close to my heart and wanted to show a different approach to building mobile and web applications.</p>
<p>The presentation is in Slovak with English subtitles.</p>
<div class="video-embed">
<iframe height="100%" width="100%" src="https://www.youtube.com/embed/hn7rR9wIfIY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>]]></content:encoded></item><item><title>This site has been rewritten again</title><link>https://michalvanko.dev/blog/2024-10-26-this-site-has-been-rewritten-again</link><description><![CDATA[<p>Hey world,
After a few months of work, I can finally present to you a new style for this site. The style isn’t the only thing that changed.</p>
]]></description><enclosure url="/images/uploads/m-logo.svg" length="" type="image/svg+xml"/><guid>https://michalvanko.dev/blog/2024-10-26-this-site-has-been-rewritten-again</guid><pubDate>Tue, 5 Nov 2024 11:26:00 +0000</pubDate><content:encoded><![CDATA[<p>Hey world,</p>
<p>After a few months of work, I can finally present to you a new style for this site. The style isn’t the only thing that changed.
I’ve <strong>completely rewritten</strong> how this site is produced.</p>
<h2 id="motivation">
Motivation</h2>
<p>With the front-end tech world spinning too fast I’ve noticed this cool little library called <em><a href="https://htmx.org/">HTMX</a></em> that takes front-end development back 10 years and provides a completely different look at how web development could’ve been done.
I’ve also done some experiments with <a href="https://qwik.dev/">Qwik</a> before, but I’m glad that I’ve abandoned this route as I would like to write less and less <em>TypeScript</em> <code>||</code> <em>Javascript</em> in the future.
After doing last year’s <a href="https://adventofcode.com/">Advent of Code</a> in <em>OCaml</em> I’ve figured that I’d like to use tools that I think make me productive and also provide me help on the path of producing the most safe, predictable and performant code.
That’s the reason <strong>I’ve chosen <em>Rust</em></strong> for this site and most of my side projects in the last couple of years.</p>
<h2 id="what-has-changed">
What has changed</h2>
<h3 id="tech-stack">
Tech stack</h3>
<p>I’ve mentioned <em>HTMX</em>, but it only inspired me and I took it a few steps back and I haven’t opted for any <em>JavaScript</em> included by default. You can find some <em>JavaScript</em> on this site but it is only inlined without any build step. However, there still has to be a build step. Instead of setting up <em><a href="https://vite.dev/">Vite</a></em> for 5th time. I opted for something classic but still new. I’ve just found the <a href="https://github.com/casey/just">just</a> command runner. It is very similar to how many <em>Makefiles</em> look, but the API is modern and <strong>written in Rust BTW</strong>. So if any developer wants to look at what commands are being used during the development or deployment, they are all located in <a href="https://github.com/michalvankodev/michalvankodev-site/blob/main/justfile"><code>.justfile</code></a>. All commands are just <code>bash</code> commands and some <code>bash</code> scripts with few instructions for <code>just</code> to combine commands by their dependencies.</p>
<h3 id="templates">
Templates</h3>
<p>During development, the site is being hosted with <a href="https://github.com/tokio-rs/axum">axum Rust web framework</a>. I want to write my templates in HTML so they can be as easily reused in any other web project with any tech stack. For Rust, there is a <strong>type-safe</strong> Jinja like template parser <a href="https://github.com/djc/askama">Askama</a> and even though I’m not very confident whether I would use it again, I can still easily migrate to any other template parser there is.</p>
<h3 id="styling">
Styling</h3>
<p>I’ve completely redesigned the site. Dodging <em>TypeScript-based</em> solutions for <abbr title="Cascade style sheets">CSS</abbr>. Opting for <a href="https://tailwindcss.com/">tailwind</a> running <em>just</em> in the background. The experience with <em>tailwind</em> is something that I’ve never felt previously with any other <abbr title="Cascade style sheets">CSS</abbr> framework before. I’m satisfied with it although it has some downsides, <strong>the productivity hasn’t been matched</strong>.</p>
<h2 id="what-stayed-the-same">
What stayed the same</h2>
<h3 id="all-the-content">
All the content</h3>
<p>I haven’t even moved the files to a different folder. I’ve just slightly adjusted the models in the <a href="https://decapcms.org/">DecapCMS</a> config for <a href="/showcase">/showcase</a> page. DecapCMS is pretty much just the same Netlify CMS that it has been before the rebranding.
I’d like to recommend it as <em>THE CMS</em>, but I can’t. I like the way how the content is managed, I also like <em>git-based</em> workflow for managing content. However, there are many struggles to set it up for use with clients that are not programmers. With more changes to the models you also start to <strong>miss the capability of running migrations</strong> on all files/records. Everyone would be better off just hosting <em><a href="https://strapi.io/">Strapi</a></em>. The ecosystem has started to get healthy. Setting up a custom media library is also a thing that has to be considered.</p>
<h3 id="media-library-hosting">
Media library hosting</h3>
<p><a href="https://answers.netlify.com/t/large-media-feature-deprecated-but-not-removed/100804">Netlify has deprecated</a> its <a href="https://docs.netlify.com/git/large-media/setup/">Large Media</a> service. I don’t mind tracking media with <code>git</code> LFS plugin. What I do mind is where I store these assets. [Github’s] free quota is pretty low considering sharing photo galleries. For now, I’ve moved all assets on my <em>“powered by”</em> <a href="https://awesome-selfhosted.net/index.html">selfhosted</a> server, running my own <a href="https://gitea.katelyn.michalvanko.dev/">Gitea</a> instance.</p>
<h3 id="static-site-generation">
Static site generation</h3>
<p>The site is still distributed as <abbr title="Static site generation">SSG</abbr> HTML files. You could argue that the logic for generating every page is just like any other <abbr title="Server Side Rendered">SSR</abbr> website.</p>
<p>For <abbr title="Server side generation">SSG</abbr> I’ve come up with a <code>wget</code> command that downloads all the necessary dependencies for all pages. It is capable of recursively crawling the whole site following all links. It was pretty hard to come up with the correct set of parameters for the <code>wget</code> command to be able to produce the same routing capabilities as with the <code>SSR</code> running server used during the development. Here I can <strong>praise all the generative AI tools</strong>. You could find multiple prompts asking for an explanation of all the options for <code>wget</code> and how I should use them for the desired output in my history.</p>
<p>The final <code>wget</code> command for generating this site looks like this:</p>
<pre><code class="language-sh"><pre style="background-color:#2b303b;">
<span style="color:#8fa1b3;">wget</span><span style="color:#bf616a;"> --no-convert-links -r -p -E -P</span><span style="color:#c0c5ce;"> dist</span><span style="color:#bf616a;"> --no-host-directories</span><span style="color:#c0c5ce;"> localhost:3080
</span></pre>
</code></pre>
<p>You can also <a href="https://lmgpthat.com/render.html?search=wget%20--no-convert-links%20-r%20-p%20-E%20-P%20dist%20--no-host-directories%20localhost%3A3080">prompt for the explanation</a>.</p>
<h2 id="aim-for-future">
Aim for future</h2>
<p>I still have to <strong>write articles</strong>.
Many many articles that I haven’t yet written down. I did some but haven’t published them, and probably I never will. But so many things happened in the meantime. How do you call an update that is old? <em>“Outofdate”</em>?
It would also be handy to have some article recommendations when you finish reading this article, right?</p>
<p>I am still gathering feedback for the new design. I am considering many of the suggestions that I get. Our <a href="https://discord.gg/2cGg7kwZEh">Discord server</a> is getting warm.</p>
<p>I was also thinking about what would I use for CMS and having a <em><a href="https://www.sqlite.org/docs.html">SQLite</a></em> database saved in the repository, it would still count as <strong>git-based management</strong>.</p>
<p><em>’Let your ambition carry you!“</em></p>
]]></content:encoded></item><item><title>I built my 3rd custom keyboard</title><link>https://michalvanko.dev/blog/2023-08-29-i-built-my-3rd-custom-keyboard</link><description><![CDATA[<p>When I was young and still had time to play games on a regular basis I got myself an 80% mechanical keyboard from <a href="https://www.coolermaster.com/catalog/peripheral/keyboards/quick-fire-rapid-i/">Cooler Master</a>. I didn’t want to waste any space with a useless num-pad. For a very long time, I was very satisfied with the keyboard. It still serves me today. It has <a href="https://www.cherrymx.de/en/cherry-mx/mx-original/mx-brown.html">cherry brown</a> switches. I got very used to them, but I always wanted to try something different.
A few years later I found the <a href="https://www.reddit.com/r/MechanicalKeyboards/">r/MechanicalKeyboards</a> subreddit. I was amazed by the collection of custom-made keyboards and the variety of switches and other customization that is available.</p>
]]></description><enclosure url="/images/uploads/20230226_130037.jpg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2023-08-29-i-built-my-3rd-custom-keyboard</guid><pubDate>Tue, 29 Aug 2023 19:34:17 +0000</pubDate><content:encoded><![CDATA[<h2 id="my-keyboard-journey">
My keyboard journey</h2>
<p>When I was young and still had time to play games on a regular basis I got myself an 80% mechanical keyboard from <a href="https://www.coolermaster.com/catalog/peripheral/keyboards/quick-fire-rapid-i/">Cooler Master</a>. I didn’t want to waste any space with a useless num-pad. For a very long time, I was very satisfied with the keyboard. It still serves me today. It has <a href="https://www.cherrymx.de/en/cherry-mx/mx-original/mx-brown.html">cherry brown</a> switches. I got very used to them, but I always wanted to try something different.</p>
<p>A few years later I found the <a href="https://www.reddit.com/r/MechanicalKeyboards/">r/MechanicalKeyboards</a> subreddit. I was amazed by the collection of custom-made keyboards and the variety of switches and other customization that is available.
I decided to <strong>make my own split orthodox Redox keyboard</strong> with <strong>very light switches</strong> so I could type fast. At least that’s what I was thinking, but after a few days, I realized that those switches were a mistake. There are so many articles on the internet about how to pick switches, but I guess <strong>it’s just finding what is good for each person by experience</strong>.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_20200301_121954_1147x860.jpg 1x, /generated_images/images/uploads/img_20200301_121954_1720x1290.jpg 1.5x, /generated_images/images/uploads/img_20200301_121954_2294x1720.jpg 2x, /generated_images/images/uploads/img_20200301_121954_3441x2580.jpg 3x, /generated_images/images/uploads/img_20200301_121954_4000x2999.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_20200301_121954_1147x860.jpg"
    width="1147"
    height="860"
    alt="My custom redox keyboard"
    
>
</picture>
<figcaption>
My custom redox keyboard</figcaption></figure></p>
<p>At the same time, <strong>I’ve switched to the <a href="https://colemak.com/">Colemak layout</a></strong>. This one was perhaps the <strong>best choice that I could make</strong> at that time. My wrists were hurting and the ease of typing words with much less friction really helps out today. The time of struggle was definitely worth it.
There is this new layout on the block called <a href="https://workmanlayout.org/">Workman</a>, where the author is going through some tests calculating various metrics on written novels. It can be easily deductible that no matter what layout you choose, it will save you many many meters of travel in comparison to QWERTY.</p>
<p>While I was using my Redox for a few months I got to know what is the potential of <a href="https://docs.qmk.fm/#/">QMK</a>. I figured that I didn’t need that many keys on the keyboard. I was searching for a minimalistic keyboard and setup and I found Redox’s smaller cousin <a href="https://github.com/That-Canadian/MiniDox_PCB">Minidox</a>.
I’ve opted for a 36-key layout with 2 keys dedicated to switching between 4 different layouts. It was a very nice experience. I was amazed that <strong>I could fit a full keyboard functionality with media keys onto a 36-key keyboard</strong>. I’ve opted for brown switches back again. I didn’t want to risk making high noise while writing on a keyboard and I was missing the tactile feel when you press the key.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20210420_155540_03_1280x720.jpg 1x, /generated_images/images/uploads/20210420_155540_03_1920x1080.jpg 1.5x, /generated_images/images/uploads/20210420_155540_03_2560x1440.jpg 2x, /generated_images/images/uploads/20210420_155540_03_3840x2160.jpg 3x, /generated_images/images/uploads/20210420_155540_03_4032x2268.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20210420_155540_03_1280x720.jpg"
    width="1280"
    height="720"
    alt="My Minidox keyboard"
    
>
</picture>
<figcaption>
My Minidox keyboard</figcaption></figure></p>
<p>The only downside of these keyboards was that they had very fragile USB ports on the ProMicros. I was very hesitant whether I should take them with me to the office or on the road. It happened multiple times that I had to solder out the ProMicros and substitute them with a new part. The experience of desoldering a microcontroller from a PCB is horrible.</p>
<p>With the Minidox I was able to survive with only one side having a functional USB port for a longer time, thanks to the <a href="https://docs.qmk.fm/#/feature_split_keyboard?id=handedness-by-eeprom"><code>EE_HANDS</code> functionality</a>.
But, I had in my mind, that the second side would fail in the near future so I kept searching for another custom build.</p>
<blockquote>
<p>In the end, I was able to succesfully replace the ProMicro microcontroller so Minidox can serve me when I need a keyboard that doesn’t make too much noise.</p>
</blockquote>
<h2 id="picking-the-keyboard-shield">
Picking the keyboard shield</h2>
<p>So <strong>I mastered split 36-key ortholinear keyboard layout</strong>. How could I improve from that?
In commercial space, the Kinesis 360 was trending and coming out. I was waiting and thinking about whether I should buy it. The reason why is the Concave keywells. In the DIY space, there were Dactyls.
You can find generators for Dactyls on the internet, and I was playing with the source code of a <a href="https://github.com/abstracthat/dactyl-manuform">Dactyl Manuform</a> myself. I couldn’t find settings that would be appropriate and I haven’t been able to test the generated models myself, because I didn’t have a 3D printer at that time.
Therefore I kept waiting and once I thought that it was time to <strong>create my own design</strong> for the keyboard.</p>
<p>I started a <a href="https://github.com/michalvankodev/rusteaks">side project with Rust and OpenSCAD</a>. As a first model, I’ve designed a parametrized headphone holder as a table attachment. Then I started to work on the custom keyboard and it didn’t go according to my imagination. I <strong>spent too much time</strong> with the math. Rotating the individual pieces inside another piece and so on was really hard. I like algebra, don’t get me wrong. But, after spending too much time just around a row of holes for the switches. I couldn’t even estimate how long it would take me to design the edges and the thumb cluster, holes for screws, and other parts that are much more complicated.</p>
<p><strong>I wanted to build the actual keyboard</strong>, so I got to find something that I was aiming for anyway. I was mostly amazed by the work of <a href="https://bastardkb.com/">Bastard Keyboards</a>. I’ve opted for the <a href="https://github.com/Bastardkb/Skeletyl/">Skeletyl</a>. It has the same 36-key layout that I am comfortable with and it has a nice tent and is <strong>easy to 3D print</strong>.</p>
<h2 id="shopping-list">
Shopping list</h2>
<p>The most important thing is to order all the necessary parts for the build. Most of them will <strong>take a full month</strong> to arrive when you order from all around the internet.
I already had the spare diodes from the other 2 builds but I will include them in the shopping list so you can include them in your order.</p>
<p>I’ve tested some clicky switches, and they did feel amazing to me so I’ve opted for noisy switches.</p>
<p>Another customization is that while the shield is designed for a wired connection between the splits, I’ve decided that I want to try <a href="https://zmk.dev/">ZMK</a> and <strong>build a wireless keyboard</strong>.</p>
<blockquote>
<p>Tip: If you plan on using your keyboard with a desktop computer, you might want to consider a wired connection. BIOS boot up check will not pass without a physical connection to a keyboard. I have to have 2 keyboards on my desk now :(</p>
</blockquote>
<table><thead><tr><th style="text-align: left">Item</th><th style="text-align: left">Quantity</th><th style="text-align: right">Price</th><th>Link</th></tr></thead><tbody>
<tr><td style="text-align: left">DSA Blank Keycaps</td><td style="text-align: left">4</td><td style="text-align: right">14 EUR</td><td><a href="https://www.aliexpress.com/item/1005002587285218.html?spm=a2g0o.order_list.0.0.3e541802H3XiA6">🛒</a></td></tr>
<tr><td style="text-align: left">Brass Hot Melt Inset Nuts M4 X D6.0 X L5.0</td><td style="text-align: left">6 of 50pcs</td><td style="text-align: right">3 EUR</td><td><a href="https://www.aliexpress.com/item/4000232925592.html?spm=a2g0o.order_list.order_list_main.43.6e0d1802Xk8ijl">🛒</a></td></tr>
<tr><td style="text-align: left">M4 * 8 Screws</td><td style="text-align: left">6 of 100pcs</td><td style="text-align: right">3 ELR</td><td><a href="https://www.aliexpress.com/item/32896139810.html?spm=a2g0o.order_list.order_list_main.38.6e0d1802Xk8ijl">🛒</a></td></tr>
<tr><td style="text-align: left">3.7V 500mAh Li-Po Rechargeable Battery</td><td style="text-align: left">2</td><td style="text-align: right">6 EUR</td><td><a href="https://www.aliexpress.com/item/1005003262232565.html?spm=a2g0o.order_list.order_list_main.5.6e0d1802Xk8ijl">🛒</a></td></tr>
<tr><td style="text-align: left">40PIN 20CM Female To Female Jumper Dupont Wire Cables</td><td style="text-align: left">1-3</td><td style="text-align: right">5 EUR</td><td><a href="https://www.aliexpress.com/item/33041631448.html?spm=a2g0o.order_list.0.0.3e541802H3XiA6">🛒</a></td></tr>
<tr><td style="text-align: left">USB-C Male-to-Female Extension Cable</td><td style="text-align: left">2</td><td style="text-align: right">6.5 EUR</td><td><a href="https://www.aliexpress.com/item/1005003238859317.html?spm=a2g0o.order_list.order_list_main.63.6e0d1802Xk8ijl">🛒</a></td></tr>
<tr><td style="text-align: left">Mini ON-OFF switches</td><td style="text-align: left">2</td><td style="text-align: right">2 EUR</td><td><a href="https://www.aliexpress.com/item/32918008270.html?spm=a2g0o.order_list.order_list_main.16.6e0d1802Xk8ijl">🛒</a></td></tr>
<tr><td style="text-align: left">KAILH BOX JADE Switches</td><td style="text-align: left">40</td><td style="text-align: right">24 EUR</td><td><a href="https://www.eloquentclicks.com/product/novelkeys-x-kailh-box-jade-switch/">🛒</a></td></tr>
<tr><td style="text-align: left">nice!nano V2 Wireless Controller</td><td style="text-align: left">2</td><td style="text-align: right">52 EUR</td><td><a href="https://42keebs.eu/shop/parts/controllers/nice-nano-v2-wireless-controller/">🛒</a></td></tr>
</tbody></table>
<p>I attempted to print the keyboard shield myself, but I’ve been only successful in printing the tents. I had to ask my friend to print me the upper body of the Skeletyl. The shield can be easily printed in a PLA so it is really cheap. I can sum up the costs to 10 EUR for the parts.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20230223_083626-1-_1147x860.jpg 1x, /generated_images/images/uploads/20230223_083626-1-_1720x1290.jpg 1.5x, /generated_images/images/uploads/20230223_083626-1-_2294x1720.jpg 2x, /generated_images/images/uploads/20230223_083626-1-_3441x2580.jpg 3x, /generated_images/images/uploads/20230223_083626-1-_4032x3023.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20230223_083626-1-_1147x860.jpg"
    width="1147"
    height="860"
    alt="3D printed shield of the Skeletyl"
    
>
</picture>
<figcaption>
3D printed shield of the Skeletyl</figcaption></figure></p>
<h2 id="assembly">
Assembly</h2>
<p>The assembly was done on <a href="https://twitch.tv/michalvankodev">my twitch stream</a> in the span of a single weekend. It took perhaps around <strong>7 hours in total</strong>.
I’ve managed to solder everything together. The hardest part was to take off the isolant of the cables. I hurt my fingers a bit.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20230226_130037_1147x860.jpg 1x, /generated_images/images/uploads/20230226_130037_1720x1290.jpg 1.5x, /generated_images/images/uploads/20230226_130037_2294x1720.jpg 2x, /generated_images/images/uploads/20230226_130037_3441x2580.jpg 3x, /generated_images/images/uploads/20230226_130037_4032x3023.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20230226_130037_1147x860.jpg"
    width="1147"
    height="860"
    alt="Wiring of switches and diodes"
    
>
</picture>
<figcaption>
Wiring of switches and diodes</figcaption></figure></p>
<p>At first, it seemed that the keyboard would not work. It was not responding at all and I was tired already to debug it that night.
I almost didn’t sleep as I was browsing through all the possible mistakes I could’ve made. I stumbled on a <em>reddit</em> thread where someone mentioned that the issue might also be a <strong>different way of wiring the grid</strong>.
That was my assumption so I could pick up on the issue of burning the firmware the next day.
<a href="https://github.com/michalvankodev/zmk-config/commit/3a77216f9f371ca331d29d952aee2805c52b7ef0">This little change</a> in the <em>ZMK</em> configuration was enough to make the keyboard responsive. But it was not all that I had to change. With this setting the keyboard acted as if all the <strong>keys were pressed simultaneously</strong> at all times. Imagine what happens to your computer when you have the whole keyboard pressed. Random keyboard shortcuts have been triggered and my whole desktop was blown away with a tornado. I had to <a href="https://github.com/michalvankodev/zmk-config/commit/eceeb9e28dcbad0bff28c61ca032c3f991d5d4e6">change more settings</a> to make the keyboard act like a proper keyboard.
These changes basically tell the keyboard CPU how to read signals from the switches and diodes and where the signal is coming from.</p>
<p>I made a <a href="https://www.youtube.com/watch?v=EaNyRKruxJE">“short” video</a> of the whole assembly from the stream footage.</p>
<div class="video-embed">
<iframe height="100%" width="100%" src="https://www.youtube.com/embed/EaNyRKruxJE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
<h2 id="keyboard-layout">
Keyboard layout</h2>
<p>With the new keyboard, I got a chance to explore more options regarding the layout. I was interested in <strong>home row mods</strong> and I’ve found out about the <a href="https://github.com/manna-harbour/miryoku/">Miryoku</a> layout. I’ve applied little changes to it but the layers are what are so special about this layout. With this layout, I am <strong>able to press all the keyboard shortcuts</strong> I use in the desktop environment with ease. Before I had to leverage sticky keys to be able to press multiple keys from different layers. Now <strong>I don’t have a single sticky key mapped</strong> as all of the functionality can be easily achieved with Miryoku. Go ahead and read the <a href="https://github.com/manna-harbour/miryoku/tree/master/docs/reference">Miryoku reference manual</a>. I might be looking into changing the main layout to Colemak DH or Workman, but <strong>I’m not going away</strong> from the rest of the Miryoku.</p>
<h2 id="switching-switches">
Switching switches</h2>
<p>After a few months with the Jade BOX switches, I noticed that the <strong>click was not very comfortable</strong> for my thinking. My <strong>brain was too focused</strong> on the click sound and Jades performed this sound twice - once on key down and then again when the key was released. The loud sound is also annoying to anyone who is sitting next to you.
I’ve attempted to retry and revive the printer that I have at home and after a few days of failed attempts, I’ve decided to once again ask my friend to reprint the shields so I can install different switches and have 2 separate shields.
While I was working on my <a href="https://michalvanko.dev/broadcasts/2023-04-27-keyboards-ergonomics-of-21st-century">presentation about keyboard ergonomics</a> I did some research on the tactile switches and stumbled upon <a href="https://www.gloriousgaming.com/products/glorious-panda-mechanical-switches?variant=37691905933487">Glorious Panda Tactile Switches</a>. They are being sold in exactly 36-piece bundles.
The assembly of just the switches was simple and I got it working right away. I just pulled the nice!nano board out and put it into the new shield. Two switches have been struggling as the space was tight so I had to grind some of the plastics off to enlarge the space a little bit. After that, I figured that <strong>I am truly a tactile kind of guy</strong>.
I got used to the tactile switches immediately and I was able to <strong>type faster</strong> right away.</p>
<h2 id="journey-continues">
Journey continues</h2>
<p>I’d say that this Skeletyl is a <strong>magical keyboard with a lore</strong>. It is constructed from 2 different types of filament printed on 2 different printers. It has 2 different upper bodies, that can be switched. Just like a skeleton from a fiction story. It is a magnificent piece that serves me very well.</p>
<p>But <strong>this is not where the journey ends</strong>. I already can feel some aspects that I’d change about this keyboard. I’d like to go another level up. Just like the brother of Skeletyl, <a href="https://github.com/Bastardkb/Charybdis/">Charydbis</a>, has a trackball. I’d like to see if I am able to <strong>construct an ultimate tool that would replace my mouse</strong>. I’d be able to not move my hands at all which could turn very beneficial for my wrists.
I also would like to <strong>position the thumb keys lower</strong>. I’d like to have the most relaxing hand position when I’m not typing. On the Skeletyl with the tents, the thumb cluster is high. So it seems like I will be working on my own design in the end. I’m very excited to play with the track-ball or a track-pad and see how to integrate one. I’d also like the <strong>next keyboard to be wired.</strong> Every computer with bios will not start up without a keyboard being plugged in. That’s how it has been for almost a century.</p>
]]></content:encoded></item><item><title>Letter for Daniel</title><link>https://michalvanko.dev/blog/2023-06-24-letter-for-daniel</link><description><![CDATA[<p>It’s 18. 06. 2023 01:00 just after midnight. Your mother had some ache in her back for a few hours but she just wanted to get some sleep before she brings you into this world. After a few hours of talking some sense to her, I made her come to the hospital with me. Nurses are smiling at us so that we look very happy and not like she is having pain. We will see.
Your due date was just yesterday. For the whole day, people were asking us what was going on and nothing was going on. You’ve decided to cause some pain just before we want to go to sleep.</p>
]]></description><enclosure url="/images/uploads/img_9715-rotated.jpg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2023-06-24-letter-for-daniel</guid><pubDate>Sat, 24 Jun 2023 16:34:45 +0000</pubDate><content:encoded><![CDATA[<p>It’s 18. 06. 2023 01:00 just after midnight. Your mother had some ache in her back for a few hours but she just wanted to get some sleep before she brings you into this world. After a few hours of talking some sense to her, I made her come to the hospital with me. Nurses are smiling at us so that we look very happy and not like she is having pain. We will see.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_9715-rotated_645x860.jpg 1x, /generated_images/images/uploads/img_9715-rotated_967x1290.jpg 1.5x, /generated_images/images/uploads/img_9715-rotated_1290x1720.jpg 2x, /generated_images/images/uploads/img_9715-rotated_1935x2580.jpg 3x, /generated_images/images/uploads/img_9715-rotated_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_9715-rotated_645x860.jpg"
    width="645"
    height="860"
    alt="Your last moments in your mother's womb"
    
>
</picture>
<figcaption>
Your last moments in your mother’s womb</figcaption></figure></p>
<p>Your due date was just yesterday. For the whole day, people were asking us what was going on and nothing was going on. You’ve decided to cause some pain just before we want to go to sleep.</p>
<p>Your mom is now being inspected while I wait outside. I hope she will let me know whether I can go home. I’ll probably not be able to fall asleep even if I should. I cannot wait for you to come to this world. You are coming to the happiest couple I know of. We are very much in love with your mother. We want to make our love even bigger by sharing it with someone special like you. You will always be special to us.</p>
<p>I still cannot believe that we have made it to this point in life.</p>
<p>I haven’t been writing about my feelings a lot but I always wanted to share them. This one is for you. By the time you will find it the world will change significantly. Our views and perspective will also change. While we are living the best time of our life. Someone is threatening us with nuclear bombs. The Internet was invented just when I was born. It was the most impactful invention in this world from my perspective. Just as electricity was the most impactful for the people that lived here before me. It enabled us to communicate with strangers just like us to acknowledge that we are all just the same and we are living on this planet together. We should also take care of each other. There will be many people who will influence your life as well. Hopefully, we, your parents, will be the best influencers that you will look up to.
I don’t have an idea when you will be able to find this letter. Read it. And believe me, it will have different meanings anytime you read it as you grow older.</p>
<p>You are coming into this world into a very happy and loving family. Nothing is perfect but at the same time, it cannot be better. All of your grandparents are looking forward to helping us take the best care of you. Your cousin, Timea, is just 1 year old so I don’t know how she is going to appreciate you stealing the spotlight from her. I believe you two will create a great team together.
We are not yet decided on other siblings. We would both like to have more kids. We are just hesitant. We don’t know whether we are capable of taking… (finish this sentence).</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20230618_143334crop_970x860.jpg 1x, /generated_images/images/uploads/20230618_143334crop_1455x1290.jpg 1.5x, /generated_images/images/uploads/20230618_143334crop_1940x1720.jpg 2x, /generated_images/images/uploads/20230618_143334crop_2669x2367.jpg 3x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20230618_143334crop_970x860.jpg"
    width="970"
    height="860"
    alt="Our first moment together"
    
>
</picture>
<figcaption>
Our first moment together</figcaption></figure></p>
<p>You made it to this world, yesterday. Congratulations. You don’t have an idea how lucky you are. You’re very healthy as well as your mom. I didn’t plan on being present during your birth. I usually get dizzy just talking about injuries and surgeries. I just went in because I wanted to support your mom. I’ve never seen anyone experiencing this much pain and I admire every woman who had to go through this. It was a life-changing experience for me. It wasn’t very pretty. There was blood and many different fluids. But seeing you come out healthy right into your mother’s arms was the most heartwarming experience I’ve ever seen. Your mom was so happy. She immediately said you have her eyes.
Just right after we were celebrating you with the whole staff I got to spend some time just with you. You were so calm. I couldn’t believe that just right after being born, you already trusted me and you’ve been feeling cozy around me.
You are very lucky. Not that many kids are born into loving families. Remember - You are special.</p>
<p>Today, your grandparents are coming for a visit. They are so so excited. Every one of them. I bet that they get you many gifts. Happy one day to you.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20230619_142638_645x860.jpg 1x, /generated_images/images/uploads/20230619_142638_967x1290.jpg 1.5x, /generated_images/images/uploads/20230619_142638_1290x1720.jpg 2x, /generated_images/images/uploads/20230619_142638_1935x2580.jpg 3x, /generated_images/images/uploads/20230619_142638_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20230619_142638_645x860.jpg"
    width="645"
    height="860"
    alt="Your grandmother couldn't wait to hold you in her arms"
    
>
</picture>
<figcaption>
Your grandmother couldn’t wait to hold you in her arms</figcaption></figure></p>
<p>I just can’t comprehend how fortunate you are. Thinking of how cringy you will feel when you will be reading this in your teenage. But don’t worry, you will get over it.</p>
<p>I’m looking forward to playing with you. I will let you do anything that will make you happy. You can be whatever you want. Just tell me. Don’t be afraid of anything. Can’t wait to teach you everything you will be curious about. I am planning on using my programming experience for building a few games for you. Thinking of that perhaps one day you will help me build some games. You can help just by giving some ideas or voice acting some characters or by composing music. If you are interested I will also find the patience for teaching you to code. Just remember that I am here always for you. No matter what.</p>
<p>Words can sometimes hurt but not all words are true. It’s hard to distinguish the truth from made-up stuff. Especially when everyone is allowed to have their own opinion. You will also shape your opinions and it is important to not waste your time on stuff that is not worth it. Through the years we have been listening to our ancestors and keeping their stories as the truth. Often these truths are skewed so they don’t make them look so bad. People have been participating in genocides. They have been treating other people like they own them just like property. They did fight against each other believing they are the chosen ones to dominate and rule the world. Only you are the chosen one to rule what you believe is good for you and your closest.
Don’t believe someone just their words. Believe in actions. And if you have a hard time thinking who to believe, look at their actions and see whether they are trying to help somebody or they are trying to hurt someone. Very often they will just follow their agenda believing they are doing good but do they? Throughout the centuries, people have wanted to rule over others. They found many creative ways. They didn’t have bright minds that were able to explain how the physics of this universe works. They invented stories that will help them not to focus on those and push their ideals onto others. Many many times are proven wrong but so eager to fight for their ideals that their stories are still considered true because they are afraid to admit that they have been living a lie and sacrificing their time for something that is just another storyteller’s idea. Even if everyone believes that something is the truth, only proof can prove it.</p>
<p>You’ve been so healthy that you’ve been released home early. Your first day at home has been great. You thought me that 5 AM is a great time to wake up. The sun is already up and shining so we can shine as well. Your whole family loves you. I love your Mom. I love You. Thank you for coming into our lives. We will always love you.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20230621_204051_645x860.jpg 1x, /generated_images/images/uploads/20230621_204051_967x1290.jpg 1.5x, /generated_images/images/uploads/20230621_204051_1290x1720.jpg 2x, /generated_images/images/uploads/20230621_204051_1935x2580.jpg 3x, /generated_images/images/uploads/20230621_204051_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20230621_204051_645x860.jpg"
    width="645"
    height="860"
    alt="Your first day at home. You shared the happiness with us."
    
>
</picture>
<figcaption>
Your first day at home. You shared the happiness with us.</figcaption></figure></p>
]]></content:encoded></item><item><title>Keyboards &amp; ergonomics of 21st century</title><link>https://michalvanko.dev/blog/2023-04-27-keyboards-ergonomics-of-21st-century</link><description><![CDATA[<p>I’ve finished another custom keyboard build this year. It got me motivated to share my experience with custom keyboards with the community. I’ve got in touch with a friendly community in Poprad by recommendation. I haven’t done any presentations for a few years. My positive keyboard experience re-ignited my passion. Hopefully, it sparked some ideas and someone finds my tips helpful.</p>
]]></description><enclosure url="/images/uploads/teta.png" length="" type="image/png"/><guid>https://michalvanko.dev/blog/2023-04-27-keyboards-ergonomics-of-21st-century</guid><pubDate>Thu, 27 Apr 2023 21:22:21 +0000</pubDate><content:encoded><![CDATA[<p>I’ve finished another custom keyboard build this year. It got me motivated to share my experience with custom keyboards with the community. I’ve got in touch with a friendly community in Poprad by recommendation. I haven’t done any presentations for a few years. My positive keyboard experience re-ignited my passion. Hopefully, it sparked some ideas and someone finds my tips helpful.</p>
<div class="video-embed">
<iframe height="100%" width="100%" src="https://www.youtube.com/embed/oMp9O5aIyhg?start=228" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>]]></content:encoded></item><item><title>DevBreak #5 Joys and concerns of Engineering manager with Pavol Dudrík</title><link>https://michalvanko.dev/blog/2023-02-04-devbreak-5-joys-and-concerns-of-engineering-manager</link><description><![CDATA[<p>In this episode, I’ve invited my friend <a href="https://www.linkedin.com/in/pavol-dudr%C3%ADk-043100168/">Pavol Dudrík</a> to talk about what motivates us and makes us happy about the work of the Engineering manager. This job combines two views, and the transition from engineer to manager is often full of pitfalls. We want to share our experience and dispel misconceptions often associated with managerial positions in IT.
This episode aired on the 4th of February 2023 and is in the Slovak language.</p>
]]></description><enclosure url="/images/uploads/devbreak.jpeg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2023-02-04-devbreak-5-joys-and-concerns-of-engineering-manager</guid><pubDate>Sat, 4 Feb 2023 20:22:21 +0000</pubDate><content:encoded><![CDATA[<p>In this episode, I’ve invited my friend <a href="https://www.linkedin.com/in/pavol-dudr%C3%ADk-043100168/">Pavol Dudrík</a> to talk about what motivates us and makes us happy about the work of the Engineering manager. This job combines two views, and the transition from engineer to manager is often full of pitfalls. We want to share our experience and dispel misconceptions often associated with managerial positions in IT.
This episode aired on the 4th of February 2023 and is in the Slovak language.</p>
<div class="video-embed">
<iframe src="https://player.twitch.tv/?video=1728904048&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
</div>]]></content:encoded></item><item><title>DevBreak #4 State of JS 2022 review with Filip Seman</title><link>https://michalvanko.dev/blog/2023-01-21-devbreak-4-state-of-js-2022</link><description><![CDATA[<p>After another year, the results of the <a href="https://2022.stateofjs.com/en-us/">State of JS</a> survey have been published.
I’ve invited my #1 fan and friend <a href="https://www.linkedin.com/in/xseman/">Filip Seman</a>.</p>
]]></description><enclosure url="/images/uploads/devbreak-4-3-.png" length="" type="image/png"/><guid>https://michalvanko.dev/blog/2023-01-21-devbreak-4-state-of-js-2022</guid><pubDate>Sat, 21 Jan 2023 20:22:21 +0000</pubDate><content:encoded><![CDATA[<p>After another year, the results of the <a href="https://2022.stateofjs.com/en-us/">State of JS</a> survey have been published.
I’ve invited my #1 fan and friend <a href="https://www.linkedin.com/in/xseman/">Filip Seman</a>.
This episode aired on the 21st of January 2023 and is in the Slovak language.</p>
<div class="video-embed">
<iframe src="https://player.twitch.tv/?video=1715138585&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
</div>]]></content:encoded></item><item><title>Lovely London Trip</title><link>https://michalvanko.dev/blog/2022-11-27-lovely-london-trip</link><description><![CDATA[<p>I have been thinking for a long time about how should I continue my blog writing. Right now, I am standing underneath <em>Tower Bridge</em> in London and I got so many inspirations on what should I write about, but the first article should be about our trip to London.
This time I want to try an experiment. Instead of writing this blog post, I am going to dictate it over to my phone and do some edits after. I’ve found that I can put my thoughts much quicker into something that can be edited. I know that I haven’t been posting my weekly posts since June, but to be honest, I had to find some motivation over time and I will be not continuing with weeklies anymore. I would much rather post something with a theme around my life.</p>
]]></description><enclosure url="/images/uploads/image_7115.jpg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2022-11-27-lovely-london-trip</guid><pubDate>Sun, 27 Nov 2022 19:49:09 +0000</pubDate><content:encoded><![CDATA[<p>I have been thinking for a long time about how should I continue my blog writing. Right now, I am standing underneath <em>Tower Bridge</em> in London and I got so many inspirations on what should I write about, but the first article should be about our trip to London.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_7631_1147x860.jpg 1x, /generated_images/images/uploads/img_7631_1720x1290.jpg 1.5x, /generated_images/images/uploads/img_7631_2294x1720.jpg 2x, /generated_images/images/uploads/img_7631_3441x2580.jpg 3x, /generated_images/images/uploads/img_7631_4032x3023.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_7631_1147x860.jpg"
    width="1147"
    height="860"
    alt="Tower Bridge surrounded in typical London weather"
    
>
</picture>
<figcaption>
Tower Bridge in London</figcaption></figure></p>
<p>This time I want to try an experiment. Instead of writing this blog post, I am going to dictate it over to my phone and do some edits after. I’ve found that I can put my thoughts much quicker into something that can be edited. I know that I haven’t been posting my weekly posts since June, but to be honest, I had to find some motivation over time and I will be not continuing with weeklies anymore. I would much rather post something with a theme around my life.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/image_7115_645x860.jpg 1x, /generated_images/images/uploads/image_7115_967x1290.jpg 1.5x, /generated_images/images/uploads/image_7115_1290x1720.jpg 2x, /generated_images/images/uploads/image_7115_1935x2580.jpg 3x, /generated_images/images/uploads/image_7115_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/image_7115_645x860.jpg"
    width="645"
    height="860"
    alt="Photo of the Big Ben taken right accross the street"
    
>
</picture>
<figcaption>
Big Ben</figcaption></figure></p>
<p>We are on the 4th day of visiting London with my wife.
We have been at <em>Buckingham Palace</em> yesterday. Today we were walking across the <em>Tower Bridge</em> and even though it is still just the 19th of November it’s already Christmas time over here. There are Christmas markets at every single popular town square and alley.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/image5_1147x860.jpg 1x, /generated_images/images/uploads/image5_1720x1290.jpg 1.5x, /generated_images/images/uploads/image5_2294x1720.jpg 2x, /generated_images/images/uploads/image5_3441x2580.jpg 3x, /generated_images/images/uploads/image5_4032x3023.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/image5_1147x860.jpg"
    width="1147"
    height="860"
    alt="Christmas market in Trafalgar square"
    
>
</picture>
<figcaption>
Christmas market in Trafalgar square</figcaption></figure></p>
<p>I have been to London previously around 7 or 9 years ago. Actually, I have been here twice already, but my wife hasn’t visited London yet so we decided that it would be a lovely small vacation before Christmas. From my perspective, London hasn’t changed much over the last few years. It is still a multicultural place full of possibilities and excitement. There are so many places to visit. We are walking around for the 4th day straight and we still haven’t seen everything that we have planned. This is also the first time I have taken my new <a href="https://www.pinqponq.com/products/blok-medium-polished-black?_pos=1&amp;_psq=blok+medium&amp;_ss=e&amp;_v=1.0">pinqponq backpack</a> with me. I must write a review on that one at some point. It is excellent.</p>
<p>Most of our commute is done by walking around. One of the most challenging things to realize is that everyone walks mainly on the left side of the road. It doesn’t only apply to vehicles but also to pedestrians. Other than that we are mostly using the London underground tube system. It consists of 11 lines. The first underground railway was built in 1863. Keeping up with the demand and innovation of the tube system is astonishingly mind-blowing. You don’t have to buy any card or ticket to use public transportation anymore. You can just use Apple pay or Google pay with your mobile phone saving a lot of unnecessary ink and paper.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_7110_645x860.jpg 1x, /generated_images/images/uploads/img_7110_967x1290.jpg 1.5x, /generated_images/images/uploads/img_7110_1290x1720.jpg 2x, /generated_images/images/uploads/img_7110_1935x2580.jpg 3x, /generated_images/images/uploads/img_7110_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_7110_645x860.jpg"
    width="645"
    height="860"
    alt="Westminster tube station photobombed by myself"
    
>
</picture>
<figcaption>
Westminster tube station photobombed by myself</figcaption></figure></p>
<p><a href="https://en.wikipedia.org/wiki/Kensington_Gardens">Kensington Gardens</a> have been the nicest park that we’ve visited. They are connected to the largest park in London, <a href="https://en.wikipedia.org/wiki/Hyde_Park,_London">Hyde Park</a>. We haven’t been able to take a walk around Hyde Park. <a href="https://hydeparkwinterwonderland.com/">Hyde Park Winter Wonderland</a> festival has already started and it has taken a colossal area of the park. We haven’t visited the festival, but we have agreed that we will take our kids to this festival one day.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/image1_645x860.jpg 1x, /generated_images/images/uploads/image1_967x1290.jpg 1.5x, /generated_images/images/uploads/image1_1290x1720.jpg 2x, /generated_images/images/uploads/image1_1935x2580.jpg 3x, /generated_images/images/uploads/image1_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/image1_645x860.jpg"
    width="645"
    height="860"
    alt="A statue of Queen Victoria sculpted by her daughter, Princess Louise, to celebrate 50 years of her mother's rule stands outside Kensington Palace"
    
>
</picture>
<figcaption>
A statue of Queen Victoria sculpted by her daughter</figcaption></figure></p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_7735_645x860.jpg 1x, /generated_images/images/uploads/img_7735_967x1290.jpg 1.5x, /generated_images/images/uploads/img_7735_1290x1720.jpg 2x, /generated_images/images/uploads/img_7735_1935x2580.jpg 3x, /generated_images/images/uploads/img_7735_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_7735_645x860.jpg"
    width="645"
    height="860"
    alt="View of the Hyde Park Winter Wonderland from the Kensington Gardens"
    
>
</picture>
<figcaption>
View of the Hyde Park Winter Wonderland from the Kensington Gardens</figcaption></figure></p>
<h2 id="unhappy-ending">
Unhappy ending</h2>
<p>One big lesson for us for future travels is to ensure that we book the correct date for flight tickets and accommodation. It can save you a big money. We, unfortunately, missed the dates on the return flight ticket and booked them a week later than we thought. We only realized this at the airport right before the security check. We had to buy new tickets for a different flight from a different airport.</p>
<p>And next time we will take a laptop with us so we can watch movies or when we accidentally overbook our flight tickets, we can at least work while being on a workation.</p>
<p>Credits for all pictures go to my wife <a href="https://www.instagram.com/marika_vnk/">Marika</a>. She says Hi!</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/image2_1147x860.jpg 1x, /generated_images/images/uploads/image2_1720x1290.jpg 1.5x, /generated_images/images/uploads/image2_2294x1720.jpg 2x, /generated_images/images/uploads/image2_3441x2580.jpg 3x, /generated_images/images/uploads/image2_4032x3023.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/image2_1147x860.jpg"
    width="1147"
    height="860"
    alt="Me and my wife on the Queens walk"
    
>
</picture>
<figcaption>
Me and my wife on the Queens walk</figcaption></figure></p>
]]></content:encoded></item><item><title>Our attempt at Rusty game jam - Weekly #25-2022</title><link>https://michalvanko.dev/blog/2022-06-26-our-attempt-at-rusty-game-jam-weekly-25-2022</link><description><![CDATA[<p>Long time no see!
I noticed that I haven’t even posted about our wedding day.</p>
]]></description><enclosure url="/images/uploads/screenshot-from-2022-06-26-22-37-16.png" length="" type="image/png"/><guid>https://michalvanko.dev/blog/2022-06-26-our-attempt-at-rusty-game-jam-weekly-25-2022</guid><pubDate>Sun, 26 Jun 2022 20:02:47 +0000</pubDate><content:encoded><![CDATA[<p>Long time no see!</p>
<h2 id="we-are-married">
We are married</h2>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_2591_645x860.jpg 1x, /generated_images/images/uploads/img_2591_720x960.jpg 1.5x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_2591_645x860.jpg"
    width="645"
    height="860"
    alt="Our wedding day"
    
>
</picture>
<figcaption>
Our wedding day</figcaption></figure></p>
<p>I noticed that I haven’t even posted about our wedding day.
It was the best day of my life. Very beautiful day. I cried almost the whole day because it was so beautiful. I love my wife!</p>
<h2 id="rusty-game-jam">
Rusty game jam</h2>
<p>This week I’ve attended a <a href="https://itch.io/jam/rusty-jam-2">Rusty game jam #2</a>. It was a great experience for me as I have been able to <strong>dive deeper into Rust and <a href="https://bevyengine.org/">bevy game-development engine</a></strong>. Big thanks to <a href="https://github.com/silen-z/">@silen-z</a>, who joined my team and helped me get going at the start. We <strong>didn’t manage to finish the game</strong> in time. As we only had a week and we also didn’t code each day of the week. We have only been able to get some collision mechanics and map creation working. But I am very proud anyway. You can <a href="https://github.com/michalvankodev/egg-fetcher/">check the source code for the game on my GitHub</a> and also <a href="https://github.com/michalvankodev/egg-fetcher/releases/tag/v1.1">download binaries to try the game out</a> on any platform. Thanks to <a href="https://github.com/septum">@septum</a> for providing <a href="https://github.com/septum/rusty_jam_bevy_template">a good boilerplate for a bevy project</a>. The whole process was <strong>broadcasted on my stream</strong>. I am very thankful for anyone who participated.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/screenshot-from-2022-06-26-22-37-16_1280x761.png 1x, /generated_images/images/uploads/screenshot-from-2022-06-26-22-37-16_1286x765.png 1.5x"
    type="image/png"
>
    <img
    src="/generated_images/images/uploads/screenshot-from-2022-06-26-22-37-16_1280x761.png"
    width="1280"
    height="761"
    alt="Egg fetcher game preview"
    
>
</picture>
<figcaption>
Egg fetcher game preview</figcaption></figure></p>
<p><a href="/showcase/egg-fetcher">You can check the result built with WASM here.</a></p>
<h2 id="what">
What’s up with the weeklys</h2>
<p>I have to admit something. I was not inspired or motivated to write weeklys for the last two months. For that, I feel a little bumped as there were many good thoughts worth sharing. I was not very comfortable writing as the last month was very tough. During that time I was focused on inner development and figuring out how my brain responds to different stimuli. I’ve been able to find a new project to work on. The onboarding story was one of the hardest I went through. I managed to move on and stop thinking about what could’ve I done better in the past and focus more on what can I do better now.</p>
<h2 id="picks">
Picks</h2>
<p>I should have at least 10 recommendations, but I haven’t accumulated any in my notes.</p>
<p>But here are some podcasts that made my days better:</p>
<ul>
<li><a href="https://open.spotify.com/show/0SvQ7orsdxIFhfEgEzcZuF">Podcast Nevyhoreni (not burnt out)</a> by Forbes Slovensko</li>
<li>Definitively <a href="https://www.youtube.com/c/ThePrimeagen">check out Primeagen’s last few videos</a>. He inspires me so much. Doesn’t have a problem talking about his failures openly on the internet.</li>
</ul>
<p>Another good resource I’ve been able to find is another Rust framework for building Web applications called <a href="https://sycamore-rs.netlify.app/">Sycamore</a>.</p>
<p>That’s it. Let’s jump into another week inspired after a very successful one and my plan is to get better with Rust. I will try to find a new project to work on. Something smaller that could improve my workflow.</p>
]]></content:encoded></item><item><title>DevBreak #3 Full-stack development with Dominik Štefan</title><link>https://michalvanko.dev/blog/2022-06-09-devbreak-3-full-stack-development</link><description><![CDATA[<p>My friend and former colleague <a href="https://www.linkedin.com/in/dominik-%C5%A1tefan-167266180/">Dominik Štefan</a> came by to talk about his experience working on full-stack web application development. We talked about many different topics but most importantly we had a lot of fun.
This episode aired on the 9th of June 2022 and is in the Slovak language.</p>
]]></description><enclosure url="/images/uploads/deevbreakdoninik.jpeg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2022-06-09-devbreak-3-full-stack-development</guid><pubDate>Thu, 9 Jun 2022 20:22:21 +0000</pubDate><content:encoded><![CDATA[<p>My friend and former colleague <a href="https://www.linkedin.com/in/dominik-%C5%A1tefan-167266180/">Dominik Štefan</a> came by to talk about his experience working on full-stack web application development. We talked about many different topics but most importantly we had a lot of fun.
This episode aired on the 9th of June 2022 and is in the Slovak language.</p>
<div class="video-embed">
<iframe src="https://player.twitch.tv/?video=1503339615&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
</div>]]></content:encoded></item><item><title>Treasure hunt - Weekly #18-2022</title><link>https://michalvanko.dev/blog/2022-05-07-treasure-hunt-weekly-18-2022</link><description><![CDATA[<p>I have so much news that I have no idea where to start.
This one would be one of those posts that I will revisit many times to be able to look back and analyze what were my feelings and where I’ve stood and how I coped with them.</p>
]]></description><guid>https://michalvanko.dev/blog/2022-05-07-treasure-hunt-weekly-18-2022</guid><pubDate>Sat, 7 May 2022 08:35:51 +0000</pubDate><content:encoded><![CDATA[<p>I have so much news that I have no idea where to start.
This one would be one of those posts that I will revisit many times to be able to look back and analyze what were my feelings and where I’ve stood and how I coped with them.</p>
<h2 id="leaving-sudolabs">
Leaving Sudolabs</h2>
<p>Let’s start with a shocker. After 2 and a half years I’ve decided to part ways with Sudolabs. It was a really hard decision to make but we have decided that it would be the best for both parties. Parting ways took only 2 days which should make things easy. I’ve made strong bondings with my colleagues and it was <strong>heart-crushing to leave them</strong>. It’s not easy to write about these emotions after 2 weeks, either.</p>
<p><strong>I want to thank everyone</strong> who helped me to grow and for all those amazing moments we spent together. It was by far the best experience I’ve had in a company. I hope we will still see each other and help each other even without any obligations to do so.</p>
<h2 id="treasure-hunt">
Treasure hunt</h2>
<p>As I haven’t had any other job in sight, I’ve started to look around the job market. There are too many companies and even more recruiters. It’s going to be hard to find and pick one that will fulfill my expectations and I’d fit in. I have a pretty good vision of what I want to do next. Certainly, I’d like to have a much bigger focus on the development than on management. I would love to teach/mentor/coach my colleagues to perform at their best. That was the one thing I enjoyed the most.</p>
<p>I haven’t realized that I have built up a work routine. I have a hard time not focusing on programming during my time off. I have time to learn a lot about myself. I would like to use as much time effectively even if I don’t have to.</p>
<p>I have some interviews planned, but I don’t want to overdo them. I’m surprised by the variation between these processes. The job market is oversaturated and it shows in the interviewing process of companies of different scales. <strong>My prediction for the future</strong> is that we will have <strong>many smaller companies</strong> with their own little cultures and bubbles. <strong>Just like in code</strong>, we want our functions to be small, granular, composable, and focused on doing one thing only and doing it well, we will have many small companies focus on doing what they are good at and doing it well.</p>
<p>Scaling companies is hard. And it will be hard. People are searching for liberation. They are going to find a job that will allow them to work whenever they want in terms that, they are going to be satisfied with. <strong>Diversification is also a very important aspect</strong>. Having hard-stop rules, ladders, and a complicated onboarding process suited only for one type of developer is exactly what makes the stereotype jokes in your company target every single one of your employees.</p>
<p>Maybe I should build my own company. It is something I’ve been thinking of for a few months. I am just avoiding having responsibility for all the shit I’d have to deal with around bureaucracy. I’m still giving a chance to someone who is not scared of that and is more educated about managing finances and people.</p>
<h2 id="devbreak-episode--2">
DevBreak episode #2</h2>
<p>So on the exact day that I’ve left Sudolabs, I have been able to save the <em>DevBreak talk</em>. Not me exactly. It has been saved by my awesome friends <a href="https://www.linkedin.com/in/daniela-chovancova-boostello/">Daniela</a>, <a href="https://www.linkedin.com/in/samuelfarkas/">Samuel</a>, and <a href="https://www.linkedin.com/in/filipjenik/">Filip</a>. They’ve attended <a href="">HackKosice hackathon</a> and were generous to talk about <strong>their experience and impressions</strong> on the <em>DevBreak talk</em>. <a href="https://www.twitch.tv/videos/1468441353">Go check out the episode</a>. They’ve brought a very good vibe and I had a great time talking with them.</p>
<p>I’ve already contacted my next guest for episode #3 and I am very excited for this one. As I have a wedding to plan and attend I don’t know when we will air the episode. But it will be a great one! Can’t wait.</p>
<h2 id="stream-updates">
Stream updates</h2>
<p>We have set up a <a href="https://discord.gg/uVYaDa25Jf">Discord server</a> where you can chat with our community whenever. <strong>We share dev tips and memes</strong>, but I’d also like to <strong>receive feedback</strong> on the stream content.</p>
<p>The last 3 streams have been devoted to game development with <a href="">Bevy game engine</a>. I’ve started to fulfill my childhood dreams and started a journey full of learning stuff that I haven’t been used to. I’ve made some games before. But I would say that this one is going to be of a larger scale.</p>
<p>I want to build a card-based strategy game. Something similar to <a href="https://playhearthstone.com/en-us">Hearthstone</a> and <a href="https://www.ubisoft.com/en-us/game/heroes-of-might-and-magic-3-hd">Heroes of Might and Magic</a>. There is a lot to learn about the engine itself so the mechanics of the game will be developed in later stages.</p>
<p>We have agreed with the community that we would like to also do some l33tcode challenges. So the variety will still be there. I’ll just want to gain more people to start doing exercises like this so we can compare our solutions with each other.</p>
<h2 id="getting-married">
Getting married</h2>
<p>This post is already filled with so much content, but I can’t skip this chapter.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20220422_214530_645x860.jpg 1x, /generated_images/images/uploads/20220422_214530_967x1290.jpg 1.5x, /generated_images/images/uploads/20220422_214530_1290x1720.jpg 2x, /generated_images/images/uploads/20220422_214530_1935x2580.jpg 3x, /generated_images/images/uploads/20220422_214530_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20220422_214530_645x860.jpg"
    width="645"
    height="860"
    alt="Wedding announcment"
    
>
</picture>
<figcaption>
Wedding announcment</figcaption></figure></p>
<p>My and my soon-to-be beautiful wife are getting married next Saturday.
Very exciting times. I am going to meet my whole family after so many years. It’s going to be not only the party of the year but of a whole life.</p>
<h2 id="picks">
Picks</h2>
<ul>
<li>
<p>As I’ve been doing research on <a href="https://bevyengine.org/">Bevy game engine</a>, I’ve stumbled on this amazing <a href="https://www.youtube.com/watch?v=WnUzWuaMzuM&amp;list=PLT_D88-MTFOPPl75g4WshL1Gx2bnGTUkz">series of tutorials</a> by <a href="https://www.youtube.com/channel/UC7v3YEDa603x_84PgCPytzA">Logic Projects</a>. It’s a really fast walkthrough of challenges during game development when starting with Bevy. It helped me build a better mental model of how should I structure entities and components for my own game.</p>
</li>
<li>
<p>I’ve discovered a <a href="https://www.youtube.com/playlist?list=PLnEuXUQOvbCi5OgBipQDnUqXeSna5S6fz">great podcast</a> by <a href="https://www.truban.sk/">Michal Truban</a>. I’ve listened to some episodes and I have to say that it is educational. Nice tips from <a href="https://www.youtube.com/watch?v=XzvJJJIjl90&amp;list=PLnEuXUQOvbCi5OgBipQDnUqXeSna5S6fz&amp;index=13">Andrea Vadkerti on coaching</a>. Or from <a href="https://www.youtube.com/watch?v=XYfBGkcePRw&amp;list=PLnEuXUQOvbCi5OgBipQDnUqXeSna5S6fz&amp;index=3">Matej Ftáčnik</a> on how to run a successful company.</p>
</li>
</ul>
]]></content:encoded></item><item><title>DevBreak #2 Stories and impressions from Hack Kosice 2022</title><link>https://michalvanko.dev/blog/2022-04-26-devbreak-2-stories-and-impressions-hack-kosice</link><description><![CDATA[<p>At the last possible moment, I have been able to invite <strong>Daniela, Samuel, and Filip</strong> over to talk about stories and their impressions of the <em>Hack Kosice 2022</em> event that took place last weekend.
This episode aired on the 26th of April 2022 and is in the Slovak language.</p>
]]></description><enclosure url="/images/uploads/deeevbreak2.jpeg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2022-04-26-devbreak-2-stories-and-impressions-hack-kosice</guid><pubDate>Tue, 26 Apr 2022 20:22:21 +0000</pubDate><content:encoded><![CDATA[<p>At the last possible moment, I have been able to invite <strong>Daniela, Samuel, and Filip</strong> over to talk about stories and their impressions of the <em>Hack Kosice 2022</em> event that took place last weekend.
This episode aired on the 26th of April 2022 and is in the Slovak language.</p>
<div class="video-embed">
<iframe src="https://player.twitch.tv/?video=1468441353&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
</div>]]></content:encoded></item><item><title>Happy Easter - Weekly #15-2022</title><link>https://michalvanko.dev/blog/2022-04-17-happy-easter-weekly-15-2022</link><description><![CDATA[<p>Hello,
I’ve been very busy the last 3 weeks and I’m so so sorry I haven’t been able to post an update. Now, I have so many things in the pipeline, I hope I’ll be able to remind myself of everything.</p>
]]></description><guid>https://michalvanko.dev/blog/2022-04-17-happy-easter-weekly-15-2022</guid><pubDate>Sun, 17 Apr 2022 08:05:05 +0000</pubDate><content:encoded><![CDATA[<p>Hello,</p>
<p>I’ve been very busy the last 3 weeks and I’m so so sorry I haven’t been able to post an update. Now, I have so many things in the pipeline, I hope I’ll be able to remind myself of everything.</p>
<p>I’m <strong>organizing a wedding</strong> with my beautiful soon-to-be wife, so I am running errands all over the world. I’m trying to still be <strong>available to stream and focus on learning</strong> in between.</p>
<h2 id="game-dev-research">
Game dev research</h2>
<p>In the latest stream, I’ve taken some time off coding and started to work on research for the <strong>upcoming side-project</strong> of creating a <strong>strategy card-based game</strong> similar to Blizzard’s Hearthstone. I’m still holding off specifying the rules and mechanics of the game. I’ve been <strong>searching for a game engine</strong> that will help me write code that I will be comfortable and productive with.</p>
<p>The engine that made me most excited was <a href="https://bevyengine.org/">Bevy</a>, built in Rust. The <a href="https://bevy-cheatbook.github.io/programming/ecs-intro.html">Entity Component System architecture</a> looks very interesting and I would love to try it out. I see some potential for reusing the architecture in different programming languages and domains.</p>
<p>I’ve been also researching the ecosystem of the <a href="https://go.dev/">Go language</a>. The ecosystem is very plain and I’ve been only able to find a single 3D game engine that didn’t have much learning materials or community around it. The language itself looks very simple, in a good sense of course. There aren’t that many concepts that need to be learned and people using it are very satisfied with their productivity. That is one thing I am worried about with Rust.</p>
<h2 id="devbreak--2">
DevBreak #2</h2>
<p>The next episode of my lifestyle talk show DevBreak is in the makings. I’ve got some people interested in the idea. We just have to agree on the exact time and place. Announcement preparations will start this week and hopefully, it will be a very cool episode. I think it will be very very interesting as we will have 2 different viewpoints on a very close topic to everyone in the IT field.</p>
<h2 id="headphone-update">
Headphone update</h2>
<p>My brand new <a href="https://europe.beyerdynamic.com/dt-900-pro-x.html">Beyerdynamic DT 900 PRO X</a> headphones have finally arrived.
I fell in love with them immediately. The initial date of arrival was estimated to be in May. One day I just received an SMS that they will be delivered the next day.
I can’t promise a review because I don’t see myself as an expert in the field of HIFI headphones but <strong>I can recommend them</strong> to anyone who seeks out a very nice balanced sound with very comfortable construction.</p>
<p>Also, these are <strong>my first open-back headphones</strong> and they are amazing for very long listening sessions and home-office. The only downside is that they are not able to block noise from a washing machine or a vacuum robot.</p>
<h2 id="picks">
Picks</h2>
<ul>
<li>
<p>My inspiration The Primeagen has released a very good <a href="https://www.youtube.com/watch?v=Z0GX2mTUtfo">Youtube video on a comparison of performance between TypeScript, Go, and Rust</a>. Very good content from him. I’d like to know what went bad with the Rust example as the performance bottlenecks look like can be optimized.</p>
</li>
<li>
<p><a href="https://kerkour.com/why-not-rust">Short article by Sylvain Kerkour</a> but a very good explanation on why would anyone not choose Rust for some use-cases.</p>
</li>
<li>
<p>We have been <strong>struggling with knowledge sharing</strong> in our codebase at work as it is growing and growing and the team is also growing. We’ve decided to <strong>focus more on documentation</strong> and I’ve found this <a href="https://www.writethedocs.org/guide/">gigantic guide on writing documentation</a>. It looks like it’s focused on Open Source projects, but I think every project should be opened to communicate its inner workings in the same way. We don’t need to read the code of open source libraries when we use them. Why won’t we provide the same comfort to our colleagues as well?</p>
</li>
<li>
<p><a href="https://github.com/xseman">@xseman</a> has linked me to this extension to the <a href="https://www.gnome.org/">Gnome Desktop environment</a> called <a href="https://github.com/pop-os/shell">Pop OS shell</a>, during one of the streams. It brings almost all the things I am missing from <a href="https://i3wm.org/">i3 window manager</a> into Gnome. It’s been developed for <a href="https://pop.system76.com/">System76 Pop OS operating system</a>, but it was no struggle to install it on my <a href="https://getfedora.org/">Fedora</a> machine. Huge thanks to Filip!</p>
</li>
</ul>
]]></content:encoded></item><item><title>Coming from DevBreak - Weekly #11-2022</title><link>https://michalvanko.dev/blog/2022-03-19-coming-from-devbreak-weekly-11-2022</link><description><![CDATA[<p>Welcome back,
You might notice, that I’ve skipped 2 weeks of posting. I have a very good excuse. I’ve been on a vacation in Mauritius. It was the best vacation I’ve ever been on. The only unfortunate part was that the flight back home from Istanbul to Budapest was canceled. So the trip got prolonged for another 2 days but we weren’t prepared for the weather.</p>
]]></description><guid>https://michalvanko.dev/blog/2022-03-19-coming-from-devbreak-weekly-11-2022</guid><pubDate>Sat, 19 Mar 2022 21:22:35 +0000</pubDate><content:encoded><![CDATA[<p>Welcome back,</p>
<p>You might notice, that I’ve skipped 2 weeks of posting. I have a very good excuse. I’ve been on a vacation in Mauritius. It was the best vacation I’ve ever been on. The only unfortunate part was that the flight back home from Istanbul to Budapest was canceled. So the trip got prolonged for another 2 days but we weren’t prepared for the weather.</p>
<h2 id="devbreak--1">
DevBreak #1</h2>
<p>Anyway, the comeback from the vacation was strong. I’ve successfully <strong>started my own talk show - <em>DevBreak</em></strong>. I’ve invited my very close friend <a href="https://twitter.com/lukasorgovan">Lukas Orgovan</a> and we had a wonderful time together on the stream. You can <a href="https://www.twitch.tv/videos/1427225407">check out the first episode here</a> where we talked about the <a href="https://2021.stateofjs.com/en-US/">State Of JS survey results</a> and much more about software development. I’ve also gathered some feedback and I am very happy with the responses I got. I will be trying to improve the show and the stream onwards.
I still don’t know how often I should do the show, and who will I invite for the next one. I have to admit I am very excited to continue delivering this type of content further, so I am already working on figuring all these things out. Thanks to everyone who joined. I really appreciate it.</p>
<p>During the regular stream on Thursday, I’ve <strong>finished working on the mermaid integration</strong> for the blog. It was a short one as I had plans for the late night. The blog post on mermaid integration is already in the progress.</p>
<h2 id="picks">
Picks</h2>
<p>During the vacation time, I’ve stumbled upon a <a href="https://www.smashingmagazine.com/2022/02/thoughts-on-markdown/">great article about markdown by Knut Melvær</a> and its usefulness for programmers and content creators. It tackles the problem with markdown very well and made me think about how do I want to write my content. Currently, I am on the unfortunate end of writing content as it’s explained in the article. That’s why I enjoyed it so much!</p>
<p>The next one is a simpler tutorial on <a href="https://www.openfaas.com/blog/react-app/">Hosting a React App with OpenFaaS</a>. I am an advocate for open-source software and I am glad to see <a href="https://www.openfaas.com">OpenFaaS</a> being still developed and trying to be more accessible for typical use-cases.</p>
<p>Be ready for the next one, Thank you!</p>
]]></content:encoded></item><item><title>DevBreak #1 State of JS 2021 review with Lukáš Orgován</title><link>https://michalvanko.dev/blog/2022-03-15-devbreak-1-state-of-js-2021</link><description><![CDATA[<p>The first episode of the <a href="/broadcasts/tags/DevBreak">DevBreak talk show</a>.
I’ve invited my close friend and a tech lead <a href="https://www.linkedin.com/in/lukasorgovan/">Lukáš Orgován</a> to talk about the opinions on the results of the <a href="https://2021.stateofjs.com/en-us/">State of JS 2021 survey</a>.</p>
]]></description><guid>https://michalvanko.dev/blog/2022-03-15-devbreak-1-state-of-js-2021</guid><pubDate>Tue, 15 Mar 2022 20:22:21 +0000</pubDate><content:encoded><![CDATA[<p>The first episode of the <a href="/broadcasts/tags/DevBreak">DevBreak talk show</a>.
I’ve invited my close friend and a tech lead <a href="https://www.linkedin.com/in/lukasorgovan/">Lukáš Orgován</a> to talk about the opinions on the results of the <a href="https://2021.stateofjs.com/en-us/">State of JS 2021 survey</a>.</p>
<p>This episode aired on the 15th of March 2022 and is in the Slovak language.</p>
<div class="video-embed">
<iframe src="https://player.twitch.tv/?video=1427225407&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
</div>]]></content:encoded></item><item><title>Second attempt @ Weekly #08-2022</title><link>https://michalvanko.dev/blog/2022-02-28-second-attempt-weekly-08-2022</link><description><![CDATA[<p>When I started to write this weekly I’ve got so deeply into writing about the error handling use case that I basically just dedicated the whole article to it. So this will be my second attempt just to not break the tradition.
You can check out the new <a href="/blog/2022-02-28-error-handling-with-either-type">article on error handling here</a>. It is basically a practical continuation of the <a href="https://michalvanko.dev/blog/2020-12-09-guide-on-error-handling">first guide for error handling</a> I’ve written a year ago.</p>
]]></description><guid>https://michalvanko.dev/blog/2022-02-28-second-attempt-weekly-08-2022</guid><pubDate>Mon, 28 Feb 2022 11:49:53 +0000</pubDate><content:encoded><![CDATA[<p>When I started to write this weekly I’ve got so deeply into writing about the error handling use case that I basically just dedicated the whole article to it. So this will be my second attempt just to not break the tradition.</p>
<h2 id="error-handling-guide">
Error handling guide</h2>
<p>You can check out the new <a href="/blog/2022-02-28-error-handling-with-either-type">article on error handling here</a>. It is basically a practical continuation of the <a href="https://michalvanko.dev/blog/2020-12-09-guide-on-error-handling">first guide for error handling</a> I’ve written a year ago.</p>
<h2 id="stream-updates">
Stream updates</h2>
<p>This week we have been discussing the <a href="https://2021.stateofjs.com/en-us/">state of JS</a> on the Tuesday stream. It went very well and I didn’t even manage to get through it to the end. I am planning to continue on the topic when I get back from vacation on the 15th of March.
I will prepare a few advertising materials (probably just a poster) and share them across social media as I want to run the stream officially.</p>
<h2 id="mermaid-diagram-parsing">
Mermaid diagram parsing</h2>
<p>On the Thursday stream, I’ve not been able to find a guest to discuss any topics so I just went for coding the diagram support for mermaid diagrams in my posts. I wanted to make render diagrams on the backend.
I was very surprised that the mermaid is directly dependent on the browser to be able to render SVG. I had to rethink all over again how I would implement it.</p>
<p>I haven’t finished it, yet. So <strong>prepare for a new article</strong> where I will present the solution. I will stream it on <a href="https://www.twitch.tv/michalvankodev">my twitch</a> so if you’re interested, <strong>please follow me there</strong>.</p>
<p>Let’s keep it short. Thank you!</p>
]]></content:encoded></item><item><title>Error handling with Either&lt;Type&gt;</title><link>https://michalvanko.dev/blog/2022-02-28-error-handling-with-either-type</link><description><![CDATA[<p>We have started a new small internal project for automating a few workflows around counting worked hours and time offs.
The <strong>application is a slack bot</strong> on top of node js, TypeScript and PostgreSQL database. We use 3rd party APIs to fetch data about the times which we need to accumulate and process to calculate valuable information to our users.</p>
]]></description><guid>https://michalvanko.dev/blog/2022-02-28-error-handling-with-either-type</guid><pubDate>Mon, 28 Feb 2022 11:30:54 +0000</pubDate><content:encoded><![CDATA[<p>We have started a new small internal project for automating a few workflows around counting worked hours and time offs.</p>
<h2 id="application-architecture">
Application architecture</h2>
<p>The <strong>application is a slack bot</strong> on top of node js, TypeScript and PostgreSQL database. We use 3rd party APIs to fetch data about the times which we need to accumulate and process to calculate valuable information to our users.</p>
<p>It will run on a server with a possibility of migrating into serverless when we decide if it’s a good use case for it. We’ve decided that we won’t experiment too much for development as we want to make it useful first.</p>
<h2 id="api-design">
API design</h2>
<p>As it is not a classic web server application I had to come up with slightly different error handling as we are used to. I’ve been trying to find a semi-functional API with all the good practices described in my <a href="/blog/2020-12-09-guide-on-error-handling">guide on error handling</a>. The main goal is to not let users be presented with internal information about errors. We want to show user-friendly messages instead.
I call this API semi-functional as <strong>I didn’t want to use monads</strong> and go 100% functional. We use simple asynchronous functions to handle interactions.
The goal is to handle errors that are expected. Unexpected errors should still be thrown and caught by an <em>“Error boundary”</em> around the whole app that will handle and log the error.</p>
<h2 id="error-types">
Error types</h2>
<p>Let’s create 2 types of <code>Error</code>s that all the other <code>Error</code>s can be extended from.
These will allow us to distinguish if the thrown <code>Error</code> should be presented to the user. We want to handle <code>InternalError</code>s differently. We might log them to different logs or trigger alarms before we convert them to a different <code>PublishableError</code>.</p>
<pre><code class="language-typescript"><pre style="background-color:#2b303b;">
<span style="color:#b48ead;">export </span><span style="color:#bf616a;">class PublishableError extends </span><span style="color:#ebcb8b;">Error </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">publishable </span><span style="color:#c0c5ce;">= </span><span style="color:#d08770;">true
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">constructor</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">message</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">string</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">    </span><span style="color:#96b5b4;">super</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">message</span><span style="color:#c0c5ce;">)
</span><span style="color:#c0c5ce;">    </span><span style="color:#ebcb8b;">Object</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">setPrototypeOf</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">this</span><span style="color:#c0c5ce;">, </span><span style="color:#ebcb8b;">PublishableError</span><span style="color:#c0c5ce;">.prototype)
</span><span style="color:#c0c5ce;">  }
</span><span style="color:#c0c5ce;">}
</span><span style="color:#c0c5ce;">
</span><span style="color:#b48ead;">export </span><span style="color:#bf616a;">class InternalError extends </span><span style="color:#ebcb8b;">Error </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">publishable </span><span style="color:#c0c5ce;">= </span><span style="color:#d08770;">false
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">constructor</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">message</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">string</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">    </span><span style="color:#96b5b4;">super</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">message</span><span style="color:#c0c5ce;">)
</span><span style="color:#c0c5ce;">    </span><span style="color:#ebcb8b;">Object</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">setPrototypeOf</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">this</span><span style="color:#c0c5ce;">, </span><span style="color:#ebcb8b;">InternalError</span><span style="color:#c0c5ce;">.prototype)
</span><span style="color:#c0c5ce;">  }
</span><span style="color:#c0c5ce;">}
</span></pre>
</code></pre>
<p>Then, we can create multiple app-specific errors by extending from these two.</p>
<blockquote>
<p>We need to set <code>Object.setPrototypeOf(...)</code> as TypeScript <a href="https://github.com/Microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work">introduced a breaking change</a> that may cause the inheritance to now work properly.</p>
</blockquote>
<h2 id="handling-errors">
Handling errors</h2>
<p>I wanted to have a similar style of handling the error as the <a href="https://ramdajs.com/docs/#tryCatch">Ramda’s <code>tryCatch</code></a> function. I couldn’t just use Ramda’s <code>tryCatch</code> as it doesn’t support asynchronous functions. I’ve found inspiration in the <a href="https://gcanti.github.io/fp-ts/modules/TaskEither.ts.html"><code>fp-ts</code> <code>TaskEither</code> type</a>.</p>
<p>I’ve come up with the following solution:</p>
<pre><code class="language-typescript"><pre style="background-color:#2b303b;">
<span style="color:#65737e;">/**
</span><span style="color:#65737e;"> * Tuple of error and a result of an asynchronous task that might throw an error
</span><span style="color:#65737e;"> */
</span><span style="color:#b48ead;">export </span><span style="color:#bf616a;">type Either</span><span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">ResultType</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">ErrorType extends </span><span style="color:#ebcb8b;">Error</span><span style="color:#c0c5ce;">&gt; = </span><span style="color:#ebcb8b;">Promise</span><span style="color:#c0c5ce;">&lt;
</span><span style="color:#c0c5ce;">  [</span><span style="color:#bf616a;">error</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">null</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">result</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">ResultType</span><span style="color:#c0c5ce;">] | [</span><span style="color:#bf616a;">error</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">ErrorType</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">result</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">undefined</span><span style="color:#c0c5ce;">]
</span><span style="color:#c0c5ce;">&gt;
</span><span style="color:#c0c5ce;">
</span><span style="color:#65737e;">/**
</span><span style="color:#65737e;"> * Try to execute an async function and return a tuple of `[Error, ResultType]`
</span><span style="color:#65737e;"> * If the operation is successful error will be `null`
</span><span style="color:#65737e;"> * If the operation fails, the `errorHandler` runs with the exception that was thrown, Result is `undefined`
</span><span style="color:#65737e;"> * It catches all errors that might happen in the passed async task
</span><span style="color:#65737e;"> * It will not catch any errors that are thrown from the `errorHander`
</span><span style="color:#65737e;"> */
</span><span style="color:#bf616a;">export </span><span style="color:#b48ead;">function </span><span style="color:#8fa1b3;">wrapWithErrorHandler</span><span style="color:#c0c5ce;">&lt;
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">ErrorType extends </span><span style="color:#ebcb8b;">Error</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">  </span><span style="color:#65737e;">// TS is still able to inherit function parameters
</span><span style="color:#c0c5ce;">  </span><span style="color:#65737e;">// eslint-disable-next-line @typescript-eslint/no-explicit-any
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">FunctionArgs extends </span><span style="color:#ebcb8b;">Array</span><span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">any</span><span style="color:#c0c5ce;">&gt;,
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">ResultType
</span><span style="color:#c0c5ce;">&gt;(
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">errorHandler</span><span style="color:#c0c5ce;">: (</span><span style="color:#bf616a;">error</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">ErrorType </span><span style="color:#c0c5ce;">| </span><span style="color:#bf616a;">unknown</span><span style="color:#c0c5ce;">) </span><span style="color:#b48ead;">=&gt; </span><span style="color:#bf616a;">ErrorType</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">fn</span><span style="color:#c0c5ce;">: (...</span><span style="color:#bf616a;">args</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">FunctionArgs</span><span style="color:#c0c5ce;">) </span><span style="color:#b48ead;">=&gt; </span><span style="color:#ebcb8b;">Promise</span><span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">ResultType</span><span style="color:#c0c5ce;">&gt;
</span><span style="background-color:#bf616a;color:#2b303b;">)</span><span style="color:#c0c5ce;">: (...</span><span style="color:#bf616a;">args</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">FunctionArgs</span><span style="color:#c0c5ce;">) </span><span style="color:#b48ead;">=&gt; </span><span style="color:#bf616a;">Either</span><span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">ResultType</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">ErrorType</span><span style="color:#c0c5ce;">&gt; {
</span><span style="color:#c0c5ce;">  return </span><span style="color:#8fa1b3;">async </span><span style="color:#c0c5ce;">(...args: FunctionArgs) =&gt; {
</span><span style="color:#c0c5ce;">    try {
</span><span style="color:#c0c5ce;">      const result = await </span><span style="color:#8fa1b3;">fn</span><span style="color:#c0c5ce;">(...args)
</span><span style="color:#c0c5ce;">      return [</span><span style="color:#d08770;">null</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">result</span><span style="color:#c0c5ce;">]
</span><span style="color:#c0c5ce;">    } </span><span style="color:#8fa1b3;">catch </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">exception</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">      const errorHandlerResult = </span><span style="color:#8fa1b3;">errorHandler</span><span style="color:#c0c5ce;">(exception)
</span><span style="color:#c0c5ce;">      return [</span><span style="color:#bf616a;">errorHandlerResult</span><span style="color:#c0c5ce;">, </span><span style="color:#d08770;">undefined</span><span style="color:#c0c5ce;">]
</span><span style="color:#c0c5ce;">    }
</span><span style="color:#c0c5ce;">  }
</span><span style="color:#c0c5ce;">}
</span></pre>
</code></pre>
<p>I struggled with the typings for a few minutes but I was impressed with the result.
The goal of this function is to wrap an asynchronous function that might throw errors and let the <code>errorHandler</code> process the error and return an alternative result.</p>
<p>As most of the errors will be possibly handled by the same <code>errorHandle</code> we can create a <code>genericErrorHandler</code> that might be used in most situations.</p>
<pre><code class="language-typescript"><pre style="background-color:#2b303b;">
<span style="color:#65737e;">/**
</span><span style="color:#65737e;"> * Generic Error handler that should be used in most common use cases
</span><span style="color:#65737e;"> * It will throw an error if any unexpected error is thrown
</span><span style="color:#65737e;"> * When used with `wrapWithErrorHandler` it is able to catch most of the expected errors
</span><span style="color:#65737e;"> */
</span><span style="color:#b48ead;">export </span><span style="color:#bf616a;">function genericErrorHandler</span><span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">ErrorType extends </span><span style="color:#ebcb8b;">Error</span><span style="color:#c0c5ce;">&gt;(
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">exception</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">ErrorType </span><span style="color:#c0c5ce;">| </span><span style="color:#bf616a;">unknown
</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">if </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">exception </span><span style="color:#c0c5ce;">instanceof </span><span style="color:#bf616a;">PublishableError</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">    </span><span style="color:#65737e;">// These errors are usually handled no need to log them
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">exception
</span><span style="color:#c0c5ce;">  }
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">if </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">exception </span><span style="color:#c0c5ce;">instanceof </span><span style="color:#bf616a;">InternalError</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">    </span><span style="color:#65737e;">// Log error and return publishable generic error
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">return </span><span style="color:#c0c5ce;">new </span><span style="color:#bf616a;">PublishableError</span><span style="color:#c0c5ce;">(
</span><span style="color:#c0c5ce;">      &quot;</span><span style="color:#a3be8c;">I&#39;m sorry for the inconvenience, but my circuits have been overloaded :zap:</span><span style="color:#c0c5ce;">&quot;
</span><span style="color:#c0c5ce;">    )
</span><span style="color:#c0c5ce;">  }
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">if </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">exception </span><span style="color:#c0c5ce;">instanceof </span><span style="color:#ebcb8b;">Error</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">    </span><span style="color:#65737e;">// This is unexpected error, better log it and let rethrow it further away
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">throw </span><span style="color:#bf616a;">exception
</span><span style="color:#c0c5ce;">  }
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">exception as ErrorType
</span><span style="color:#c0c5ce;">}
</span></pre>
</code></pre>
<p>To spare some characters of redundant code I’ve applied a partial application principle to use these functions together. At first, I’ve tried to use <a href="https://ramdajs.com/docs/#partial">Ramda’s <code>partial</code></a>, but it was not able to inherit correct types, so I’ve just written the partially applied function myself:</p>
<pre><code class="language-typescript"><pre style="background-color:#2b303b;">
<span style="color:#65737e;">/**
</span><span style="color:#65737e;"> * Helper function with applied `genericErrorHandler` to `wrapWithErrorHandler`
</span><span style="color:#65737e;"> */
</span><span style="color:#b48ead;">export </span><span style="color:#bf616a;">function wrapWithGenericErrorHandlerFunction</span><span style="color:#c0c5ce;">&lt;
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">FunctionArgs extends </span><span style="color:#ebcb8b;">Array</span><span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">any</span><span style="color:#c0c5ce;">&gt;,
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">ResultType
</span><span style="color:#c0c5ce;">&gt;(</span><span style="color:#bf616a;">fn</span><span style="color:#c0c5ce;">: (...</span><span style="color:#bf616a;">args</span><span style="color:#c0c5ce;">: </span><span style="color:#bf616a;">FunctionArgs</span><span style="color:#c0c5ce;">) </span><span style="color:#b48ead;">=&gt; </span><span style="color:#ebcb8b;">Promise</span><span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">ResultType</span><span style="color:#c0c5ce;">&gt;</span><span style="background-color:#bf616a;color:#2b303b;">)</span><span style="color:#c0c5ce;"> {
</span><span style="color:#c0c5ce;">  return </span><span style="color:#8fa1b3;">wrapWithErrorHandler</span><span style="color:#c0c5ce;">(genericErrorHandler, fn)
</span><span style="color:#c0c5ce;">}
</span></pre>
</code></pre>
<h2 id="usage">
Usage</h2>
<p>To sum it up, I’d like to present you the way how this API is used:</p>
<pre><code class="language-typescript"><pre style="background-color:#2b303b;">
<span style="color:#b48ead;">const </span><span style="color:#c0c5ce;">[</span><span style="color:#bf616a;">error</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">memberId</span><span style="color:#c0c5ce;">] = </span><span style="color:#bf616a;">await </span><span style="color:#8fa1b3;">wrapWithGenericErrorHandlerFunction</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">getUserId</span><span style="color:#c0c5ce;">)(
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">user</span><span style="color:#c0c5ce;">.token
</span><span style="color:#c0c5ce;">)
</span><span style="color:#b48ead;">if </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">error</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">await </span><span style="color:#8fa1b3;">respond</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">error</span><span style="color:#c0c5ce;">.message)
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">return
</span><span style="color:#c0c5ce;">}
</span></pre>
</code></pre>
<p>This API allows me to keep the separation of concerns between the tasks and error handling into two separate functions. It also allows us to use the <code>wrapWithErrorHandler</code> with different <code>errorHandler</code> when we need to take special care. Also, I am still able to throw errors from the <code>errorHandler</code> that might be caught by a different <code>errorHandler</code> from an upper scope.</p>
<p>Thank you for reading!</p>
]]></content:encoded></item><item><title>Laptop battery got fat - Weekly #07-2022</title><link>https://michalvanko.dev/blog/2022-02-20-weekly-07-2022</link><description><![CDATA[<p>Another week, another update is here.
This week, I have been able to commit to a schedule for my streams. I will keep this schedule onwards. I’ll be <strong>online on Tuesday and Thursday</strong>. The <strong>stream starts somewhen between 18:00 to 20:00 CET</strong>.</p>
]]></description><enclosure url="/images/uploads/laptopbattery.jpg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2022-02-20-weekly-07-2022</guid><pubDate>Sun, 20 Feb 2022 17:50:56 +0000</pubDate><content:encoded><![CDATA[<p>Another week, another update is here.</p>
<h2 id="stream-updates">
Stream updates</h2>
<p>This week, I have been able to commit to a schedule for my streams. I will keep this schedule onwards. I’ll be <strong>online on Tuesday and Thursday</strong>. The <strong>stream starts somewhen between 18:00 to 20:00 CET</strong>.
Currently, I am just testing the streaming setup and trying to adapt to the commitment. I plan on doing 2 more of these testing streams next week.</p>
<p>I will be going on vacation on the 2nd of March for a week so I will take a small break. After that, I would like to <strong>start promoting my stream</strong> on social networks. I’d like to bring a first official <strong>guest for a talk/interview</strong> and present the stream to a wider audience.</p>
<h2 id="laptop-battery">
Laptop battery</h2>
<p>My laptop lid was not closing properly, recently. I’ve noticed that there is a significant bump in an area of my keyboard that prevents the lid to be closed.
I’ve assumed that it might be caused by the battery. I’ve opened my laptop and removed the battery as it was much bigger than it is supposed to be.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/laptopbattery_1192x860.jpg 1x, /generated_images/images/uploads/laptopbattery_1788x1290.jpg 1.5x, /generated_images/images/uploads/laptopbattery_2384x1720.jpg 2x, /generated_images/images/uploads/laptopbattery_3576x2580.jpg 3x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/laptopbattery_1192x860.jpg"
    width="1192"
    height="860"
    alt="Worn out laptop battery"
    
>
</picture>
<figcaption>
Worn out laptop battery</figcaption></figure></p>
<p>Right now, I am operating without a battery. It feels weird because I have to always keep in mind that I have to <strong>turn off the laptop</strong> every time I want to move it to a different place. I’ve already made an order for a new battery, but unfortunately, I have to wait till Monday for the delivery.</p>
<h2 id="vim">
Vim</h2>
<p>I’ve received a small gift from my friend and colleague. He visited us at the office and gave me a sticker. But not just any sticker. He got me a <em>Vim</em> sticker. As a proud Vim user, I’ve immediately put it on my laptop.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20220220_191347_1147x860.jpg 1x, /generated_images/images/uploads/20220220_191347_1720x1290.jpg 1.5x, /generated_images/images/uploads/20220220_191347_2294x1720.jpg 2x, /generated_images/images/uploads/20220220_191347_3441x2580.jpg 3x, /generated_images/images/uploads/20220220_191347_4032x3023.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20220220_191347_1147x860.jpg"
    width="1147"
    height="860"
    alt="My laptop stickers"
    
>
</picture>
<figcaption>
Laptop cover</figcaption></figure></p>
<p>I have to also mention, that I’ve changed my theme to light <a href="https://github.com/overcache/NeoSolarized">NeoSolarized</a> with high contrast. since last week. It feels much better not having a bright background for reading/writing code.</p>
<h2 id="picks-for-this-week">
Picks for this week</h2>
<p>Github is now able to parse <a href="https://github.com/mermaid-js/mermaid#readme">mermaid diagrams</a>. You can <a href="https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/">check out the announcement</a>. It looks like I could be able to do that on my blog as well. I will put this into my to-do list and perhaps code it sometime during my stream ;)</p>
<p><a href="https://2021.stateofjs.com/en-US/">State of JS</a> results for 2021 has been presented this week. I am very excited about the satisfaction rate of <a href="https://github.com/sveltejs/kit">@svelte/kit</a>.</p>
]]></content:encoded></item><item><title>I&apos;ve started streaming - Weekly #06-2022 </title><link>https://michalvanko.dev/blog/2022-02-13-ive-started-streaming-weekly-06-2022</link><description><![CDATA[<p>I’ve been thinking of giving the <em>Weekly</em> series a <strong>numbering system</strong>. I’ve come up with the format of <code>#weekNumber-year</code>. This article is only a second <em>Weekly</em>, but it is published for the sixth week of this year, so it will receive a number <code>#06-2022</code>.
I use my <em>Samsung Galaxy S21</em> as a camera via <em>DroidCam</em>. I have even bought the <em>DroidCamX</em>, just because it is the only software that successfully allowed me to <strong>connect my phone camera with the PC.</strong></p>
]]></description><guid>https://michalvanko.dev/blog/2022-02-13-ive-started-streaming-weekly-06-2022</guid><pubDate>Sun, 13 Feb 2022 19:39:33 +0000</pubDate><content:encoded><![CDATA[<p>I’ve been thinking of giving the <em>Weekly</em> series a <strong>numbering system</strong>. I’ve come up with the format of <code>#weekNumber-year</code>. This article is only a second <em>Weekly</em>, but it is published for the sixth week of this year, so it will receive a number <code>#06-2022</code>.</p>
<h2 id="streaming-setup">
Streaming setup</h2>
<p>I use my <em>Samsung Galaxy S21</em> as a camera via <em>DroidCam</em>. I have even bought the <em>DroidCamX</em>, just because it is the only software that successfully allowed me to <strong>connect my phone camera with the PC.</strong></p>
<p>I needed to improve my camera angle. I was surprised by how quickly I was able to resolve this. We went shopping with my fiance and I found a wireless selfie stick that can be put into stand mode at the cash registers. I’ve immediately added it into the basket. It serves as a camera holder.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/20220212_120400_645x860.jpg 1x, /generated_images/images/uploads/20220212_120400_967x1290.jpg 1.5x, /generated_images/images/uploads/20220212_120400_1290x1720.jpg 2x, /generated_images/images/uploads/20220212_120400_1935x2580.jpg 3x, /generated_images/images/uploads/20220212_120400_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/20220212_120400_645x860.jpg"
    width="645"
    height="860"
    alt="The packaging box of the wireless selfie stick"
    
>
</picture>
<figcaption>
Wireless selfie stick packaging</figcaption></figure></p>
<p>I was thinking about how I could leverage scenes in <em>OBS</em> to make the streaming settings more maintainable. A thought hit me that I can <strong>compose scenes</strong> into each other just like objects in programming. I couldn’t be the first to think of it, so I’ve tried to find somewhere on the internet a tutorial on how others manage it. I’ve found a great <a href="https://www.youtube.com/watch?v=CvzAoqXtREw">tutorial on <em>YouTube</em> by <em>Gaming Careers</em></a>, where he gives some tips on how to manage and compose scenes. I’ve created some scenes to be able to start streaming.</p>
<p>I asked one of my colleagues for a favor to <strong>encourage me to start to stream</strong>. We went on a call and I’ve started to stream. We were just talking about basic stuff around programming, but I’ve really enjoyed it.
I left a note for myself that this post is being written during the first broadcast.</p>
<blockquote>
<p>I am streaming right now as I am writing this post!</p>
</blockquote>
<p>Broadcasting is very demanding for resources so to make better quality content I will eventually have to <strong>buy a dedicated computer</strong>.
I’ve set a new <strong>sub goal</strong> for buying a streaming PC. I’ll buy it when the stream will <strong>reach 50 subs</strong>. Wish me luck!</p>
<h2 id="picks-for-this-week">
Picks for this week</h2>
<p>Last week I’ve been showing off new headphones that I’ve ordered and the <em>YouTube</em> algorithm has been suggesting great videos on the topic since. I will give another shoutout. This time it is to <a href="https://www.youtube.com/channel/UCx9bOYEjkevIDYONBAstK-A">Joshua’s Valour <em>YouTube</em> channel</a></p>
<div class="video-embed">
<iframe class="embed" width="100%" height="100%" src="https://www.youtube.com/embed/hoLMdrD5pic" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p>My next pick will be a <a href="https://open.spotify.com/show/3PkkSdQE8DfeiKvSk1Mg1J">podcast <em>Lifespan</em> hosted by Dr. David Sinclair</a> where he talks about what we can do and how should we treat our bodies to live a longer and healthier life.</p>
<p>In our management course, we’ve been learning about different <strong>person typologies</strong>. That helped me to understand how I should treat other people and how to communicate better with them.</p>
<p>That’s it for this week. Thank you very much!</p>
]]></content:encoded></item><item><title>First Weekly</title><link>https://michalvanko.dev/blog/2022-02-06-first-weekly</link><description><![CDATA[<p>Hey dear reader,
I’d like to resurrect a series of articles called <strong>Weeklys</strong>. It’s a weekly round-down of <strong>things I’ve learned, discovered, or caught my attention</strong>.</p>
]]></description><guid>https://michalvanko.dev/blog/2022-02-06-first-weekly</guid><pubDate>Sun, 6 Feb 2022 17:13:02 +0000</pubDate><content:encoded><![CDATA[<p>Hey dear reader,</p>
<p>I’d like to resurrect a series of articles called <strong>Weeklys</strong>. It’s a weekly round-down of <strong>things I’ve learned, discovered, or caught my attention</strong>.
I came up with this idea a few years ago when I’ve started blogging. I think it’s a good idea to <strong>keep up with writing</strong> on regular basis.</p>
<h2 id="headphone-upgrade">
Headphone upgrade</h2>
<p>I’ve been searching for new headphones for a few months.
My current <a href="https://www.audio-technica.com/en-us/ath-sr50bt">ATH-SR50BT from audio-technica</a> headphones started to peel their skin off and they also have their headband holder broken. I’ve tried to find replacement parts but I’ve been already redirected 2 times to a different support company and the vision of repairing them started to fade away. I’ve been looking around the internet to find a new pair and I would like to shout out some of the few good resources I’ve stumbled on.
I recommend checking out <a href="https://www.youtube.com/c/DMS3TV">DMS YouTube channel</a> for professional and interesting opinions on the performance of the most popular headphones out there.
I’ve also found an online magazine <a href="https://www.headphonecheck.com/">headphonecheck.com</a>. It features reviews and rankings of various types of headphones.
In the end, I was searching for sustainable studio overhead open-backed headphones and I managed to order <a href="https://europe.beyerdynamic.com/dt-900-pro-x.html">bayerdynamics new DT 900 Pro X</a>.</p>
<p><img
  alt="Preview of the beyerdynamic DT 900 Pro X headphones"
  src="https://d33p2k2w4zpozf.cloudfront.net/media/catalog/product/cache/4832662449f27945c40903385bb67a42/b/e/beyerdynamic-dt-900-pro-x-perspective.jpeg"
/>Preview of the beyerdynamic DT 900 Pro X headphones</figcaption></figure></p>
<p>I can’t wait till they arrive. I got an unpleasant estimate of the arrival of 31th of May in my order confirmation mail. So I guess I will forget that I’ve ordered them and made myself a nice birthday surprise.</p>
<h2 id="stream-setup">
Stream setup</h2>
<p>I always wanted to use my mobile phone camera as the main camera for the stream. Mainly because of the quality of the camera and the ability to have much better visuals. It would not look good on the stream to have a full-screen scene of just my notebook camera.
I tried so many apps to accomplish this but finally, I’ve managed to get it working
I was hoping that I can make the <a href="https://www.dev47apps.com/obs/">DroidCam OBS</a> combination of OBS plugin and Android application to work but for some unknown reason the OBS plugin couldn’t find the camera through a USB connection.
Instead, I’ve managed to connect through the plain <a href="https://www.dev47apps.com/">DroidCam</a> application. I’ve had to install a few missing libraries for the video capture and got it working. I’m really pumped up about this and started to plan out first testing streams and then planning on making something more official. Something like a talk show with some guests and just casual streams around coding and lifestyle.</p>
<h2 id="weeklys">
Weeklys</h2>
<p>I think that around the New year I wanted to start writing more often and bring back this idea of writing <strong>weekly updates</strong>. I must have forgotten about that because a whole month just went by and I haven’t written a single thing yet. But, I am happy that I made myself write this. I certainly want to write much more and <strong>create more content</strong>. In this series, I’d also like to post articles that caught my eye, but I don’t think that this week I’ve read any interesting ones. We’ve been introduced to <a href="https://www.youtube.com/watch?v=VZF-G7MCSG4">the 7 delegations level</a> for a <a href="https://management30.com/practice/delegation-poker/">delegation poker workshop</a> that we did with management at work to align with each other. I hope it brings a valuable outcome. We yet have to see.</p>
<p>Ok, Thank you very much for reading and see you next week!</p>
]]></content:encoded></item><item><title>How we handle the rapid growth of the project team</title><link>https://michalvanko.dev/blog/2021-12-16-how-we-handle-the-rapid-growth-of-the-project-team</link><description><![CDATA[<p>I’ve been interviewed by my friend <a href="https://www.instagram.com/zubkka/">@zubkka</a> about my current position at <em>The Expert</em> team as an Engineering Manager. We have talked about my journey at <a href="https://sudolabs.com">sudolabs</a> but also about my early years and how I’ve got into programming. Please, <a href="https://sudolabs.com/blog/how-we-handle-the-rapid-growth-of-the-project-team">go check out the interview</a> and let me know what you think. Happy reading!</p>
]]></description><guid>https://michalvanko.dev/blog/2021-12-16-how-we-handle-the-rapid-growth-of-the-project-team</guid><pubDate>Thu, 16 Dec 2021 11:02:10 +0000</pubDate><content:encoded><![CDATA[<p>I’ve been interviewed by my friend <a href="https://www.instagram.com/zubkka/">@zubkka</a> about my current position at <em>The Expert</em> team as an Engineering Manager. We have talked about my journey at <a href="https://sudolabs.com">sudolabs</a> but also about my early years and how I’ve got into programming. Please, <a href="https://sudolabs.com/blog/how-we-handle-the-rapid-growth-of-the-project-team">go check out the interview</a> and let me know what you think. Happy reading!</p>
]]></content:encoded></item><item><title>Introduction to JavaScript Application testing</title><link>https://michalvanko.dev/blog/2021-05-07-introduction-javascript-to-javascript-application-testing</link><description><![CDATA[<p>An automated testing suite for your application is like a feature requirements document.
When this suite is passing, it means that the features are delivered correctly as described in the requirements.</p>
]]></description><guid>https://michalvanko.dev/blog/2021-05-07-introduction-javascript-to-javascript-application-testing</guid><pubDate>Fri, 7 May 2021 14:44:57 +0000</pubDate><content:encoded><![CDATA[<p>An automated testing suite for your application is like a feature requirements document.
When this suite is passing, it means that the features are delivered correctly as described in the requirements.
Automated tests help reduce manual testing time and as long as they are passing, it means that the feature is working as described.
Not only do they reduce costs and time of manual testing but they also can help while development.</p>
<p>Different language ecosystems offer a different set of tools to develop automated tests.
In this article, I am going to describe how various types of tests can be utilized to cover the most common testing scenarios for Web/Mobile applications development with 3rd party integrations.</p>
<ul>
<li><a href="#unit-tests">Unit tests</a>
<ul>
<li><a href="#tools-that-help-writing-unit-tests">Tools that help writing unit tests</a></li>
<li><a href="#additional-resources-for-unit-tests">Additional resources for unit tests</a></li>
</ul>
</li>
<li><a href="#snapshots">Snapshots</a>
<ul>
<li><a href="#additional-resources-on-snapshots">Additional resources on snapshots</a></li>
</ul>
</li>
<li><a href="#integration-testing">Integration testing</a>
<ul>
<li><a href="#tools-that-help-writing-integration-tests">Tools that help writing integration tests</a></li>
<li><a href="#additional-resources-for-integration-tests">Additional resources for integration tests</a></li>
</ul>
</li>
<li><a href="#end-to-end-e2e-testing">End to End (e2e) testing</a>
<ul>
<li><a href="#element-selectors">Element selectors</a></li>
<li><a href="#form-validation">Form validation</a></li>
<li><a href="#authentication">Authentication</a></li>
<li><a href="#dedicated-api-routes">Dedicated API routes</a></li>
<li><a href="#screenshot-matching">Screenshot matching</a></li>
<li><a href="#tools-that-help-writing-e2e-tests">Tools that help writing e2e tests</a></li>
<li><a href="#additional-resources-for-e2e-tests">Additional resources for e2e tests</a></li>
</ul>
</li>
<li><a href="#component-tests">Component tests</a>
<ul>
<li><a href="#tools-and-resources-for-component-testing">Tools and resources for component testing</a></li>
</ul>
</li>
<li><a href="#code-coverage">Code coverage</a>
<ul>
<li><a href="#additional-resources-for-code-coverage">Additional resources for code coverage</a></li>
</ul>
</li>
</ul>
<h2 id="unit-tests">
Unit tests</h2>
<p>Unit tests are functions that test a single specific part of the application in small units.
<em>Function</em> can be interpreted as a logical unit.
Unit tests verify if this <em>function</em> has correct behavior with different arguments or its dependencies.
A test consists of a description of the requirement that <strong>should</strong> be satisfied.</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#8fa1b3;">describe</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">Location formatter</span><span style="color:#c0c5ce;">&#39;, () </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">it</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">should format location based on a passed primaryLocation</span><span style="color:#c0c5ce;">&#39;, () </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">formattedLocation </span><span style="color:#c0c5ce;">= </span><span style="color:#8fa1b3;">formatLocation</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">      primaryLocation: &#39;</span><span style="color:#a3be8c;">Pennsylvania</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">      city: </span><span style="color:#d08770;">null</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">      countryIsoCode: &#39;</span><span style="color:#a3be8c;">US</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">    })
</span><span style="color:#c0c5ce;">    </span><span style="color:#8fa1b3;">expect</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">formattedLocation</span><span style="color:#c0c5ce;">).</span><span style="color:#8fa1b3;">toBe</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">Pennsylvania, US</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">  })
</span><span style="color:#c0c5ce;">  
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">it</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">should format location based on a passed city</span><span style="color:#c0c5ce;">&#39;, () </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">formattedLocation </span><span style="color:#c0c5ce;">= </span><span style="color:#8fa1b3;">formatLocation</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">      primaryLocation: </span><span style="color:#d08770;">null</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">      city: &#39;</span><span style="color:#a3be8c;">Svidník</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">      countryIsoCode: &#39;</span><span style="color:#a3be8c;">SK</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">    })
</span><span style="color:#c0c5ce;">    </span><span style="color:#8fa1b3;">expect</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">formattedLocation</span><span style="color:#c0c5ce;">).</span><span style="color:#8fa1b3;">toBe</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">Svidník, SK</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">  })
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">it</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">should format location based on a passed primaryLocation with city being ignored</span><span style="color:#c0c5ce;">&#39;, () </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">formattedLocation </span><span style="color:#c0c5ce;">= </span><span style="color:#8fa1b3;">formatLocation</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">      primaryLocation: &#39;</span><span style="color:#a3be8c;">Pennsylvania</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">      city: &#39;</span><span style="color:#a3be8c;">Košice</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">      countryIsoCode: &#39;</span><span style="color:#a3be8c;">US</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">    })
</span><span style="color:#c0c5ce;">    </span><span style="color:#8fa1b3;">expect</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">formattedLocation</span><span style="color:#c0c5ce;">).</span><span style="color:#8fa1b3;">toBe</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">Pennsylvania, US</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">  })
</span><span style="color:#c0c5ce;">})
</span></pre>
</code></pre>
<p>These tests can run after any code change and they don’t affect other resources other than <em>CPU</em>.
They are also good for practicing <em>test-driven development</em> as they are fast to execute and give immediate feedback to the developer.</p>
<p>If the tested unit is dependent on some external resource like <em>time</em>, browser setting, API response, or some service,<em>mocking</em> can be used to substitute the resource to the unit.
<em>Mocking</em> is especially useful with a technique called <a href="https://en.wikipedia.org/wiki/Dependency_injection"><em>dependency injection</em></a></p>
<p>The effect of the unit can be then tested on these <em>mocks</em>:</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#b48ead;">const </span><span style="color:#bf616a;">service </span><span style="color:#c0c5ce;">= {
</span><span style="color:#c0c5ce;">  fetch: </span><span style="color:#bf616a;">jest</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">fn</span><span style="color:#c0c5ce;">(() </span><span style="color:#b48ead;">=&gt; </span><span style="color:#bf616a;">responseFixture</span><span style="color:#c0c5ce;">)
</span><span style="color:#c0c5ce;">}
</span><span style="color:#c0c5ce;">
</span><span style="color:#65737e;">// Using dependency injection
</span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">result </span><span style="color:#c0c5ce;">= </span><span style="color:#8fa1b3;">fetchAndParseResponse</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">service</span><span style="color:#c0c5ce;">, &#39;</span><span style="color:#a3be8c;">path</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#65737e;">// -- snip expecting correct result
</span><span style="color:#65737e;">// It&#39;s expected that `fetchAndParseResponse` will call the `fetch` only once
</span><span style="color:#8fa1b3;">expect</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">service</span><span style="color:#c0c5ce;">.fetch).</span><span style="color:#8fa1b3;">toHaveBeenCalled</span><span style="color:#c0c5ce;">(</span><span style="color:#d08770;">1</span><span style="color:#c0c5ce;">)
</span></pre>
</code></pre>
<p>Unit tests can also make use of <a href="#snapshots">snapshots</a>.</p>
<h3 id="tools-that-help-writing-unit-tests">
Tools that help writing unit tests</h3>
<ul>
<li><a href="https://jestjs.io/">Jest testing framework</a></li>
<li><a href="https://mochajs.org/">mocha test runner</a> - Usually paired with assertion library of choice</li>
<li><a href="https://www.chaijs.com/">Chai assertion library</a></li>
<li><a href="https://github.com/hustcc/jest-date-mock">jest-date-mock</a> - Testing functionality that is dependent on the Date</li>
</ul>
<h3 id="additional-resources-for-unit-tests">
Additional resources for unit tests</h3>
<ul>
<li><a href="https://jestjs.io/docs/manual-mocks">Jest mocking guide</a></li>
<li><a href="https://www.freecodecamp.org/news/how-to-start-unit-testing-javascript/">How to Start Unit Testing Your JavaScript Code</a> by <a href="https://www.freecodecamp.org/news/author/ondrej/">Ondrej Polesny @ FreeCodeCamp</a></li>
<li><a href="https://medium.com/@gondy/the-importance-of-test-driven-development-f80b0d02edd8">The importance of test driven development</a> by <a href="https://medium.com/@gondy">Godswill Okwara @ medium.com</a></li>
</ul>
<h2 id="snapshots">
Snapshots</h2>
<p>Snapshot matchers will save the input object as an artifact and they will use that artifact to compare the snapshot with the snapshot of the next test run.</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#8fa1b3;">it</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">should create a JSON response</span><span style="color:#c0c5ce;">&#39;, () </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">feed </span><span style="color:#c0c5ce;">= </span><span style="color:#8fa1b3;">createFeedResponse</span><span style="color:#c0c5ce;">();
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">expect</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">JSON</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">parse</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">feed</span><span style="color:#c0c5ce;">)).</span><span style="color:#8fa1b3;">toMatchSnapshot</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">parsed feed</span><span style="color:#c0c5ce;">&#39;);
</span><span style="color:#c0c5ce;">});
</span></pre>
</code></pre>
<blockquote>
<p>On subsequent test runs, Jest will compare the output with the previous snapshot. If they match, the test will pass.
If they don’t match, either the test runner found a bug in your code that should be fixed, or the implementation has changed and the snapshot needs to be updated.</p>
</blockquote>
<p>These snapshots help to detect changes in the output of some functionality.
They can also be used with a component renderer to detect changes in the output of component’s <em>HTML</em>, <em>DOM</em> or virtual <em>DOM</em>.
Snapshots are created and updated by the test runner automatically so they lower the churn of updating tests over time.</p>
<p>Snapshot artifacts should be committed into the code repository alongside the tests, so they can be used as an input for the next test runs.
Snapshots can be utilized for many types of tests.</p>
<p>If the expected result includes some varying data,
<a href="https://jestjs.io/docs/snapshot-testing#property-matchers">property matchers</a> can be utilized to verify the type of the data instead.</p>
<h3 id="additional-resources-on-snapshots">
Additional resources on snapshots</h3>
<ul>
<li><a href="https://jestjs.io/docs/snapshot-testing">Jest snapshot testing</a></li>
</ul>
<h2 id="integration-testing">
Integration testing</h2>
<p>Integration tests are meant to validate the behavior of the individual components and their interaction with different system modules.</p>
<p>Take an API resolver as an example:</p>
<ol>
<li>Resolver is given different parameters</li>
<li>It fetches some data from a database</li>
<li>Makes an action given the data</li>
<li>Returns a response to the client</li>
</ol>
<p>An integration test should test and validate the functionality of this resolver.
It should simulate a client request and assert desired response output.
It should also verify the actions that the resolver is making.</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#65737e;">// Mock the e-mail service, we don&#39;t want to send real emails
</span><span style="color:#bf616a;">jest</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">mock</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">../email.js</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">
</span><span style="color:#8fa1b3;">it</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">should send activation email</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#b48ead;">async </span><span style="color:#c0c5ce;">() </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">client </span><span style="color:#c0c5ce;">= </span><span style="color:#bf616a;">await Client</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">query</span><span style="color:#c0c5ce;">().</span><span style="color:#8fa1b3;">findOne</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">    firstName: &#39;</span><span style="color:#a3be8c;">testing</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">    lastName: &#39;</span><span style="color:#a3be8c;">user</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">  })
</span><span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">sendActivationEmail </span><span style="color:#c0c5ce;">= </span><span style="color:#bf616a;">await apolloClient</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">mutate</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">    mutation: </span><span style="color:#bf616a;">SEND_ACTIVATION_EMAIL_MUTATION</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">    variables: { clientId: </span><span style="color:#bf616a;">client</span><span style="color:#c0c5ce;">.id },
</span><span style="color:#c0c5ce;">  })
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">expect</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">sendActivationEmail</span><span style="color:#c0c5ce;">.errors).not.</span><span style="color:#8fa1b3;">toBeDefined</span><span style="color:#c0c5ce;">()
</span><span style="color:#c0c5ce;">  </span><span style="color:#65737e;">// One email should be sent
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">expect</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">email</span><span style="color:#c0c5ce;">.send).</span><span style="color:#8fa1b3;">toHaveBeenCalledTimes</span><span style="color:#c0c5ce;">(</span><span style="color:#d08770;">1</span><span style="color:#c0c5ce;">)
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#65737e;">// Verify that the email was sent with correct parameters
</span><span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">expect</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">email</span><span style="color:#c0c5ce;">.send).</span><span style="color:#8fa1b3;">toHaveBeenCalledWith</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">    templateId: </span><span style="color:#ebcb8b;">EMAIL_TEMPLATE_IDS</span><span style="color:#c0c5ce;">.CLIENT_EMAIL_ACTIVATION,
</span><span style="color:#c0c5ce;">    to: </span><span style="color:#bf616a;">client</span><span style="color:#c0c5ce;">.contactEmail,
</span><span style="color:#c0c5ce;">    data: {
</span><span style="color:#c0c5ce;">      client_first_name: </span><span style="color:#bf616a;">client</span><span style="color:#c0c5ce;">.firstName,
</span><span style="color:#c0c5ce;">    },
</span><span style="color:#c0c5ce;">  })
</span><span style="color:#c0c5ce;">})
</span></pre>
</code></pre>
<p>It might be useful to <strong>mock</strong> some <strong>dependencies</strong> such as a database or a 3rd party API to ensure that these tests can run multiple times in sandboxed environment.</p>
<p>Sometimes it is desired to have a database, that is set up just for integration tests to avoid mocking.
It can be also used for validating integration in between the tests.
In this scenario, it is very useful to <em>seed</em> the database. The database can be then truncated and seeded in between the tests ensuring predictable and stable test results.</p>
<p>Integration tests can be run with the same test runner as the <a href="#unit-tests">unit tests</a>, but with a different configuration.
Configuration might include a <a href="https://jestjs.io/docs/configuration#globalsetup-string">setup</a> and a <a href="https://jestjs.io/docs/configuration#globalteardown-string">teardown</a> process.
When running tests with a database in place the tests could fail because the same data dependency is being modified in multiple tests at once, therefore it is required for these tests to run in <a href="https://jestjs.io/docs/cli#--runinband">serial mode</a>.</p>
<p>Keep in mind that you should <strong>avoid testing</strong> 3rd party libraries or <strong>code that is not part</strong> of the application logic.
This code is usually tested by the authors of those libraries.</p>
<h3 id="tools-that-help-writing-integration-tests">
Tools that help writing integration tests</h3>
<ul>
<li><a href="https://www.npmjs.com/package/casual">casual</a> or <a href="https://www.npmjs.com/package/faker">faker</a> - Fake data generators</li>
<li><a href="https://www.apollographql.com/docs/apollo-server/testing/testing/">Utilities for testing Apollo Server</a></li>
<li><a href="https://github.com/visionmedia/supertest">supertest</a> - HTTP request assertion library</li>
<li><a href="https://github.com/jefflau/jest-fetch-mock#readme">jest-fetch-mock</a> - Request mocking</li>
<li><a href="https://sinonjs.org/">SinonJS</a> - Standalone test spies, stubs and mocks</li>
</ul>
<h3 id="additional-resources-for-integration-tests">
Additional resources for integration tests</h3>
<ul>
<li><a href="https://www.edureka.co/blog/what-is-api-testing">What is API testing</a> by <a href="https://www.edureka.co/blog/author/archana-cedureka-co/">Archana Choudary @ edureka.com</a></li>
<li><a href="https://medium.com/@jdeflaux/graphql-integration-tests-with-apollo-server-testing-jest-mongodb-and-nock-af5a82e95954">GraphQL integration tests with apollo-server-testing, jest-mongodb and nock</a> by <a href="https://medium.com/@jdeflaux">Julien Deflaux @ medium.com</a></li>
</ul>
<h2 id="end-to-end--e2e--testing">
End to End (e2e) testing</h2>
<p>e2e tests should guarantee that our <strong>users can browse and use the application</strong>.
Every part of the application, whether it is a front-end, back-end, or a 3rd party integration, will be tested.
We look to verify the behavior of the application in response to events triggered by the user or any different side-effect.
The goal is to test the application from the user’s perspective.</p>
<h3 id="element-selectors">
Element selectors</h3>
<p>There are different frameworks and approaches for creating <em>DOM</em> elements.
These elements can have different representations of their state in the <em>HTML</em>.
Developers should agree on the practice of selecting these elements in tests that suits them best.
You should try to target elements in a way that you can be sure that the <strong>results will be consistent</strong> even after different layout changes that might happen throughout the lifetime of the project.
Therefore I’d say that <a href="https://developer.mozilla.org/en-US/docs/Web/XPath">XPath</a> is not the recommended way.
To have selectors that are reliable and not changing with different layout changes we can use <code>id</code> attributes or even more sophisticated <code>data-*</code> attributes.
With <code>data-*</code> attributes we can target multiple nodes with the same identifier and establish a convention that will make sure that the elements with these attributes are being used in the tests so if they are changed/deleted, there will have to be a change in the tests as well.</p>
<pre><code class="language-html"><pre style="background-color:#2b303b;">
<span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">button </span><span style="color:#d08770;">data-test-id</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">close-modal-button</span><span style="color:#c0c5ce;">&quot;&gt;Close&lt;/</span><span style="color:#bf616a;">button</span><span style="color:#c0c5ce;">&gt;
</span></pre>
</code></pre>
<p>You don’t have to mark all targeted elements with <code>id</code> you can use <em>CSS</em> selectors as well.
Ensure, that you will not have to change stable test cases because of new additional features, which are not relevant to the established tested features.</p>
<h3 id="form-validation">
Form validation</h3>
<p>Forms should be tested for correctly evaluating validation.
The errors should appear when the validation fails.
It’s also good user experience testing when tests require validation errors to be displayed only if the elements were previously touched or the form was submitted.</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#8fa1b3;">it</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">should display validation errors</span><span style="color:#c0c5ce;">&#39;, () </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">  </span><span style="color:#65737e;">// Form is submitted with `enter`
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;first-name-input&quot;]</span><span style="color:#c0c5ce;">&#39;).</span><span style="color:#8fa1b3;">type</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">{enter}</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#65737e;">// All validation errors should be visible
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;first-name-hint&quot;]</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">should</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">exist</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">and</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">contain</span><span style="color:#c0c5ce;">&#39;, &#39;</span><span style="color:#a3be8c;">First Name is a required field</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;last-name-hint&quot;]</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">should</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">exist</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">and</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">contain</span><span style="color:#c0c5ce;">&#39;, &#39;</span><span style="color:#a3be8c;">Last Name is a required field</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;email-hint&quot;]</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">should</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">exist</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">and</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">contain</span><span style="color:#c0c5ce;">&#39;, &#39;</span><span style="color:#a3be8c;">Email is a required field</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;password-hint&quot;]</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">should</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">exist</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">and</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">contain</span><span style="color:#c0c5ce;">&#39;, &#39;</span><span style="color:#a3be8c;">Password is a required field</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;terms-accepted-hint&quot;]</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">should</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">exist</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">and</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">contain</span><span style="color:#c0c5ce;">&#39;, &#39;</span><span style="color:#a3be8c;">You have to accept the terms of use</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#65737e;">// Email field validation
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;email-input&quot;]</span><span style="color:#c0c5ce;">&#39;).</span><span style="color:#8fa1b3;">type</span><span style="color:#c0c5ce;">(
</span><span style="color:#c0c5ce;">    `</span><span style="color:#a3be8c;">invalid{enter}</span><span style="color:#c0c5ce;">`
</span><span style="color:#c0c5ce;">  )
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;email-hint&quot;]</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">should</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">exist</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">and</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">contain</span><span style="color:#c0c5ce;">&#39;, &#39;</span><span style="color:#a3be8c;">Invalid email</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">})
</span></pre>
</code></pre>
<p>Form submission should be tested as well, to see if the back-end works correctly.
It should be expected to see correct error messages being shown to the user when the submission fails.
For example logging in with wrong password:</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#8fa1b3;">it</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">should toast wrong password</span><span style="color:#c0c5ce;">&#39;, () </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;email-input&quot;]</span><span style="color:#c0c5ce;">&#39;).</span><span style="color:#8fa1b3;">type</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">user@login.test</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;password-input&quot;]</span><span style="color:#c0c5ce;">&#39;)</span><span style="color:#8fa1b3;">type</span><span style="color:#c0c5ce;">(
</span><span style="color:#c0c5ce;">    `</span><span style="color:#a3be8c;">wrongpassword{enter}</span><span style="color:#c0c5ce;">`
</span><span style="color:#c0c5ce;">  </span><span style="color:#65737e;">// -- snip -- wait for submission
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;error-message&quot;]</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">should</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">exist</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    .</span><span style="color:#8fa1b3;">and</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">contain</span><span style="color:#c0c5ce;">&#39;, &#39;</span><span style="color:#a3be8c;">The password is invalid or the user does not have a password.</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">})
</span></pre>
</code></pre>
<p>While submission is in progress, the loading state of the form can be expected for having disabled input fields.
There should be a way to wait for asynchronous operations to be completed, so we can reliably test the loading behavior.
<em>Cypress</em> has an <a href="https://docs.cypress.io/api/commands/intercept.html">intercept method</a>, that allows us to <strong>hold the response</strong> and do as many expectations <strong>until it is desired</strong> for the response to be let go with the <a href="https://docs.cypress.io/api/commands/wait.html">wait method</a>.</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">intercept</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">**/submit</span><span style="color:#c0c5ce;">&#39;).</span><span style="color:#8fa1b3;">as</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">submission</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#65737e;">// -- snip -- fill the form
</span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;submit-button&quot;]</span><span style="color:#c0c5ce;">&#39;).</span><span style="color:#96b5b4;">click</span><span style="color:#c0c5ce;">()
</span><span style="color:#65737e;">// Expecting loading state
</span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">get</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">[data-test-id=&quot;submit-button&quot;]</span><span style="color:#c0c5ce;">&#39;).</span><span style="color:#8fa1b3;">should</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">have.class</span><span style="color:#c0c5ce;">&#39;, &#39;</span><span style="color:#a3be8c;">loading</span><span style="color:#c0c5ce;">&#39;).</span><span style="color:#8fa1b3;">and</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">be.disabled</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#65737e;">// If submission request is already done if will be witheld from resolving until `cy.wait` is called
</span><span style="color:#65737e;">// Otherwise it will wait until the request is completed
</span><span style="color:#bf616a;">cy</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">wait</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">@submission</span><span style="color:#c0c5ce;">&#39;)
</span></pre>
</code></pre>
<h3 id="authentication">
Authentication</h3>
<p><strong>Don’t try to log in to the application for every test</strong> that needs authenticated user.
Test the login flow as a proper test case, but just for that sake only.
For tests that require a user to be logged in, create a separate log-in functionality that will authenticate the user in the background without any browser interaction. This will speed up the testing process tremendously.
Read <a href="https://docs.cypress.io/guides/getting-started/testing-your-app#Logging-in">cypress documentation on login</a> for more examples.</p>
<h3 id="dedicated-api-routes">
Dedicated API routes</h3>
<p>Sometimes it’s beneficial to implement a dedicated testing API route/resolver which will help seed individual tests or perform a clean up after some tests.
Also, can be used for simulating a result of integration with 3rd party API without accessing 3rd party resources.
Make sure that, these routes can only be run in a non-production environment.</p>
<h3 id="screenshot-matching">
Screenshot matching</h3>
<p>Many plugins can be integrated into the testing environment, that enable <strong>testing visual regressions</strong>.</p>
<p>Screenshot snapshot matching allows to:</p>
<ul>
<li>Ensure, that the layout is not broken for different window sizes</li>
<li>Check visual changes of every component as part of a review</li>
<li>Prevent accidental visual regression in components</li>
</ul>
<p>Screenshots can be taken of the whole viewport or individual components.
Be sure to not include any variable data (Dates, IDs, etc.) in the screenshots as they will easily cause a mismatch.</p>
<h3 id="tools-that-help-writing-e2e-tests">
Tools that help writing e2e tests</h3>
<ul>
<li><a href="https://www.cypress.io/">Cypress testing framework</a> - Next generation front-end testing framework</li>
<li><a href="https://playwright.dev/">Playwright</a> - Playwright is a library to automate Chromium, Firefox and WebKit with a single API</li>
<li><a href="https://pptr.dev/">Puppeteer</a> - High level API to control Chromium</li>
<li><a href="https://github.com/americanexpress/jest-image-snapshot">Jest-image-snapshot</a> Matching image comparisons</li>
<li><a href="https://percy.io/">Percy</a> - Visual review platform for visual regression tests</li>
</ul>
<h3 id="additional-resources-for-e2e-tests">
Additional resources for e2e tests</h3>
<ul>
<li><a href="https://docs.cypress.io/guides/getting-started/testing-your-app#Testing-strategies">Cypress testing strategies</a></li>
<li><a href="https://www.browserstack.com/guide/end-to-end-testing">BrowserStack e2e testing guide</a> by Shreya Bose</li>
</ul>
<h2 id="component-tests">
Component tests</h2>
<p>Component tests take individual components and test them as a unit.
These can run in the browser or just <em>DOM</em> simulator.</p>
<p>These tests, just like unit tests, should ensure that the expected output of the component is rendered based on the component input.
They should also test the behavior of the component when any internal state change happens.
For example, when you open an accordion, you should expect the children to be shown in the output.
You can also expect, that the passed callbacks are being called according to event triggers.</p>
<p>Most often, these tests rely on <a href="#snapshots">snapshots</a>.
They reveal changes in the output of the components.
Also, they save a tremendous amount of time and pain while maintaining these tests.</p>
<h3 id="tools-and-resources-for-component-testing">
Tools and resources for component testing</h3>
<ul>
<li><a href="https://docs.cypress.io/guides/component-testing/introduction/">Cypress component testing</a></li>
<li><a href="https://reactjs.org/docs/testing.html">React testing overview</a></li>
</ul>
<h2 id="code-coverage">
Code coverage</h2>
<p>While being an important metric it can also be misleading.
It is a measure, that indicates how much of the source code is being executed while testing.
Coverage can <strong>identify branches of code</strong>, that haven’t been validated.
Having 100% coverage <strong>does not mean</strong>, that you have <strong>successfully tested</strong> your application.
High coverage does not guarantee to have bug-free code.
Therefore, it’s important to test all <strong>possible scenarios</strong> and <strong>validate the code according to requirements</strong>.</p>
<h3 id="additional-resources-for-code-coverage">
Additional resources for code coverage</h3>
<ul>
<li><a href="https://www.emgoto.com/jest-code-coverage/">Jest code coverage report explained</a></li>
<li><a href="https://www.softwaretestinghelp.com/code-coverage-tutorial/#What_Is_Code_Coverage">Code Coverage Tutorial</a></li>
</ul>
]]></content:encoded></item><item><title>Podcast episode about Agile development</title><link>https://michalvanko.dev/blog/2021-02-16-podcast-episode-about-agile-development</link><description><![CDATA[<p>Last week I’ve been invited as a guest in <a href="https://open.spotify.com/show/2sAEYvBrVtH74BZP8VGtx2">Sudolabs Products podcasts</a>. I’ve been interviewed by the host and my colleague Ján Koscelanský on my experience with <strong>building and leading</strong> the team behind <a href="https://theexpert.com">The Expert</a>. We were also talking about implementing <em>Continous Integration</em> and <em>End to End</em> testing with <a href="https://www.cypress.io/">Cypress framework</a>.
I have to admit, it was a wonderful experience and I’ve enjoyed it. I’d like to do it again in the future.</p>
]]></description><guid>https://michalvanko.dev/blog/2021-02-16-podcast-episode-about-agile-development</guid><pubDate>Tue, 16 Feb 2021 09:09:57 +0000</pubDate><content:encoded><![CDATA[<p>Last week I’ve been invited as a guest in <a href="https://open.spotify.com/show/2sAEYvBrVtH74BZP8VGtx2">Sudolabs Products podcasts</a>. I’ve been interviewed by the host and my colleague Ján Koscelanský on my experience with <strong>building and leading</strong> the team behind <a href="https://theexpert.com">The Expert</a>. We were also talking about implementing <em>Continous Integration</em> and <em>End to End</em> testing with <a href="https://www.cypress.io/">Cypress framework</a>.</p>
<p>I have to admit, it was a wonderful experience and I’ve enjoyed it. I’d like to do it again in the future.</p>
<p>The episode was <strong>recorded in Slovak</strong> language. You can <strong>listen to the podcast episode here</strong>:</p>
<iframe src="https://open.spotify.com/embed-podcast/episode/0NE6Gakn6wUP6oj7Rq4viR" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>]]></content:encoded></item><item><title>Logging recommendations</title><link>https://michalvanko.dev/blog/2020-12-18-logging-recommendations</link><description><![CDATA[<p><strong>Do not</strong> use <code>console</code> directly. Programs should be able to define logging strategies dependant on the environment.
We strongly recommend using a logging library such as <a href="https://github.com/winstonjs/winston">Winston</a> (commonly used in <em>node.js</em> programs).</p>
]]></description><guid>https://michalvanko.dev/blog/2020-12-18-logging-recommendations</guid><pubDate>Fri, 18 Dec 2020 14:39:53 +0000</pubDate><content:encoded><![CDATA[<h2 id="back-end">
Back-end</h2>
<h3 id="1--use-a-logger">
1. Use a logger</h3>
<p><strong>Do not</strong> use <code>console</code> directly. Programs should be able to define logging strategies dependant on the environment.
We strongly recommend using a logging library such as <a href="https://github.com/winstonjs/winston">Winston</a> (commonly used in <em>node.js</em> programs).</p>
<p>Libraries should provide an easy way to choose a <em>transport</em> which will be used as on output for logs.
Examples of transports:</p>
<ul>
<li>File</li>
<li>Console</li>
<li>3rd party monitoring tools (e.g: <a href="https://www.loggly.com/">loggly</a>)</li>
</ul>
<h3 id="2--log-levels">
2. Log levels</h3>
<p>You can set up as many log levels as needed.
<em>npm</em> uses these 7:</p>
<pre><code class="language-json"><pre style="background-color:#2b303b;">
<span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">  </span><span style="background-color:#bf616a;color:#2b303b;">error</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">0</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">  </span><span style="background-color:#bf616a;color:#2b303b;">warn</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">1</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">  </span><span style="background-color:#bf616a;color:#2b303b;">info</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">2</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">  </span><span style="background-color:#bf616a;color:#2b303b;">http</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">3</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">  </span><span style="background-color:#bf616a;color:#2b303b;">verbose</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">4</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">  </span><span style="background-color:#bf616a;color:#2b303b;">debug</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">5</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">  </span><span style="background-color:#bf616a;color:#2b303b;">silly</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">6
</span><span style="color:#c0c5ce;">}
</span></pre>
</code></pre>
<p>These are sorted by priority and should be used appropriately.
What is logged depends on the environment(dev, test, prod…) in which the program operates.
<em>In the production environment, <code>debug</code> logs won’t be displayed since they would cause a lot of unnecessary noise.</em>
<em>Verbose logs might be useful for staging environments where you want to see more information about what is happening in the system.</em></p>
<h3 id="3--what-to-log">
3. What to log</h3>
<p>It is very important to log all the information required for developers and DevOps to retrieve as much valuable information from the logs as possible.</p>
<p>Messages are usually generated in a way where developers don’t have to add things like timestamps, log levels, etc., and repeat themselves.
Timestamps and log levels are attached to the messages automatically.</p>
<h4 id="errors">
Errors</h4>
<p>Not every error should be logged.
Validation errors and expected errors such as handled errors
are common and they’re expected to happen frequently, so they shouldn’t interrupt the system in any way whatsoever.
Therefore they should not be seen in logs. They can be shown in the access logs.</p>
<p>Unexpected errors should be logged with the highest priority (error). These errors should be monitored, prevented, and handled.</p>
<h4 id="system-state-changes">
System state changes</h4>
<p>Back-end apps strive to be stateless but they also do have some state like:</p>
<ul>
<li>served endpoint, port</li>
<li>database connection</li>
<li>3rd party integrations</li>
</ul>
<p>Changes in the system with valuable information should be logged with the <code>info</code> level. They serve as a great source of information about the system prior to the failure.</p>
<h4 id="debug">
Debug</h4>
<p>Don’t be hesitant to use <code>debug</code> logs. They can always be turned off or turned on back again while debugging.
Most of the time developers tend to use <code>console.log</code> and then remove it before committing.
I’d encourage you to use <code>debug</code> level and leave it there.
It can be a big help in the future which can point you to the part of the code which is responsible for the manipulation of the data.</p>
<h3 id="4--don">
4. Don’t log sensitive information</h3>
<p><strong>Never, ever log credentials, passwords, or any sensitive information.</strong></p>
<h2 id="front-end">
Front-end</h2>
<p>Front-end logging is fundamentally different from the back-end.
There is a new instance of the program for every opened window and it is only opened for 1 particular client.
Developers should <strong>not</strong> expose the functionality to users.</p>
<p>There is an <em>npm package</em> <a href="https://www.npmjs.com/package/debug">debug</a> that
enables developers to write debug logs that persist in the codebase.
To reveal the logs in the console, they have to be enabled.</p>
<p>There are 3rd party <strong>error monitoring</strong> services, that can help us collect information from errors that occur in different parts of the application.</p>
<ul>
<li><a href="https://www.bugsnag.com/">bugsnag</a></li>
<li><a href="https://sentry.io/welcome/">Sentry</a></li>
</ul>
]]></content:encoded></item><item><title>Guide on error handling</title><link>https://michalvanko.dev/blog/2020-12-09-guide-on-error-handling</link><description><![CDATA[<p>Having good <em>error handling</em> practice is very important when building complex applications.
Applications which consist of multiple applications such as web applications,</p>
]]></description><guid>https://michalvanko.dev/blog/2020-12-09-guide-on-error-handling</guid><pubDate>Wed, 9 Dec 2020 14:44:11 +0000</pubDate><content:encoded><![CDATA[<p>Having good <em>error handling</em> practice is very important when building complex applications.
Applications which consist of multiple applications such as web applications,
where you have multiple parts like <em>client</em>, <em>server</em> and <em>database</em> create a system where
errors can happen in any individual parts or in communication between them.</p>
<p>Let me show you some examples of what kind of errors there might happen and how to treat them.</p>
<h2 id="common-scenarios">
Common scenarios</h2>
<p>Don’t pay attention to the coding style here.
There might be a different kind of libraries and conventions used in applications.
Code samples are written just for the demonstration of the various scenarios and what should happen in those scenarios.
Please read through the comments as they explain how errors should be treated.</p>
<h3 id="back-end-application---single-run-script-or-a-cron-job">
Back-end application - Single run script or a CRON job</h3>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">try </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">    </span><span style="color:#65737e;">// This can be any type of action that might fail
</span><span style="color:#c0c5ce;">    </span><span style="color:#bf616a;">Database</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">connect</span><span style="color:#c0c5ce;">().</span><span style="color:#8fa1b3;">insert</span><span style="color:#c0c5ce;">({ value: &#39;</span><span style="color:#a3be8c;">Some information</span><span style="color:#c0c5ce;">&#39; })
</span><span style="color:#c0c5ce;">  } </span><span style="color:#b48ead;">catch </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">err</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">    </span><span style="color:#65737e;">// It&#39;s important to **log these** errors with highest priority (error)
</span><span style="color:#c0c5ce;">    </span><span style="color:#bf616a;">logger</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">error</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">Error happened while trying to insert some information</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">err</span><span style="color:#c0c5ce;">) </span><span style="color:#65737e;">// Error context is passed here
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">    </span><span style="color:#65737e;">// Should the script continue?
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">return
</span><span style="color:#c0c5ce;">  }
</span></pre>
</code></pre>
<p>It is necessary to pass the error along with context information, that will help to trace and resolve the issue.</p>
<h3 id="back-end-application---responding-to-client-requests">
Back-end application - Responding to client requests</h3>
<p>This is usually how web applications are built and the most common scenario where errors should be properly handled as it might be a security threat.</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">app</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">post</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">/message</span><span style="color:#c0c5ce;">&#39;, (req, res) </span><span style="color:#b48ead;">=&gt; </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">const </span><span style="color:#c0c5ce;">{ </span><span style="color:#bf616a;">message</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">author </span><span style="color:#c0c5ce;">} = </span><span style="color:#bf616a;">req
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">try </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">      </span><span style="color:#bf616a;">Database</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">connect</span><span style="color:#c0c5ce;">().</span><span style="color:#8fa1b3;">insert</span><span style="color:#c0c5ce;">({ </span><span style="color:#bf616a;">message</span><span style="color:#c0c5ce;">, authorId: </span><span style="color:#bf616a;">author</span><span style="color:#c0c5ce;">.id })
</span><span style="color:#c0c5ce;">    } </span><span style="color:#b48ead;">catch </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">err</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">      </span><span style="color:#b48ead;">if </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">err </span><span style="color:#c0c5ce;">instanceof </span><span style="color:#bf616a;">ValidationError</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// Validation errors are expected to happen often.
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// They are caused by a bad input from the user.
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// There is no need to log these errors as they don&#39;t cause a system crash.
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// Client should be informed on the matter of what went wrong with the request.
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">res</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">status</span><span style="color:#c0c5ce;">(</span><span style="color:#d08770;">400</span><span style="color:#c0c5ce;">).</span><span style="color:#8fa1b3;">json</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">          message: `</span><span style="color:#a3be8c;">Invalid input. </span><span style="color:#c0c5ce;">${</span><span style="color:#bf616a;">err</span><span style="color:#c0c5ce;">.message}`
</span><span style="color:#c0c5ce;">        })
</span><span style="color:#c0c5ce;">      } </span><span style="color:#b48ead;">else if </span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">err </span><span style="color:#c0c5ce;">instanceof </span><span style="color:#bf616a;">RecoverableError</span><span style="color:#c0c5ce;">) {
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// As a `RecovarableError` we can think of an error that most usually doesn&#39;t happen
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// but can and we have a way to continue the rest of the process and handle the error recovery later.
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// As an example we can imagine a case of sending an email to users with some information.
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">logger</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">error</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">Error while sending mail</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">err</span><span style="color:#c0c5ce;">, { </span><span style="color:#bf616a;">message</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">author </span><span style="color:#c0c5ce;">})
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// Sending an email is not a crucial part of the functionality and can be done later.
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// So we continue the functionality but we log the error so we know that we have to handle it. 
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">res</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">status</span><span style="color:#c0c5ce;">(</span><span style="color:#d08770;">200</span><span style="color:#c0c5ce;">).</span><span style="color:#8fa1b3;">json</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">data</span><span style="color:#c0c5ce;">)
</span><span style="color:#c0c5ce;">      } </span><span style="color:#b48ead;">else </span><span style="color:#c0c5ce;">{
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// This is an example of an **unexpected** error.
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// It doesn&#39;t only apply to a `Database`. You can replace the `Database` with any other 3rd party system.
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// These type of errors can be programmers fault for not handling certain scenarios correctly,
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// but also be caused by unexpected events happening in the system
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// (ex. Database server is down, usage of the wrong ID of relations in SQL queries)
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// In this case we want to `log` this error with full context,
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// so it can be found and fixed if it is a programmer&#39;s fault.
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">logger</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">error</span><span style="color:#c0c5ce;">(&#39;</span><span style="color:#a3be8c;">Unexpected error happened</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">err</span><span style="color:#c0c5ce;">, { </span><span style="color:#bf616a;">message</span><span style="color:#c0c5ce;">, </span><span style="color:#bf616a;">author </span><span style="color:#c0c5ce;">})
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">        </span><span style="color:#65737e;">// Client should be informed of such an error but without the context, as it might reveal proprietary information about the system
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">res</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">status</span><span style="color:#c0c5ce;">(</span><span style="color:#d08770;">500</span><span style="color:#c0c5ce;">).</span><span style="color:#8fa1b3;">json</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">          message: &#39;</span><span style="color:#a3be8c;">Unexpected error happened</span><span style="color:#c0c5ce;">&#39;
</span><span style="color:#c0c5ce;">        })
</span><span style="color:#c0c5ce;">      }
</span><span style="color:#c0c5ce;">    }
</span><span style="color:#c0c5ce;">  })
</span></pre>
</code></pre>
<p>It is very important to <strong>keep all proprietary information</strong> about the system <strong>secure and private</strong> only to developers and system administrators.
It is a <strong>security threat</strong> and it might be abused by malicious hackers when any of the information gets leaked.
It doesn’t matter if the information is shown to the users or not, while it’s being sent to the client it can be discovered. I</p>
<p>However, if there is an additional error handling functionality in place which
is able to filter out the error context in the <code>production</code> environment,
it might be a good practice to include the error context in the <code>development</code> environment,
as it will speed up the error discoverability.</p>
<p>To implement such a feature I’d recommend splitting <code>message</code> types sent to the client,
so there isn’t a chance that someone would accidentally send proprietary information to the client.</p>
<pre><code class="language-javascript"><pre style="background-color:#2b303b;">
<span style="color:#c0c5ce;">  </span><span style="color:#b48ead;">function </span><span style="color:#8fa1b3;">sendError</span><span style="color:#c0c5ce;">(body) {
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">if </span><span style="color:#c0c5ce;">(process.env === &#39;</span><span style="color:#a3be8c;">production</span><span style="color:#c0c5ce;">&#39;) {
</span><span style="color:#c0c5ce;">      </span><span style="color:#b48ead;">return </span><span style="color:#8fa1b3;">omit</span><span style="color:#c0c5ce;">(</span><span style="color:#bf616a;">body</span><span style="color:#c0c5ce;">, &#39;</span><span style="color:#a3be8c;">errorContext</span><span style="color:#c0c5ce;">&#39;)
</span><span style="color:#c0c5ce;">    }
</span><span style="color:#c0c5ce;">    </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">body
</span><span style="color:#c0c5ce;">  }
</span><span style="color:#c0c5ce;">
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">res</span><span style="color:#c0c5ce;">.</span><span style="color:#8fa1b3;">status</span><span style="color:#c0c5ce;">(</span><span style="color:#d08770;">500</span><span style="color:#c0c5ce;">).</span><span style="color:#8fa1b3;">json</span><span style="color:#c0c5ce;">(</span><span style="color:#8fa1b3;">sendError</span><span style="color:#c0c5ce;">({
</span><span style="color:#c0c5ce;">    message: &#39;</span><span style="color:#a3be8c;">Unexpected error happened</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">    errorContext: {</span><span style="color:#bf616a;">req</span><span style="color:#c0c5ce;">, message: `</span><span style="color:#a3be8c;">Error while submitting </span><span style="color:#c0c5ce;">${</span><span style="color:#bf616a;">req</span><span style="color:#c0c5ce;">.path}`, </span><span style="color:#bf616a;">err</span><span style="color:#c0c5ce;">}
</span><span style="color:#c0c5ce;">  })
</span></pre>
</code></pre>
<h3 id="front-end-application---handle-an-error-from-back-end">
Front-end application - Handle an error from back-end</h3>
<p>When an error happens on the back-end we should show this error to the client so the user knows that his action was not successfully fulfilled.</p>
<p>If the back-end handles errors correctly and sends the client an appropriate message, the client should be able to just show the informative description of the error that occurred.</p>
<h4 id="translating-errors">
Translating errors</h4>
<p>If your application is built with any <strong>internationalization</strong> framework or with a translation system in general,
there has to be some kind of mapping from error sent from the back-end to the translated message.</p>
<p>Simple mapping could be done by having a table of error codes for any application error that might happen.
These codes don’t have to be strictly numeric. They can consist of some rules like having separators to distinguish
services or parts that fail and which way. Examples <code>RESOURCE-404</code>, <code>POST-404</code>.
It’s a good practice to have always a default message for unexpected errors which often do happen.</p>
<h3 id="front-end-application---network-error">
Front-end application - Network Error</h3>
<p>These types of errors are least expected by developers but they happen regularly.
Users usually lose connection when they browse while traveling or are connected to an unreliable mobile connection.
It should always be anticipated that these errors can happen and the application should recover
from the failure and allowed to retry their action.</p>
<h3 id="front-end-application---unexpected-error-in-application-code">
Front-end application - unexpected error in application code</h3>
<p>Errors which happen only on front-end are often a coding error.
Most common examples would be not covering all possible cases of application logic or accessing properties
that are undefined because of the unexpected shape of response data.
While back-end applications can recover from their errors by restarting the process,
it doesn’t apply to client applications where these types of <strong>errors might cause a crash</strong> or a <strong>freeze</strong> of an application.
Some frameworks allow to <strong>recover from these crashes</strong> by implementing an <strong>error boundaries</strong>.
See <a href="https://reactjs.org/docs/error-boundaries.html"><em>React</em> Error Boundaries for example</a>.
I can only highly recommend using such a type of recovery.</p>
<p>If you use an external error monitoring tool like <a href="https://sentry.io/welcome/">Sentry</a> or <a href="https://www.bugsnag.com/">bugsnag</a>, don’t forget to log these errors from the error boundary.</p>
<h2 id="rules">
Rules</h2>
<p>To summarize this, we can create a list of rules:</p>
<ul>
<li>Error messages have to be informative for the client, but they <strong>can’t reveal private and proprietary information</strong> about the system architecture</li>
<li>Errors should be logged only when they require additional action</li>
<li>Errors that are recoverable should not crash the system</li>
<li>Unexpected errors should turn into expected errors and be handled properly</li>
<li>Failures between connections of the systems should be anticipated</li>
<li>Client applications should be always able to <strong>recover from unexpected errors</strong> just as from expected ones</li>
</ul>
]]></content:encoded></item><item><title>I&apos;ve moved my site to Netlify</title><link>https://michalvanko.dev/blog/2020-11-20-ive-moved-my-site-to-netlify</link><description><![CDATA[<p>So I’ve been trying to set access to my CMS for a long time now. The problem was that in order to have CMS accessible through my domain, it <strong>requires an <em>OAuth</em> server</strong>.
I’ve figured out that creating a custom <strong>serverless function</strong> which will handle the authentication with <em>GitHub</em> would be sufficient.</p>
]]></description><guid>https://michalvanko.dev/blog/2020-11-20-ive-moved-my-site-to-netlify</guid><pubDate>Fri, 20 Nov 2020 20:35:05 +0000</pubDate><content:encoded><![CDATA[<h2 id="prelude">
Prelude</h2>
<p>So I’ve been trying to set access to my CMS for a long time now. The problem was that in order to have CMS accessible through my domain, it <strong>requires an <em>OAuth</em> server</strong>.
I’ve figured out that creating a custom <strong>serverless function</strong> which will handle the authentication with <em>GitHub</em> would be sufficient.
So I’ve took some inspiration from the 2 repositories linked in <a href="https://www.netlifycms.org/docs/external-oauth-clients/"><em>Netlify CMS</em> documentation</a>:</p>
<ul>
<li><a href="https://github.com/marksteele/netlify-serverless-oauth2-backend">netlify-serverless-oauth2-backend</a></li>
<li><a href="https://github.com/vencax/netlify-cms-github-oauth-provider">netlify-cms-github-oauth-provider</a></li>
</ul>
<p>The reason because I link both of these was that I’ve used the <em>almighty AWS Amplify</em> CLI to create an API for me, which has bootstrapped an <a href="http://expressjs.com/"><em>express</em></a> application for me. This turned out to be way easier then to make 4 independent <em>functions</em> as <em>Amplify</em> is not providing a different way of creating simple shared repository of multiple functions like <a href="https://www.serverless.com/">serverless-framework</a> does.</p>
<p>At one point as I was struggling with the <em>Amplify</em> CLI it <strong>managed to delete</strong> whole API once as I was trying to synchronize my changes with the cloud. But somewhere in the development process of the <em>Amplify framework</em> they made some changes which had made my previously used backend environments unusable. I wasn’t able to find any migration information about these changes or anything. I’d found some GitHub issues about similar problems which haven’t been resolved in any user friendly manners. So I’ve decided to just <strong>re-create these environments</strong>. I was lucky that I haven’t had anything important published in these. Also my IDE had saved me and I was able to <strong>restore deleted files from cache</strong>.</p>
<h2 id="turning-point">
Turning point</h2>
<p>After several attempts to publish the API on the AWS I figured out that to be able to use this API I have to configure a custom domain for it because <em>AWS API Gateway</em> appends <code>/${stage}</code> to every URL that it is published. This wouldn’t be so bad for any API but I was trying to create a <strong>OAuth authentication</strong> for <em>Netlify CMS</em> which required the <a href="https://github.com/netlify/netlify-cms/blob/aded9d7c24f40f6fa6a159ea764c1e4fccbe82c3/packages/netlify-cms-lib-auth/src/netlify-auth.js#L44"><code>base_name</code> to be equal to the <code>origin</code></a> of the API. I was trying to configure custom domain name for this API then. Thinking that <em>Amplify</em> will be able to help me, but this isn’t a concern for its use case yet. So I’ve managed to create a certificate for the domain first. Waited half an hour for validation. And then finally I’ve got to the point that to be able to configure that custom domain name for this API I will have to configure <em>AWS Route58</em>, which is their DNS configuration service, to manage my entire domain which I haven’t set up yet as I didn’t think it would be necessary. My site has been working correctly just with <code>CNAME</code> configuration just right.</p>
<p>That was my the moment I’ve decided to <strong>quit AWS completely</strong>. This was after 2 days configuring and debugging a very simple think such as creating a simple API with 4 routes.</p>
<h2 id="alternatives">
Alternatives</h2>
<p>I’ve done some research and was looking between 2 main contenders to host my precious website:</p>
<ul>
<li><a href="https://www.netlify.com/">Netlify</a></li>
<li><a href="https://vercel.com/">Vercel</a></li>
</ul>
<p>I haven’t found a big decider feature in any of these so I just went with the one I already had an account set up - <em>Netlify</em>.
I was able to <strong>configure whole deployment</strong> of my website <strong>in 8 minutes</strong>.
The instructions where so clear and simple that I’ve also <strong>transfered my DNS nameserver</strong> too. I didn’t have to search in 10 different services and documentations. I’ve just went with the flow. Didn’t had to change a single line of code. I’ve just removed the <code>amplify</code> folder and I don’t want to see any of the AWS docs ever again.</p>
<p>It got me so pumped that I’ve immediately logged in to the CMS (finally) through my own domain and written this article.</p>
]]></content:encoded></item><item><title>How to deal with technical debt</title><link>https://michalvanko.dev/blog/2020-11-08-how-to-deal-with-technical-debt</link><description><![CDATA[<p>Technical debt is a massive problem that costs a lot of time for everyone working on the team.
It should be part of the development cycle to eliminate it.</p>
]]></description><guid>https://michalvanko.dev/blog/2020-11-08-how-to-deal-with-technical-debt</guid><pubDate>Sun, 8 Nov 2020 15:57:18 +0000</pubDate><content:encoded><![CDATA[<p>Technical debt is a massive problem that costs a lot of time for everyone working on the team.
It should be part of the development cycle to eliminate it.
<strong>Manage a secured time</strong> in a development process to increase development experience.</p>
<p>By planning <strong>regular tasks</strong> like:</p>
<ul>
<li>Upgrading dependencies, gathering information about migrations, and creating tasks for those which require attention.</li>
<li>Solving <em>action points</em> discussed on retrospective.</li>
<li>Finding and creating tasks for dealing with technical debt.</li>
</ul>
<p>Assign a certain amount of story points for each sprint to those tasks which have been accumulated and prioritized.</p>
<blockquote>
<p><em>Action points</em> are tasks agreed upon on <strong>retrospectives</strong>. These are the <em>points</em> which the team has agreed on. They should be solved in the upcoming sprint to make the developer experience better for the whole team.</p>
</blockquote>
]]></content:encoded></item><item><title>WebAssembly briefing</title><link>https://michalvanko.dev/blog/2020-08-05-webassembly-briefing</link><description><![CDATA[<p>Last week I’d organized a small briefing at work about <strong>WebAssembly</strong>. The goal of the briefing was to give my colleagues an overview about what WebAssembly is and how it can be used to improve our products.
I’ve used these notes for the discussion.</p>
]]></description><guid>https://michalvanko.dev/blog/2020-08-05-webassembly-briefing</guid><pubDate>Wed, 5 Aug 2020 15:38:18 +0000</pubDate><content:encoded><![CDATA[<p>Last week I’d organized a small briefing at work about <strong>WebAssembly</strong>. The goal of the briefing was to give my colleagues an overview about what WebAssembly is and how it can be used to improve our products.</p>
<p>I’ve used these notes for the discussion.</p>
<h2 id="what-should-have-been-already-known">
What should have been already known</h2>
<blockquote>
<p>WebAssembly is a new type of code that can be run in modern web browsers — it is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages such as C/C++ and Rust with a compilation target so that they can run on the web. It is also designed to run alongside JavaScript, allowing both to work together.</p>
</blockquote>
<ul>
<li><em>https://developer.mozilla.org/en-US/docs/WebAssembly</em></li>
</ul>
<p>The list of the languages that compile to WebAssembly is getting bigger and bigger. But does it support <code>_.sample(popularLanguages)</code>? Most probably: yes, it does. Someone already made a <strong>language designed for WebAssembly</strong> called <a href="https://www.assemblyscript.org/">Assembly script</a>.
It is heavily inspired by <a href="https://www.typescriptlang.org/">Typescript</a>, and it targets web developers that don’t want to learn a new language.</p>
<h3 id="motivation-behind-webassembly">
Motivation behind WebAssembly</h3>
<blockquote>
<p>WebAssembly is a safe, portable, low-level code format designed for efficient execution and compact representation. Its main goal is to enable high performance applications on the Web, but it does not make any Web-specific assumptions or provide Web-specific features, so it can be employed in other environments as well.</p>
</blockquote>
<ul>
<li><em>https://webassembly.github.io/spec/core/intro/introduction.html</em></li>
</ul>
<p>As the WebAssembly programs are compiled into portable binary-code format, they are fast to transfer over the wire and also parse by clients.</p>
<p>Use of such technology allows new types of programs to be ran inside the web browser, such as:</p>
<ul>
<li>Image / Video editors</li>
<li>AAA Games</li>
<li>ML / AI-powered applications</li>
<li>Scientific visualizations and simulations</li>
<li>Live video augmentation</li>
<li>Language interpreters and VMs</li>
<li>CAD applications</li>
</ul>
<h3 id="brief-history">
Brief history</h3>
<ul>
<li>2013 - Mozilla designed <a href="http://asmjs.org/">asm.js</a>, <strong>subset of JavaScript</strong> which allowed compiling C / C++ applications to be compiled for web while maintaining performance characteristics.
<ul>
<li>Compiling is done via <a href="https://emscripten.org">Emscripten</a>, which is still used as of today to port C applications to WASM.</li>
</ul>
</li>
<li>2015 - WebAssembly announced with a feature set of asm.js</li>
<li>2017 - Initial MVP API designed and agreed upon in between 4 major browser vendors</li>
<li>2018 - Core specification released</li>
</ul>
<h2 id="specification">
Specification</h2>
<p>Standardization process of WebAssembly is done in public at <a href="https://github.com/WebAssembly/meetings/blob/master/process/phases.md">Github</a> and is very similar to standardization process of JavaScript language.</p>
<p><strong>Current version of the API is 1.1</strong>. It is intended that it will be superseded by new incremental releases with additional features in the future.</p>
<p>Here is a list of latest standartized features which are being implemented in browsers: <a href="https://webassembly.org/roadmap/">Roadmap</a></p>
<h2 id="list-of-applications-making-use-of-webassembly">
List of applications making use of WebAssembly</h2>
<ul>
<li><a href="https://medium.com/figma-design/webassembly-cut-figmas-load-time-by-3x-76f3f2395164">Figma</a>
<ul>
<li><a href="https://medium.com/figma-design/webassembly-cut-figmas-load-time-by-3x-76f3f2395164">Figma has improved load times 3x with WASM</a></li>
</ul>
</li>
<li><a href="https://medium.com/google-earth/google-earth-comes-to-more-browsers-thanks-to-webassembly-1877d95810d6">Google Earth</a></li>
<li><a href="https://silvia-odwyer.github.io/photon/">Photon Image processing</a></li>
<li><a href="https://personal-1094.web.app/gemrb.html">Baldur’s Gate II</a></li>
<li><a href="http://wasm.continuation-labs.com/d3demo/">Doom 3</a></li>
<li><a href="https://medium.com/leaningtech/running-flash-in-webassembly-using-cheerpx-an-update-d500b6fbc44e">Flash virtualization</a></li>
<li><a href="https://www.autodesk.com/products/autocad-web-app/overview?linkId=68719474&amp;plc=ACDIST&amp;term=1-YEAR&amp;support=ADVANCED&amp;quantity=1">AutoCAD</a></li>
<li><a href="https://tech.ebayinc.com/engineering/webassembly-at-ebay-a-real-world-use-case/?utm_source=twitter&amp;utm_medium=Social&amp;utm_campaign=AddThisWidget#.XOhbJgZV6Nw.twitter">ebay using WASM for barcode scanning</a></li>
<li><a href="https://blogs.dropbox.com/tech/2018/06/building-better-compression-together-with-divans/">Dropbox compression algorithm</a></li>
</ul>
<h2 id="endless-possibilities">
Endless possibilities</h2>
<h3 id="what-it-could-be-used-for">
What it could be used for</h3>
<ul>
<li>Cross platform application development</li>
<li>Hardware made directly for consuming WebAssembly instructions
<ul>
<li>If WASM is emergint to become the standard format for executables, tailor-made hardware chips migth become more efficient in the future. No more competing architectures.</li>
</ul>
</li>
<li>Transformation of the web to more efficient platform would have ecological impact on the environment. If the bundles are smaller and easier to parse that means it will significantly lower need for resources (less power consumption, cheaper hardware).</li>
</ul>
<h2 id="web-application-frameworks">
Web application frameworks</h2>
<h3 id="blazor">
Blazor</h3>
<blockquote>
<p>Blazor is an open source and cross-platform web UI framework for building single-page apps using .NET and C# instead of JavaScript.
Blazor is based on a powerful and flexible component model for building rich interactive web UI.
You implement Blazor UI components using a combination of .NET code and Razor syntax: an elegant melding of HTML and C#. Blazor components can seamlessly handle UI events, bind to user input, and efficiently render UI updates.</p>
<p>Blazor WebAssembly is now the second supported way to host your Blazor components: client-side in the browser using a WebAssembly-based .NET runtime. Blazor WebAssembly includes a proper .NET runtime implemented in WebAssembly, a standardized bytecode for the web. This .NET runtime is downloaded with your Blazor WebAssembly app and enables running normal .NET code directly in the browser. No plugins or code transpilation are required. Blazor WebAssembly works with all modern web browsers, both desktop and mobile. Similar to JavaScript, Blazor WebAssembly apps run securely on the user’s device from within the browser’s security sandbox. These apps can be deployed as completely standalone static sites without any .NET server component at all, or they can be paired with ASP.NET Core to enable full stack web development with .NET, where code can be effortlessly shared with the client and server.</p>
</blockquote>
<ul>
<li><em>https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-now-available/</em></li>
</ul>
<p><img
  alt=""
  src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fimage.slidesharecdn.com%2Fskackovwasm-net-fwdaysimproved-180418134149%2F95%2Foleksandr-skachkov-running-in-your-web-browser-with-webassembly-44-638.jpg%3Fcb%3D1524058963&f=1&nofb=1"
/>Blazor overview</figcaption></figure></p>
<h3 id="yew">
Yew</h3>
<p>Yew is a modern Rust framework for creating multi-threaded front-end web apps with WebAssembly.</p>
<ul>
<li>Features a macro for declaring interactive HTML with Rust expressions. Developers who have experience using JSX in React should feel quite at home when using Yew.</li>
<li>Achieves high performance by minimizing DOM API calls for each page render and by making it easy to offload processing to background web workers.</li>
<li>Supports JavaScript interoperability, allowing developers to leverage NPM packages and integrate with existing JavaScript applications.</li>
<li><a href="https://github.com/yewstack/yew">github repository</a></li>
<li><a href="https://yew.rs/docs/getting-started/build-a-sample-app">Sample app</a></li>
</ul>
<h3 id="oak">
Oak</h3>
<p>Really simple framework for Golang <a href="https://github.com/elliotforbes/go-webassembly-framework">Github</a></p>
<h2 id="conclusion">
Conclusion</h2>
<p>The discussion went for about 2 hours and it sparked many ideas. I am really glad that I’ve found time to do prepare and organize it.
We talked a lot about how WebAssembly could be used in front-end frameworks for computational parts to increase the speed of calculations of DOM changes. In virtual-dom frameworks for example it could calculate the diffs more efficiently.
I think, the frameworks presented are doing something similar, but with use of different languages. Which I think is for better. This sparked another discussion about programming languages. Especially how <em>JavaScript</em> is useful enough but is also ergonomic and friendly for programmers to use?</p>
<p>Discussions lasted for few days. I am really excited about the feedback I’ve received. I’ll definitely organize something similar in the near future.</p>
]]></content:encoded></item><item><title>I&apos;ve made an RSS feed</title><link>https://michalvanko.dev/blog/2020-06-15-ive-made-rss-feed</link><description><![CDATA[<p>I was thinking of the ways how could people subscribe to the blog without checking it periodically or me having to post on social media any time that I post a new article.
The first thing that came to my mind was an RSS feed. I thought that it should be very easy to create one (and it is) so I started to work on it last weekend.</p>
]]></description><guid>https://michalvanko.dev/blog/2020-06-15-ive-made-rss-feed</guid><pubDate>Mon, 15 Jun 2020 15:48:18 +0000</pubDate><content:encoded><![CDATA[<p>I was thinking of the ways how could people subscribe to the blog without checking it periodically or me having to post on social media any time that I post a new article.
The first thing that came to my mind was an RSS feed. I thought that it should be very easy to create one (and it is) so I started to work on it last weekend.
RSS is pretty old specification (talking in software years) and there are few alternatives that are trying to make online syndication more generic or better said adaptable for broader / modern use.</p>
<h2 id="what-is-rss-feed">
What is RSS feed</h2>
<p>RSS stands for <em>Rich Site Summary</em>. It is a specification for a <code>XML</code> formatted file which should provide short summary of the site content. First version was built in 1999 and the standard is still <em>de facto</em> standard across the web.</p>
<p>The feed should be short so the consuming applications can download these feeds on regular basis without too much data being transferred.</p>
<p>As I was looking into how could I implement RSS feed for this blog I was pretty unsatisfied with the information on the internet as there weren’t that many recipes or tutorials. The web was full of plugins or suggestions of dedicated software applications. Nothing really straight forward.</p>
<h2 id="alternatives">
Alternatives</h2>
<p>As the use of the internet changed through out the years there came some alternatives to the RSS feed specifications. First one that is also <code>XML</code> based is called <strong>Atom</strong>.</p>
<p><em>Atom</em> was created as a response to the frozen <em>RSS 2.0.1</em> specification which was not satisfying to use anymore. It requires feed to specify some required fields so its easier for feed readers to parse and display.</p>
<p>On the other side there is <strong>JSON Feed</strong> specification which takes the modern approach of minimalism and even more broader use. It only specifies a little information as required and supports a case of <strong>microblogging</strong>.
The reason for <code>JSON</code> is that it is much commonly used in this information era than the <code>XML</code> format. I personally feel that way as well but the downside of this specification is that it is not very well known and used in consumer applications.</p>
<p>I am sure you can find much more information about <a href="https://en.wikipedia.org/wiki/Atom_(Web_standard)"><em>Atom</em></a> and <a href="https://jsonfeed.org/"><em>JSON Feed</em></a> on the internet.</p>
<h2 id="how-to-create-a-feed-for-sapper-site">
How to create a feed for sapper site</h2>
<p>This blog is created with <a href="https://sapper.svelte.dev/"><strong>sapper</strong></a> so to generate a feed I needed to create following file structure:</p>
<pre><code class="language-bash"><pre style="background-color:#2b303b;">
<span style="color:#c0c5ce;">  </span><span style="color:#8fa1b3;">-</span><span style="color:#c0c5ce;"> src
</span><span style="color:#c0c5ce;">    </span><span style="color:#8fa1b3;">+</span><span style="color:#c0c5ce;"> components
</span><span style="color:#c0c5ce;">    </span><span style="color:#8fa1b3;">-</span><span style="color:#c0c5ce;"> routes
</span><span style="color:#c0c5ce;">      </span><span style="color:#8fa1b3;">+</span><span style="color:#c0c5ce;"> blog
</span><span style="color:#c0c5ce;">      </span><span style="color:#8fa1b3;">-</span><span style="color:#c0c5ce;"> feed
</span><span style="color:#c0c5ce;">        </span><span style="color:#8fa1b3;">_feed.js
</span><span style="color:#c0c5ce;">        </span><span style="color:#8fa1b3;">index.json.js
</span><span style="color:#c0c5ce;">        </span><span style="color:#8fa1b3;">index.xml.js
</span></pre>
</code></pre>
<p><code>_feed.js</code> contains logic for creating the feed and the <code>index.json.js</code> and <code>index.xml.js</code> are there to provide desired path for the feeds.
As you can tell I’ve built RSS feed as well as <em>JSON</em> one. For feed construction I’ve used node library https://github.com/jpmonette/feed which provides a way to export constructed feed into multiple formats. That’s why there is a <code>_feed.js</code> file which contains this logic that can be easily reused. Speaking of reusing logic. For the content I just refactored the blog listing logic and put the parser of the blog files into separate function and that was it.</p>
<p>I use <code>export</code> functionality of <strong>sapper</strong> so constructing the feed was not enough. As the <code>export</code> uses a crawler to find linked content on your site from the entry point, I had 2 options. Either add the path of the feeds to the export command to the AWS console or just link the feeds somewhere accessible in the site. Therefore I went for the generally better option and I’ve linked these feeds in the <code>footer</code> so now you can <strong>subscribe</strong> and receive updates with your feed consuming application of choice.</p>
<h2 id="discoverability">
Discoverability</h2>
<p>One thing that I’d struggled with was finding out information about how to let know these consumer applications where they can find my feeds.
It took me some time to find but in the end it is just one <code>&lt;meta&gt;</code> tag in the <code>&lt;head&gt;</code> for each feed I offer.</p>
<pre><code class="language-html"><pre style="background-color:#2b303b;">
<span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">link </span><span style="color:#d08770;">rel</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">alternate</span><span style="color:#c0c5ce;">&quot; </span><span style="color:#d08770;">type</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">application/rss+xml</span><span style="color:#c0c5ce;">&quot; </span><span style="color:#d08770;">title</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">RSS feed for latest posts</span><span style="color:#c0c5ce;">&quot; </span><span style="color:#d08770;">href</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">https://michalvanko.dev/feed.xml</span><span style="color:#c0c5ce;">&quot; /&gt;
</span><span style="color:#c0c5ce;">&lt;</span><span style="color:#bf616a;">link </span><span style="color:#d08770;">rel</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">alternate</span><span style="color:#c0c5ce;">&quot; </span><span style="color:#d08770;">title</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">JSON feed for latest posts</span><span style="color:#c0c5ce;">&quot; </span><span style="color:#d08770;">type</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">application/json</span><span style="color:#c0c5ce;">&quot; </span><span style="color:#d08770;">href</span><span style="color:#c0c5ce;">=&quot;</span><span style="color:#a3be8c;">https://michalvanko.dev/feed.json</span><span style="color:#c0c5ce;">&quot; /&gt;
</span></pre>
</code></pre>
<p>I’m pretty happy about implementing these feeds as I’ve learned something new about the possibilities of subscribing to content that I can filter myself and is not controlled by anyone else. I am trying out some applications that I’ve found and perhaps make a review of those at some point.</p>
]]></content:encoded></item><item><title>Samsung Galaxy Buds+ Review</title><link>https://michalvanko.dev/blog/2020-06-07-samsung-galaxy-buds-plus-review</link><description><![CDATA[<p>I was very sceptical of the new trend of truly wireless headphones at first.
I didn’t believe that the technology is capable of producing a high quality headphones with a sensible time of usage without some major drawbacks in comfort of wearing them or some other major issues.</p>
]]></description><guid>https://michalvanko.dev/blog/2020-06-07-samsung-galaxy-buds-plus-review</guid><pubDate>Sun, 7 Jun 2020 22:58:18 +0000</pubDate><content:encoded><![CDATA[<p>I was very sceptical of the new trend of truly wireless headphones at first.
I didn’t believe that the technology is capable of producing a high quality headphones with a sensible time of usage without some major drawbacks in comfort of wearing them or some other major issues.
I have to say that I came very late to the party as well. I haven’t been using plugs in years because of the lack of quality sound in many of which I’ve used. I am still pretty happy with my over-head headphones after all but after some time has passed I got very curious about these.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/IMG_20200607_235043_645x860.jpg 1x, /generated_images/images/uploads/IMG_20200607_235043_967x1290.jpg 1.5x, /generated_images/images/uploads/IMG_20200607_235043_1290x1720.jpg 2x, /generated_images/images/uploads/IMG_20200607_235043_1935x2580.jpg 3x, /generated_images/images/uploads/IMG_20200607_235043_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/IMG_20200607_235043_645x860.jpg"
    width="645"
    height="860"
    alt="Samsung Galaxy Buds+ in a case"
    
>
</picture>
<figcaption>
Samsung Galaxy Buds+ in a case</figcaption></figure></p>
<h2 id="sound-quality">
Sound quality</h2>
<p>This is the number one priority for me that I seek in any headphones or speakers. I want to listen to music as it was composed to be listened. Sound has to be balanced and clear. I don’t like the amplified bass that most of the speakers are trying to do to hide the lack of the quality of the higher tones.</p>
<p>With the <em>Buds+</em> I am really satisfied. For ear buds they have a very good sound. Nothing I ever experienced before. The listening still doesn’t match the enjoyment of listening on monitors but it’s very convienient for traveling or walking.</p>
<h2 id="noise-cancellation">
Noise cancellation</h2>
<p>There is no active noise cancellation but I don’t think it’s even required. They even have butter noise cancellation then my other over-head headphones with active cancellation. They isolate as you put them inside your ear. It doesn’t even occur to me that I would have to re-adjust them. They fit nicely and stay and hold tight.</p>
<h2 id="ambient-sound">
Ambient sound</h2>
<p>Both buds have integrated microphone so when you have them on and don’t want to get them off you can just turn ambient sound and be back in the reality. The ambient sound is also loud enough that you don’t have to turn music off while doing some work, but when you try to communicate it really helps to turn it off. I don’t know how I should feel about not pulling them off while conversations because there isn’t really any downside to using ambient sound.</p>
<p>I wish that the microphone will be able to recognize wind and not cause my ears to bleed. It would make cycling a lot more enjoyable.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/IMG_20200607_235105_645x860.jpg 1x, /generated_images/images/uploads/IMG_20200607_235105_967x1290.jpg 1.5x, /generated_images/images/uploads/IMG_20200607_235105_1290x1720.jpg 2x, /generated_images/images/uploads/IMG_20200607_235105_1935x2580.jpg 3x, /generated_images/images/uploads/IMG_20200607_235105_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/IMG_20200607_235105_645x860.jpg"
    width="645"
    height="860"
    alt="Samsung Galaxy Buds+ preview in and outside of case"
    
>
</picture>
<figcaption>
Samsung Galaxy Buds+ preview in and outside of case</figcaption></figure></p>
<h2 id="comfort">
Comfort</h2>
<p>These pair of buds is convenient to use. You can pick either one bud and use only that one. There is no limitation on which one has to be used as both are able to receive signal right from the source. They last as long as advertised. I charge them once a week and the charging speed is incredible. I am not going to put any numbers here because they charge so fast I don’t even notice them when they are fully charged.</p>
<p>One thing I was sceptical about truly wireless headphones is their ability to hold tight in ear without causing any pain.
At first it was bothering me as I was not used to any of the plug headphones but in one week I got used to them.
When I have my ears blocked with headphones I can feel the beat of my feet in my head, but that one got lost as well, or it’s not that significant anymore.</p>
<p>I didn’t had any technical difficulties.</p>
<h2 id="controls">
Controls</h2>
<p>There is still a room for improvement. I’d expect the controls to be more responsive. It often happens to me that I will prematurely press the “button” one more time than I wanted to because of the late sound feedback. Also there is this setting that I can control sound volume with a precise hit of the corner of the buds, but after many attempts I was not successful to find it.
On the other hand, I really like the functionality of turning on ambient sound just by long-press.</p>
<h3 id="extra-tip">
Extra tip</h3>
<p>There is one issue with the volume level by default. Buds have their own volume control when paired with some (old) phones and the volume is not set to be on maximum which can make you think that these do not perform very well at all. To fix this you have to:</p>
<ol>
<li>go to the <em>Wearables app</em> and set controls on to volume controls.</li>
<li>Set the volume on maximum.</li>
<li>You can set the controls back to any of your preferences.</li>
<li>Enjoy?</li>
</ol>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/IMG_20200607_235238_645x860.jpg 1x, /generated_images/images/uploads/IMG_20200607_235238_967x1290.jpg 1.5x, /generated_images/images/uploads/IMG_20200607_235238_1290x1720.jpg 2x, /generated_images/images/uploads/IMG_20200607_235238_1935x2580.jpg 3x, /generated_images/images/uploads/IMG_20200607_235238_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/IMG_20200607_235238_645x860.jpg"
    width="645"
    height="860"
    alt="Samsung Galaxy Buds+ in case"
    
>
</picture>
<figcaption>
Samsung Galaxy Buds+ in case</figcaption></figure></p>
<h2 id="summary">
Summary</h2>
<p>In overall I recommend these or trying any truly wireless headphones out there. They are a revolutionary piece of hardware.</p>
<blockquote>
<p><em>“What more can I say? Are you not entertained”?</em></p>
</blockquote>
]]></content:encoded></item><item><title>Transition to Colemak keyboard layout</title><link>https://michalvanko.dev/blog/2020-05-11-transition-to-colemak-layout</link><description><![CDATA[<p>So it has been 3 months since I’ve made a full transition to only writing on <strong>Colemak keyboard layout</strong>. Now that I am in the middle of the adaptation period is time to sum up what was causing the most problems and what went smoothly.
Lazy explanation would be that it is something between <em>QWERTY</em> and <em>Dvorak</em>. It keeps the most used shortcuts unchanged and moves the keys that are most frequently used to the middle.</p>
]]></description><guid>https://michalvanko.dev/blog/2020-05-11-transition-to-colemak-layout</guid><pubDate>Mon, 11 May 2020 05:38:18 +0000</pubDate><content:encoded><![CDATA[<p>So it has been 3 months since I’ve made a full transition to only writing on <strong>Colemak keyboard layout</strong>. Now that I am in the middle of the adaptation period is time to sum up what was causing the most problems and what went smoothly.</p>
<h2 id="what-is-colemak-keyboard-layout">
What is Colemak keyboard layout</h2>
<p>Lazy explanation would be that it is something between <em>QWERTY</em> and <em>Dvorak</em>. It keeps the most used shortcuts unchanged and moves the keys that are most frequently used to the middle.</p>
<p><img
  alt="Colemak keyboard layout preview"
  src="https://colemak.com/wiki/images/6/6c/Colemak2.png"
/>Colemak keyboard layout preview</figcaption></figure></p>
<p>At the time of writing this it is the <strong>3rd most used keyboard layout</strong> after those 2 mentioned above.</p>
<p>To get to know <em>Colemak</em> better I’d visit the <a href="https://colemak.com/">official website</a>.</p>
<p>In last article you might get sense of how I got passional for keyboards and it was a gateway for how can I improve my typing experience even more. I had no idea that there was something like <em>Colemak</em> before that. I knew that there is <em>Dvorak</em> but also knew that it is completely different and if I were to learn something new I had to see an easier path. Somewhere on the <em>YouTube</em> I saw few videos and presentations where there were comparisons between layouts and the typical <em>“Why are we even using QWERTY in 21st century.”</em> And I couldn’t agree more. It is never too late to learn something but I start to regret I haven’t done that much sooner.</p>
<p>The best argument to choose <em>Colemak</em> instead of <em>Dvorak</em> was that the most used and sometimes <strong>destructive keyboard shortcuts are kept at the same spot</strong>. So keys like <code>Q</code> <code>W</code> <code>A</code> <code>Z</code> <code>X</code> <code>C</code> <code>V</code> are not moved and will still be accessible used with <code>Ctrl</code> key. I did have a little problem with these keys anyway as I learned to use all 10 fingers as I was learning <em>Colemak</em>. According to <em>Colemak</em> website <em>“Only 2 keys move between hands”</em>. Which means that other keys moved only a little.</p>
<h2 id="how-i">
How I’ve learned it</h2>
<p>The best way to learn it is to not get frustrated. So don’t set high expectations on yourself. I’ve decided to <strong>practice</strong> each evening for at least 20 - 30 minutes. I started with the home row practice on https://thetypingcat.com/. I’d go from using only 4 keys to whole row by adding 2 keys each time until I’ve got to 30 words per second.</p>
<p>I’ve set <em>Colemak</em> as the primary keyboard layout in GNOME which made all shortcuts to only work as I was on <em>Colemak</em> even if I were using <em>QWERTY</em> while I was working. That turned up to be a great start because my brain thinks of shortcuts not by its key but what it usually does. The first shortcut that caused me pain was <code>Ctrl-S</code> even thou it only moved one key to the right. I am used to save my files manually so I press this combination a lot. Fortunately <code>Ctrl-R</code> does nothing in my <em>VS Code</em> so nothing bad happens when I miss the save. I’ve lasted for 2 weeks and then I had a comfortable time just being on this weird setup where I had different shortcuts but still familiar keyboard until <strong><a href="https://michalvanko.dev/blog/2020-04-10-custom-redox-keyboard-assembly">I’ve built myself a keyboard</a></strong> and decided to only type with <em>Colemak</em> on it. So I did it. I’ve known only home row and some other shortcuts (<code>Ctrl-P</code> to open file, <code>Ctrl-F</code> to find). And without any practice of other rows I’ve <strong>jumped into the water</strong> and started to write on keyboard layout with much difficulties but I have to say it is the best way to learn it as you just have to learn what key does what.</p>
<p>For the first week I had a browser tab opened with the keyboard layout so I look up a key and find it that way rather than try out every key. As Sean <em>“Day 9”</em> Plott says: <em>“You don’t click the building in the menu. You find the shortcut and press it anyway so you learn it along the way.”</em> As he is teaching how to play <em>StarCraft</em>. Inspired by this, I don’t have to have the keys printed out on the keyboard. I have to <strong>memorize their position</strong> by using them.</p>
<p>You may ask how was I able to work when I was barely able to write? Well as a programmer I don’t write that much code. Most of the time the programming consist of reading someone elses code and thinking how to implement certain feature. The writing part is the smallest one really. Most of the writing is done in between the programmers themselves. Yeah I had much <strong>struggles responding to messages</strong> as I was typing a single sentence for whole minute. This frustrated me but I was progressing very quickly.</p>
<p>After few months I’ve discovered that I can also <strong>setup <em>Colemak</em> on the mobile phone</strong> as well. So I immediately did. Writing on mobile is completely different experience because I tend to miss-click a lot more because of the size of the buttons. And mistakes are harder to auto-correct as it isn’t build with the premise of using different layout. The <code>E</code> and <code>I</code> keys are now next to each other and which causes these mistakes to happen.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/colemak_keyboard_mobile_1080x729.jpg 1x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/colemak_keyboard_mobile_1080x729.jpg"
    width="1080"
    height="729"
    alt=""
    
>
</picture>
<figcaption>
Colemak setup on mobile phone</figcaption></figure></p>
<h2 id="common-struggles">
Common struggles</h2>
<h3 id="shortcuts">
Shortcuts</h3>
<p>The first most common struggle was with shortcuts. Simply my brain is not connecting the letters to the actions but it just remembers that when I press certain combinatation it does that action which I got used to. The most destructive shortcuts are at the same position so I haven’t got any serious issues.</p>
<h3 id="finding-a-key">
Finding a key</h3>
<p>Sounds simple but I often used to just hit the same spot for single key over and over and it just wasn’t there. Also quotes are moved on the <code>P</code> key position which is hard to find on keyboard as you don’t expect a non letter key over there. But it turned out to be a great change.</p>
<h3 id="passwords">
Passwords</h3>
<p>Another one that your brain don’t even think of how it types out, it just does. Well all passwords automatically changed for me so I had to le-learn to write them differently.</p>
<h2 id="current-state">
Current state</h2>
<p>At this point I am comfortable using <em>Colemak</em> layout without any reoccurring struggles. The typing speed is not even close to what it used to be but that doesn’t matter that much. I do a lot of mistakes so the <code>Backspace</code> button is used more often. That was the first thing that I got used to actually. The <strong><code>CapsLock</code> button being transformed into <code>Backspace</code></strong> is great idea. I haven’t used <code>CapsLock</code> ever.</p>
<p>I’ve started to struggle to type on <em>QWERTY</em> layout instead. Every time I have to type with it I have to look at the keyboard and only type with 4 fingers which is pretty ugly. I’m also used to tap <code>CapsLock</code> whenever I do mistake which happens a lot.</p>
<p>In overall I am very happy with the switch. It’s not only that I learned typing with all 10 fingers but the layout is much more comfortable as well.</p>
]]></content:encoded></item><item><title>Custom Redox keyboard assembly</title><link>https://michalvanko.dev/blog/2020-04-10-custom-redox-keyboard-assembly</link><description><![CDATA[<p>In this blog post, I am going to take you out on a little journey of making my own keyboard. Perhaps help you if you’ve decided to make yourself your own.
But first, I want to tell you:</p>
]]></description><enclosure url="/images/uploads/img_20200301_171735.jpg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2020-04-10-custom-redox-keyboard-assembly</guid><pubDate>Fri, 10 Apr 2020 15:16:54 +0000</pubDate><content:encoded><![CDATA[<p>In this blog post, I am going to take you out on a little journey of making my own keyboard. Perhaps help you if you’ve decided to make yourself your own.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/img_20200301_171735_1147x860.jpg 1x, /generated_images/images/uploads/img_20200301_171735_1720x1290.jpg 1.5x, /generated_images/images/uploads/img_20200301_171735_2294x1720.jpg 2x, /generated_images/images/uploads/img_20200301_171735_3441x2580.jpg 3x, /generated_images/images/uploads/img_20200301_171735_4000x2999.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/img_20200301_171735_1147x860.jpg"
    width="1147"
    height="860"
    alt="Preview of Redox keyboard with RGB lights turned on in a dark"
    
>
</picture>
<figcaption>
Preview of Redox keyboard with RGB lights turned on in a dark</figcaption></figure></p>
<p>But first, I want to tell you:</p>
<h2 id="why-i">
Why I’ve decided to make my own keyboard</h2>
<p>I’ve been feeling pain in the wrist of my right hand for 2 years now. I’ve started to feel it when I started to be more physically active. I’ve been working out and played a lot of squash. It got to the point that I had to visit an orthopedist. I was scared that I’d have the infamous <strong>carpal tunnel syndrome</strong>. At this stage, he said it was just strained. The pain eased out after some therapy but it still comes back and it got to the point that I had to change my work habits.</p>
<p>One night I was checking out the state of programming streams on <em>twitch</em>. I’ve found a great streamer <a href="https://www.twitch.tv/jmswrnr">JMS WRN</a> and as I was chatting with him about my wrist problem he gave me a recommendation for my new mouse: <a href="https://www.logitech.com/en-us/product/mx-vertical-ergonomic-mouse?crid=7">Logitech MX Vertical</a> and then he showed me that he has a <em>Discord channel</em> just for keyboard stuff. And that was <strong>the moment</strong> for me. I’ve discovered a whole new world of possibilities around keyboards.</p>
<p>I’ve started to do research around custom keyboards, their parts, <em>QMK software</em>, and its possibilities. I was 100% sure that, if I am going to make my own keyboard it would be <strong>split and ergonomic</strong>. It just looks so cool to be able to have one part of the keyboard away from the other.</p>
<h3 id="some-split-keyboards-that-caught-my-attention">
Some split keyboards that caught my attention</h3>
<ul>
<li><a href="https://keeb.io/collections/frontpage/products/iris-keyboard-split-ergonomic-keyboard?variant=29480467267678">Iris Keyboard</a></li>
<li><a href="https://keeb.io/collections/frontpage/products/quefrency-60-65-split-staggered-keyboard?variant=16032981385310">Quefrency - 60%/65%</a></li>
<li><a href="https://www.ergodox.io/">ErgoDox</a></li>
</ul>
<p>I got to the point when I realized, that if I want to make a keyboard, I have to make a complete shopping list. My first go-to was the <em>Iris Keyboard</em> from <em>Keeb.io</em>. But then I’ve found the shipping cost and that changed my decision. I was looking into other options that were accessible in Europe and then I’ve found <a href="https://falba.tech/">FalbaTech</a>, which is based in Poland which made me so excited. There are really not that many shops which are based in Europe.</p>
<p>I took a look at his selection of custom keyboards and decided on the <strong>Redox keyboard</strong>. It is a split orthodox keyboard heavily inspired by <strong>Ergodox</strong>. There are two options for this keyboard: Classic wired and wireless. I’ve decided on a wired one as I didn’t want to complicate my first build.</p>
<p>If you want to take a closer look into the Redox keyboard and its details, I recommend reading the designer’s <a href="https://hackaday.io/project/160610-redox-keyboard">Hackaday.io page</a></p>
<h2 id="let">
Let’s make a shopping list</h2>
<p>You might think that making a custom mechanical keyboard can be cheap but the opposite is quite true. The electronics are the cheapest part and they are not that hard to get. You might find many of them in the nearest radio amateur shop. Parts that you have to reconsider and are most important are the PCB, case, switches, and keycaps.</p>
<p>For my build, as it is my first one, I had to also buy a soldering iron and various tools.</p>
<p>I decided that I want my custom case and my friend was so kind that he asked his brother to <strong>3D print</strong> it for me.</p>
<ul>
<li>
<p><a href="https://www.thingiverse.com/thing:2886662">3D printed case</a> - 32€</p>
<ul>
<li>I’ve paid only for materials but the overall cost is higher</li>
</ul>
</li>
<li>
<p><a href="https://falba.tech/product/redox-pcb-electrical-boards-set-of-2/">REDOX PCB Electrical Boards</a> - 20€</p>
</li>
<li>
<p><a href="https://www.banggood.com/70PCS-Pack-3Pin-Gateron-White-Switch-Keyboard-Switch-for-Mechanical-Gaming-Keyboard-p-1446975.html?rmmds=category&amp;cur_warehouse=CN">70pcs Pack 3Pin Gateron White Switch</a> - ~18€</p>
<ul>
<li>I have to say that these switches are VERY light and they are difficult to get used to.</li>
</ul>
</li>
<li>
<p><a href="https://www.banggood.com/104-Key-DSA-Profile-PBT-Blank-Keycaps-Key-Caps-Set-for-Mechanical-Keyboard-p-1287125.html?rmmds=myorder&amp;ID=520016&amp;cur_warehouse=CN">DSA Profile PBT Blank Keycaps</a> - ~23€</p>
</li>
<li>
<p><a href="https://www.banggood.com/Pro-Micro-5V-16M-Mini-Leonardo-Microcontroller-Development-Board-p-1077675.html?akmClientCountry=SK&amp;">2x Pro Micro ATmega32U4</a> - ~8€</p>
</li>
<li>
<p><a href="https://www.banggood.com/1M-Non-Waterproof-WS2812-WS2812B-RGB-30-LED-Strip-Light-Individually-Addressable-5V-p-1145875.html?rmmds=search&amp;ID=233&amp;cur_warehouse=CN">RGB LED Strip</a> - ~6€</p>
</li>
<li>
<p><a href="https://www.aliexpress.com/item/32912263133.html?spm=a2g0o.productlist.0.0.9d673fa24q3hHR&amp;algo_pvid=49d8606d-c49b-4211-bbc1-cd7667735e68&amp;algo_expid=49d8606d-c49b-4211-bbc1-cd7667735e68-0&amp;btsid=89610db5-e204-4d29-b6b9-8f2136fdeffd&amp;ws_ab_test=searchweb0_0,searchweb201602_3,searchweb201603_55">Momentary switches</a> - ~2€</p>
</li>
<li>
<p><a href="https://www.aliexpress.com/item/32368285821.html?spm=a2g0s.9042311.0.0.3dea4c4dDeY5X1">3.5MM TRRS Sockets</a> ~2€</p>
</li>
<li>
<p><a href="https://www.aliexpress.com/item/4000106882350.html?spm=a2g0o.productlist.0.0.40a839853cDhpo&amp;algo_pvid=507b146d-4ff4-4220-8f28-a5351fc63eef&amp;algo_expid=507b146d-4ff4-4220-8f28-a5351fc63eef-9&amp;btsid=0ab6fa8115849986217431938e8598&amp;ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_">3.5MM TRRS Jacks</a> ~2€</p>
</li>
<li>
<p><a href="https://www.banggood.com/100pcs-1N4148-Switching-Diode-Kit-DIY-Electronic-Component-Set-Straight-Pin-DO-35-p-1182125.html?rmmds=myorder&amp;cur_warehouse=CN">1N4148 Switching Diodes</a> - ~2€</p>
</li>
</ul>
<p>You will also need some cables so make sure that you will buy everything you need.</p>
<p>So this makes a total of ~115 € for only components of the keyboard itself and these are very cheap compared to the commercial market where you could pay this price for keycaps only.</p>
<p>I also had to buy some tools, so I could make this project a reality. There is a nice sum-up of tools you’ll need at <a href="https://docs.keeb.io/soldering-tools/">keeb.io</a>.</p>
<ul>
<li><a href="https://www.banggood.com/MUSTOOL-SD1-SD2-LCD-60W-Soldering-Station-Professional-PID-Soldering-Iron-Station-4-in-1-Tool-Kit-Adjustable-Temperature-200-480C-with-Solder-Wire-Holder-p-1590175.html?rmmds=myorder&amp;ID=471846280495&amp;cur_warehouse=CN">60W Soldering Station</a> - ~25€</li>
<li><a href="https://www.banggood.com/ESD-10-15-Safe-Anti-static-Tweezer-Maintenance-Repair-Nippers-Forceps-p-1102662.html?rmmds=myorder&amp;ID=514829&amp;cur_warehouse=CN">Anti-static Tweezers</a> - ~1€</li>
<li><a href="https://www.banggood.com/Universal-Keyboard-Key-Cap-Puller-for-Mechanical-Keyboard-p-1211430.html?rmmds=myorder&amp;cur_warehouse=CN">Keycap Puller</a> - ~4€</li>
<li><a href="https://www.banggood.com/Antistatic-Vacuum-Desoldering-Pump-Irons-Sucker-Removal-Remover-Tool-p-911889.html?rmmds=myorder&amp;ID=6284394&amp;cur_warehouse=CN">Solder sucker</a> - ~3€</li>
<li><a href="https://www.banggood.com/Pliers-Nipper-H-Practical-Electrical-Wire-Cable-Cutter-Cutting-Side-Snips-Flush-Pliers-Mini-Pliers-p-1371806.html?rmmds=myorder&amp;ID=3160&amp;cur_warehouse=CN">Flush Cutters</a> - ~3€</li>
<li><a href="https://www.banggood.com/6337-0_8mm-Tin-Lead-Rosin-Core-Flux-Solder-Soldering-Welding-Iron-Wire-Reel-p-992684.html?rmmds=myorder&amp;ID=6284394&amp;cur_warehouse=CN">Solder</a> - ~3€</li>
<li><a href="https://www.banggood.com/Hand-Soldering-Iron-Stand-Helping-Clamp-Magnifying-Tool-Auxiliary-Clip-Magnifier-Station-Holder-p-1017105.html?rmmds=myorder&amp;cur_warehouse=CN">Helping Hands</a> - ~10€</li>
</ul>
<p>For a total of ~49€ for tools needed.</p>
<h2 id="assembly-time">
Assembly time</h2>
<p>So I waited a few weeks for the goods to arrive home and after everything that was needed for the assembly came I’ve scheduled the assembly to be done over the weekend. The first weekend had come by and I was not feeling it, so I moved it to the next one when I was sure I wouldn’t be interrupted by anyone. It fit very well because I was soldering the keyboard over the night that was on a day of parliamentary elections. I knew that I was going to be up very late anyway.</p>
<h3 id="1--prepare-your-workplace">
1. Prepare your workplace</h3>
<p>Find a suitable room which has enough light and can be ventilated. One office desk should be enough for soldering.</p>
<p>So this is how the keyboard looks like when you pack it up into a shoe-box:</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/2020-03-23_20-24-13_625_1147x860.jpg 1x, /generated_images/images/uploads/2020-03-23_20-24-13_625_1720x1290.jpg 1.5x, /generated_images/images/uploads/2020-03-23_20-24-13_625_2294x1720.jpg 2x, /generated_images/images/uploads/2020-03-23_20-24-13_625_3441x2580.jpg 3x, /generated_images/images/uploads/2020-03-23_20-24-13_625_4000x2999.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/2020-03-23_20-24-13_625_1147x860.jpg"
    width="1147"
    height="860"
    alt="Keyboard parts stored in a shoe-box next to soldering station"
    
>
</picture>
<figcaption>
Keyboard parts stored in a shoe-box next to soldering station</figcaption></figure></p>
<h3 id="2--solder-switching-diodes">
2. Solder switching diodes</h3>
<p>What’s nice about keyboard assembly is that, when you are going to solder for the first time or like me the second time after 12 years, it starts from the simplest tasks to the hardest. In the end, it is simple all the way because after the first 70 soldered joints you will get into it and you will learn how the solder behaves.</p>
<p>PCBs of Redox are the same for both sides. That means you will put diodes on the opposite side of the PCB that it is going to be used for the second hand.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/2020-03-23_20-24-08_235_1147x860.jpg 1x, /generated_images/images/uploads/2020-03-23_20-24-08_235_1720x1290.jpg 1.5x, /generated_images/images/uploads/2020-03-23_20-24-08_235_2294x1720.jpg 2x, /generated_images/images/uploads/2020-03-23_20-24-08_235_3441x2580.jpg 3x, /generated_images/images/uploads/2020-03-23_20-24-08_235_4000x2999.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/2020-03-23_20-24-08_235_1147x860.jpg"
    width="1147"
    height="860"
    alt="PCB with soldered switching diodes"
    
>
</picture>
<figcaption>
PCB with soldered switching diodes</figcaption></figure></p>
<p>Remember that the <strong>direction of the diodes matters</strong> and that means the black part of the diode should be placed at the square on the PCB!!!</p>
<p>When you are done with soldering be careful while cutting the pins. They are going to fly across the whole room. Also, you should wear protective glasses if you are not able to point them to be shooting away from you. I was able to find some pins after 2 weeks in the corners of the room.</p>
<h3 id="3--solder-momentary-switches">
3. Solder momentary switches</h3>
<p>Momentary switches are used to send a reset signal to the Pro Micro. They are going to be essential for installing firmware into the Pro Micro.</p>
<h3 id="4--solder-trrs-sockets">
4. Solder TRRS sockets</h3>
<p>They will provide a way for connecting two sides together so you would only use one USB connection with a computer.</p>
<p>Also, if you decide to install RGB backlights, the coordinating signal is going to be wired through the socket pin as well.</p>
<h3 id="5--solder-the-header-pins-for-the-pro-micro">
5. Solder the header pins for the Pro Micro</h3>
<p>By header pins, I mean the small pins that are supposed to hold the Pro Micro above the PCB.</p>
<p>Don’t solder the Pro Micro yet! You won’t be able to put the switches onto the board.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/2020-03-23_20-24-06_393_1147x860.jpg 1x, /generated_images/images/uploads/2020-03-23_20-24-06_393_1720x1290.jpg 1.5x, /generated_images/images/uploads/2020-03-23_20-24-06_393_2294x1720.jpg 2x, /generated_images/images/uploads/2020-03-23_20-24-06_393_3441x2580.jpg 3x, /generated_images/images/uploads/2020-03-23_20-24-06_393_4000x2999.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/2020-03-23_20-24-06_393_1147x860.jpg"
    width="1147"
    height="860"
    alt="Header pins of Pro Micro soldered into the PCB"
    
>
</picture>
<figcaption>
Head pins of Pro Micro soldered into the PCB</figcaption></figure></p>
<p>At this point, you would notice that the USB port is going to be very low and not very tight on the left side. Please be aware of this. If you want to, you can also just wire some leftover pins from the diodes and make them shorter but I was doing OK with the header pins of the Pro Micro.</p>
<h3 id="6--mount-the-switches">
6. Mount the switches</h3>
<p>Try to put at least a few switches on the case and try mounting them on the board. The PCB has holes in it to mount the switches so it should fit together. Mine didn’t so I had to make a little hack on a few switches so they wouldn’t bend my board by cutting their bottoms.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/2020-03-23_20-24-15_387_645x860.jpg 1x, /generated_images/images/uploads/2020-03-23_20-24-15_387_967x1290.jpg 1.5x, /generated_images/images/uploads/2020-03-23_20-24-15_387_1290x1720.jpg 2x, /generated_images/images/uploads/2020-03-23_20-24-15_387_1935x2580.jpg 3x, /generated_images/images/uploads/2020-03-23_20-24-15_387_2580x3440.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/2020-03-23_20-24-15_387_645x860.jpg"
    width="645"
    height="860"
    alt="Cutting switch bottoms to be able to put them in the case"
    
>
</picture>
<figcaption>
Cutting switch bottoms to be able to put them in the case</figcaption></figure></p>
<h3 id="7--solder-the-switches">
7. Solder the switches</h3>
<p>All of the switches should have their feet on the other side of the board. Try to push them closer to the board so it will be easier for the solder to make a joint.</p>
<h3 id="8--solder-the-pro-micro">
8. Solder the Pro Micro</h3>
<p>Almost done with the solder. Now it’s time to wire the brains of the keyboard. These joints are going to be the best. There is not that much room so you’ll have to bring your A-game here.</p>
<p>To be honest, I had to revisit and try to fix both of my Pro Micros. One full column of my keyboard was not working because of one bad contact. I was trying to unmount the whole Pro Micro out but wasn’t able to suck the solder out so I desperately just tried it out again and it worked. I just needed to apply more heat to it. A few days later, my Enter key was not working and it was the same problem, but on the other side.</p>
<p>Before you proceed to the next step:</p>
<p>The USB ports on the Pro Micro are infamous to break when you manipulate a lot with them. I’ve decided that I want to have my right-hand side as a master and the USB port broke after 3 days. I was not able to burn a new firmware on it but I had to rewire the RGB backlight and I use my left-hand side as a master. On the left-hand side, the USB port is sitting between the board and Pro Micro so it should be harder to break. Be careful with them anyway!</p>
<h3 id="9--wire-rgb-backlight">
9. Wire RGB backlight</h3>
<p>Find some small cables that are flexible and wire them according to this: https://github.com/mattdibi/redox-keyboard/tree/master/redox#rgb-underglow</p>
<h2 id="burning-the-firmware">
Burning the firmware</h2>
<p>I’ll recommend getting to know QMK software. You can do so here: https://qmk.fm/</p>
<p>Check it’s abilities and <em>Getting Started</em> section</p>
<p>But to sum up, what I did was pretty simple:</p>
<ol>
<li><code>git clone https://github.com/qmk/qmk_firmware</code></li>
<li>Set the number of RGB diodes in <code>/keyboards/redox/keymaps/default/config.h</code></li>
<li>Made my own keyboard layout over: https://config.qmk.fm/#/redox/rev1/LAYOUT</li>
<li>Burn it <code>make redox/rev1:default:avrdude</code>
<ul>
<li>Of course, I’ve struggled a little bit with this part. I’ve ended up running this command with <code>sudo</code> as I wasn’t able to set some stuff described here: https://docs.qmk.fm/#/faq_build?id=can39t-program-on-linux</li>
</ul>
</li>
</ol>
<p>In case you want to check out my configuration you can <a href="/files/redox_keyboard_layout.json" title="Keyboard layout mappings for QMK configuration tool">download it here</a> and upload it into the configurator. I’ve added some multimedia keys in a third layout and much more.</p>
<p>And that’s it. You have built yourself a keyboard.</p>
<h2 id="feedback">
Feedback</h2>
<p>Well, I just made a big change in my experience with writing. I was learning to write with 10 fingers and if that wasn’t the biggest change I’ve made. I’ve decided that if I am going to learn to write properly I’ll do it even better. I’ve learned to use the <strong>Colemak keyboard layout</strong>. To enhance it to the best experience possible I’ve built myself an <strong>orthodox split keyboard</strong> and I must say: <strong><em>WORTH IT!</em></strong></p>
<p>So what are the little things that come with switching to an orthodox keyboard, you may ask? Well, it depends on your habits. Mine was certainly very bad as I didn’t know how to write with 10 fingers. Only 8 in my case. Also, I was used to typing H and Y keys with my left hand. But that was not the biggest problem. Most errors I’ve made was by trying to hit the <code>C</code> key. I was used to hitting it with my pointer finger which I think is not only my issue. With the classic keyboard design, hitting the <code>C</code> key with the middle finger seems like torture. After one month I am now able to properly hit any key with the correct finger. I just still do many mistakes because of the layout change.</p>
<p>I’ve chosen very light switches because I wanted them to be less noisy as I work in a shared office and also my ol’ <em>Cherry browns</em> seem a little too hard for me. I have to say this change was very hard to get used to. First 3 days I was accidentally pushing <code>oooooooooooo</code> key while trying to concentrate on which key I should press while learning new shortcuts. Simply put, I cannot rest my fingers on the keyboard as I’ve used to. But I think that they are not that bad after I got used to them. Sometimes I accidentally press <code>Backspace</code> just before hitting <code>Enter</code>. Therefore I’d definitely recommend getting heavier switches for function keys. If I was to make another keyboard, which I am definitely going to make, I will choose something in between. When I try to type on my old keyboard I feel like I have to do push-ups with my fingers. But I know it is just because I got used to these light ones.</p>
<p><figure>
<picture>
    <source
    srcset="/generated_images/images/uploads/2020-03-23_20-24-18-981_1147x860.jpg 1x, /generated_images/images/uploads/2020-03-23_20-24-18-981_1720x1290.jpg 1.5x, /generated_images/images/uploads/2020-03-23_20-24-18-981_2294x1720.jpg 2x, /generated_images/images/uploads/2020-03-23_20-24-18-981_3441x2580.jpg 3x, /generated_images/images/uploads/2020-03-23_20-24-18-981_4000x2999.jpg 4x"
    type="image/jpeg"
>
    <img
    src="/generated_images/images/uploads/2020-03-23_20-24-18-981_1147x860.jpg"
    width="1147"
    height="860"
    alt="Workspace setup at work"
    
>
</picture>
<figcaption>
Workspace setup at work</figcaption></figure></p>
<p>And this is what my setup looks like at work.</p>
]]></content:encoded></item><item><title>How I&apos;ve built my website</title><link>https://michalvanko.dev/blog/2020-02-28-how-ive-built-my-web-site</link><description><![CDATA[<p>Creating my own website with blog was something I had in my mind from start of my professional career after I left school.
I had a lot of new experience with development which I wanted to elaborate on and save into a small library so I can take a look back on my thoughts how they evolve over time.</p>
]]></description><enclosure url="/images/uploads/DSC01202.jpg" length="" type="image/jpeg"/><guid>https://michalvanko.dev/blog/2020-02-28-how-ive-built-my-web-site</guid><pubDate>Fri, 28 Feb 2020 16:00:55 +0000</pubDate><content:encoded><![CDATA[<h2 id="motivation">
Motivation</h2>
<p>Creating my own website with blog was something I had in my mind from start of my professional career after I left school.
I had a lot of new experience with development which I wanted to elaborate on and save into a small library so I can take a look back on my thoughts how they evolve over time.
This was like 6 years ago. I had a successful first attempt at doing so.</p>
<p>I created a WordPress with the simplest theme I’ve found and wrote some articles. I’ve published it under a domain of one of the first startups I’ve been part of. I still have a backup of the <em>Wordpress</em> database somewhere so I can export those articles here when I will feel like doing so. The blog haven’t lived for long as the domain once expired and I was not satisfied with it enough to deploy it somewhere else.</p>
<p>For all those years I was trying to create it in my spare time (of which wasn’t that much apparently). There were several attempts. One with <em>Angular</em> when it was “the cool kid on the block”. Another one with <em>cycle.js</em> which was not that far from being done. I regret it now as it would be really satisfying to finish that one. I had created neat <abbr title="Server side rendering">SSR</abbr> layer which was not really difficult to accomplish with <em>cycle.js</em> as it is reactive and it only required skipping first client render of <em>virtual-dom</em> after page load. I’m still in love with <em>cycle.js</em> but after <em>sapper</em> was released I’ve found out of its ability to create a nice <strong>static site</strong> I wanted to try it out. I think that the approach of <strong>compiling the source code</strong> as classic client applications have been doing for many years makes a lot of sense on the internet as well. This is the one thing I’d really like to be able to accomplish with reactive frameworks like <em>cycle.js</em>.</p>
<h2 id="how-i-got-my-domain">
How I got my domain</h2>
<p>There was the big boom of new available generic top-level domains released around 2013. More than 1000 new gTLDs have been made available. After that I didn’t want a local <em>(.sk)</em> or any common <em>(.com)</em> domain for my personal site. I wanted <strong>.dev</strong> from the first time I saw it. It was very unfortunate decision for me, because the <em>.dev</em> domain was bought entirely by <em>Google</em> and was not available for public until 28th of February 2019. When I found out that this day will come, I was so excited that I’d set up an alarm for it. I was among the first who bought the domain right away after few minutes it was available.</p>
<p>The <strong>michalvanko.dev</strong> domain celebrates 1 year with the release of this blog post.</p>
<h2 id="build-setup">
Build setup</h2>
<p>So I had a domain with no content published on it for few months. But I knew that I’d use it at least for my <strong>portfolio</strong> page. I was not rushing myself into building it right away. But the time came when had to do it as I wanted to change my employment as I was not satisfied with the one at that time.</p>
<p>While I was choosing technology stack I was making a decision between static site generators as I wanted to make it as less resource dependent as possible. This lead me to the <em>sapper</em> framework as it supports building (exporting) static assets with <strong>efficiency</strong>. At first I’ve wrote the portfolio just in <em>HTML</em> without need of any <abbr title="Content management system">CMS</abbr>. I wanted to build it really fast and deploy it with some automated pipeline just like <abbr title="Javascript, APIs, Markup">JAM</abbr> applications should be.</p>
<p>I’ve chosen to deploy it on <em>Amazon’s S3</em> as I have some experience with it and I knew that it won’t cost me anything. I had a 1 year free tier still available for my account even that it was 6 years old as I haven’t set up any payments yet. <em>Amazon</em> made this neat tool called <a href="https://aws.amazon.com/amplify/"><em>amplify</em></a> which suits very well for my needs. It supports <strong>git based deployment</strong> so I can setup my site to build whenever I create or edit any article and it will be published right away with purged cache. I just had to setup configuration of build step with <em>sapper</em> so the exported site is correctly uploaded to <em>S3</em> and that was it.</p>
<p>Build configuration:</p>
<pre><code class="language-yaml"><pre style="background-color:#2b303b;">
<span style="color:#bf616a;">version</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">0.1
</span><span style="color:#bf616a;">frontend</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">phases</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">    </span><span style="color:#bf616a;">preBuild</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">      </span><span style="color:#bf616a;">commands</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">        - </span><span style="color:#a3be8c;">npm install
</span><span style="color:#c0c5ce;">    </span><span style="color:#bf616a;">build</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">      </span><span style="color:#bf616a;">commands</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">        - </span><span style="color:#a3be8c;">npm run export
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">artifacts</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">    </span><span style="color:#65737e;"># IMPORTANT - Please verify your build output directory
</span><span style="color:#c0c5ce;">    </span><span style="color:#bf616a;">baseDirectory</span><span style="color:#c0c5ce;">: </span><span style="color:#a3be8c;">__sapper__/export
</span><span style="color:#c0c5ce;">    </span><span style="color:#bf616a;">files</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">      - &#39;</span><span style="color:#a3be8c;">**/*</span><span style="color:#c0c5ce;">&#39;
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">cache</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">    </span><span style="color:#bf616a;">paths</span><span style="color:#c0c5ce;">:
</span><span style="color:#c0c5ce;">      - </span><span style="color:#a3be8c;">node_modules/**/*
</span></pre>
</code></pre>
<p>Mapping <em>amplified</em> app to my domain was easy as well. I didn’t even needed to leave <em>amplify</em> console. They’ve built the setup of the domain right into it so I just had to copy the settings for verification into my domain name registrar and in few minutes it was all synced and done.</p>
<h2 id="development-experience">
Development experience</h2>
<p>So when I had deployed a basic portfolio site I was able to find a new job with completely different terms which made me happier as I am much more independent. I’ve started to build the blog with the features that <em>sapper</em> offers. I was doing some research around some <abbr title="Content management system">CMS</abbr> systems and tried some of them out. One that deserves the mention is <a href="https://strapi.io/"><em>strapi</em></a>. It was really cool as I could create models for anything and if I was building an application which was not static I’d use it. Now I can only recommend you to look at it. I was looking for something like it for 5 years and it finally looks like there are now more options. While I am writing this article I have to mention <a href="https://www.keystonejs.com/"><em>Keystone.js</em></a> as one of those libraries that I was looking at 5 years ago and it looked promising but just wasn’t 100% compatible with my requirements. At the end I just grabbed the most simple tool I’ve found and that is <a href="https://www.netlifycms.org/"><em>Netlify CMS</em></a>. I was skeptic about it as one colleague had some experience with it and he was not enjoying it. But when I’ve tried it out and set it up for my needs it just met all of the requirements I have and it integrates with the deploy pipeline very well.</p>
<p>I’ve created some articles for development with <a href="https://jaspervdj.be/lorem-markdownum/">Lorem Markdownum</a> so I can debug styles for every type of content without hassle.
Then I had to finally meet the power of the <em>sapper</em> framework. I had to adapt my thoughts (find better word) about how to create an <abbr title="Application Programmer Interface">API</abbr> so it can be exported into static assets. One thing that makes me mad about it to this day is <a href="https://www.typescriptlang.org/"><em>Typescript</em></a> support. As I was writing back-end there was no help for node <abbr title="Application Programmer Interface">API</abbr> which I got so used to from my other projects I work on. I’ve definitely lost some unnecessary minutes debugging why some variables are not something I thought they should be.</p>
<p>I also had some trouble with finding library for parsing article content. I was looking at the <a href="https://github.com/netlify-templates/gatsby-starter-netlify-cms"><em>netlify-gatsby</em> starter template</a> and the name for the package is just not clearly searchable on the internet. At least I was struggling with it because search results for markdown parsing didn’t contain or support anything for files of the type that <em>netlify CMS</em> produces. But after some searching I found the library as <a href="https://github.com/jxson/front-matter"><em>front-matter</em></a>. It turned out that I didn’t know what I was searching for. When you read the documentation of it then it will make sense to you why you couldn’t find it out. I needed an <em>YAML</em> parser. After that when I got the files parsed I just parsed markdown body with the with the most popular <a href="https://marked.js.org">markdown parser <em>marked</em></a> which has really nice options.</p>
<h2 id="netlify-cms">
Netlify CMS</h2>
<p>I didn’t have any experience with <a href="https://www.netlifycms.org/"><em>Netlify CMS</em></a>. Setup was very straight forward.</p>
<ol>
<li>Create a static <em>HTML</em> page with one script tag.</li>
<li>Setup <em>YAML</em> configuration file with git repository mapping, models and input types.</li>
</ol>
<p>That’s it. Sometimes I’ve changed some properties in my models, but I haven’t struggled with it.</p>
<p>For example this is the model of this blog post:</p>
<pre><code class="language-yaml"><pre style="background-color:#2b303b;">
<span style="color:#c0c5ce;">- </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">blog</span><span style="color:#c0c5ce;">&#39; </span><span style="color:#65737e;"># Used in routes, e.g., /admin/collections/blog
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Blog</span><span style="color:#c0c5ce;">&#39; </span><span style="color:#65737e;"># Used in the UI
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">folder</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">_posts/blog</span><span style="color:#c0c5ce;">&#39; </span><span style="color:#65737e;"># The path to the folder where the documents are stored
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">create</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">true </span><span style="color:#65737e;"># Allow users to create new documents in this collection
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">slug</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">{{year}}-{{month}}-{{day}}-{{slug}}</span><span style="color:#c0c5ce;">&#39; </span><span style="color:#65737e;"># Filename template, e.g., YYYY-MM-DD-title.md
</span><span style="color:#c0c5ce;">  </span><span style="color:#bf616a;">fields</span><span style="color:#c0c5ce;">: </span><span style="color:#65737e;"># The fields for each document, usually in front matter
</span><span style="color:#c0c5ce;">    - { </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Layout</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">layout</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">widget</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">hidden</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">default</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">blog</span><span style="color:#c0c5ce;">&#39; }
</span><span style="color:#c0c5ce;">    - { </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Title</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">title</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">widget</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">string</span><span style="color:#c0c5ce;">&#39; }
</span><span style="color:#c0c5ce;">    - {
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Published</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">published</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">widget</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">boolean</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">default</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">true</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">      }
</span><span style="color:#c0c5ce;">    - { </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Publish Date</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">date</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">widget</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">datetime</span><span style="color:#c0c5ce;">&#39; }
</span><span style="color:#c0c5ce;">    - {
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Featured Image</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">thumbnail</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">widget</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">image</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">required</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">false</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">      }
</span><span style="color:#c0c5ce;">    - {
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Tags</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">tags</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">widget</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">list</span><span style="color:#c0c5ce;">&#39;,
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">default</span><span style="color:#c0c5ce;">: [&#39;</span><span style="color:#a3be8c;">News</span><span style="color:#c0c5ce;">&#39;],
</span><span style="color:#c0c5ce;">        </span><span style="color:#bf616a;">required</span><span style="color:#c0c5ce;">: </span><span style="color:#d08770;">false</span><span style="color:#c0c5ce;">,
</span><span style="color:#c0c5ce;">      }
</span><span style="color:#c0c5ce;">    - { </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Body</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">body</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">widget</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">markdown</span><span style="color:#c0c5ce;">&#39; }
</span><span style="color:#c0c5ce;">    - { </span><span style="color:#bf616a;">label</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">Writers notes</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">name</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">notes</span><span style="color:#c0c5ce;">&#39;, </span><span style="color:#bf616a;">widget</span><span style="color:#c0c5ce;">: &#39;</span><span style="color:#a3be8c;">markdown</span><span style="color:#c0c5ce;">&#39; }
</span></pre>
</code></pre>
<p>Neat part of the <em>CMS</em> are those widgets. In editor they will be presented by appropriate component as well as in the editor preview.
I am very satisfied with it and I recommend it.</p>
<h2 id="whats-next">
What’s next</h2>
<p>I’ve decided not to wait for perfect product and I want to release this blog as soon as possible. I will have same approach as with other products. Make a <abbr title="Minimum Viable Product"><strong>MVP</strong></abbr> and then release features as they are done.
I’ve put some features into <em>Github Projects Board</em>. I will very likely make a redesign with experimental layout changes. I’d like to experiment with colors and make the blog look distinguishable and personal while maintaining accessibility.</p>
<p>There are also some things I want to do with <a href="https://svelte.dev"><em>Svelte</em></a>. I haven’t tried it’s transition and animation modules. I’d definitely like to add some page transitions and animate menus.</p>
]]></content:encoded></item></channel></rss>