<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daan Debie</title>
    <description>The latest articles on DEV Community by Daan Debie (@dandydev).</description>
    <link>https://dev.to/dandydev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F604894%2Fb1b382b4-b296-4e77-be56-37bbd4c7af8e.jpeg</url>
      <title>DEV Community: Daan Debie</title>
      <link>https://dev.to/dandydev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dandydev"/>
    <language>en</language>
    <item>
      <title>Static Duck Typing in Python with Protocols</title>
      <dc:creator>Daan Debie</dc:creator>
      <pubDate>Sat, 27 Nov 2021 16:55:23 +0000</pubDate>
      <link>https://dev.to/dandydev/static-duck-typing-in-python-with-protocols-1ghk</link>
      <guid>https://dev.to/dandydev/static-duck-typing-in-python-with-protocols-1ghk</guid>
      <description>&lt;p&gt;At &lt;a href="https://source.ag/"&gt;Source&lt;/a&gt;, we write most of our code in Python. It's a language that both our Software Engineers and Data Scientists are equally at home in. It's easy to be productive in Python, in part due to its dynamic nature. Not having to think too much about the types of your variables and functions, can make it easier to experiment, especially if you're not entirely clear yet on how you're going to solve a particular problem.&lt;/p&gt;

&lt;p&gt;When moving our code to production however, we want to have more guarantees about the behaviour of our code. Writing (unit) tests is one way to get those guarantees, but we also make heavy use of &lt;a href="https://www.python.org/dev/peps/pep-0484/"&gt;type hints&lt;/a&gt; to give us more confidence in our code. Type hints can also provide a productivity boost, because not only humans can reason better about type hinted code, your editor can as well!&lt;/p&gt;

&lt;p&gt;Sometimes though, using type hints everywhere can feel like you're losing out on a lot of the magic and speed that a dynamic type system brings you. One particular trait of dynamic typing that is pretty idiomatic in Python, is &lt;strong&gt;duck typing&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duck Typing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Duck_typing"&gt;Duck typing&lt;/a&gt; is a philosophy in programming where you care more about the behaviour and properties of an object than its stated type to determine if that object is useful in a certain situation. Duck typing is inspired by the &lt;em&gt;duck test&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If it walks like a duck and it quacks like a duck, then it must be a duck&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice this means that when you write a function that receives a certain input, you care only about the behaviour and/or attributes of that input, not the explicit &lt;em&gt;type&lt;/em&gt; of that input.&lt;/p&gt;

&lt;p&gt;One interesting question that arises is: if you don't want to be strict about the type of the parameters a function receives, are there still any &lt;em&gt;static type guarantees&lt;/em&gt; to be had?&lt;/p&gt;

&lt;p&gt;And the other way around is interesting as well: if you have a function with statically typed inputs, can you &lt;em&gt;loosen up&lt;/em&gt; those parameters to make the function more universally useful, the way duck typing does?&lt;/p&gt;

&lt;p&gt;As it turns out, Python provides a neat way to have our cake and eat it too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Protocols to the Rescue
&lt;/h2&gt;

&lt;p&gt;When reviewing some code recently, I came across a function that looked roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_windowed_avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;measurements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TemperatureMeasurement&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HumidityMeasurement&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
        &lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;window_upper_bound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;measurements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;
    &lt;span class="n"&gt;current_window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;window_averages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;measurements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# various calculations happen here
&lt;/span&gt;        &lt;span class="c1"&gt;# based on the timestamp of each measurement
&lt;/span&gt;        &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;window_averages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal of this function is to calculate the average of a certain field (identified by &lt;code&gt;field_name&lt;/code&gt;) in a rolling window. At the time of writing this function, we were using it for &lt;code&gt;TemperatureMeasurement&lt;/code&gt; and &lt;code&gt;HumidityMeasurement&lt;/code&gt;, but it is very likely we'll want to use it for different types of measurements in the future.&lt;/p&gt;

&lt;p&gt;If we look closely at how the function uses the input, it turns out that the only thing we want to be guaranteed off, is that the items we pass into the function have a &lt;code&gt;timestamp&lt;/code&gt; field. So instead of specifying each different type that has adheres to this contract, we'd like to tell the type checker that we only care about having a &lt;code&gt;timestamp&lt;/code&gt; field to work with.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Protocol&lt;/code&gt; from the &lt;code&gt;typing&lt;/code&gt; module lets us do that. Just like with duck typing, &lt;strong&gt;Protocols&lt;/strong&gt; let you specify the behaviour or attributes you expect, without caring about the type. Here is what that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MeasurementLike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_windowed_avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;measurements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MeasurementLike&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;window_upper_bound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;measurements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the type checker doesn't know &lt;em&gt;exactly&lt;/em&gt; what the type is of whatever is provided as &lt;code&gt;measurements&lt;/code&gt; but it &lt;em&gt;does&lt;/em&gt; know what those items have a &lt;code&gt;timestamp&lt;/code&gt; field because they adhere to the &lt;code&gt;MeasurementLike&lt;/code&gt; Protocol.&lt;/p&gt;

&lt;p&gt;In a sense, a Protocol acts like one side of an &lt;em&gt;Interface&lt;/em&gt; as we know it from Java or Typescript. Instead of having to specify the behaviour and properties both on a &lt;em&gt;type&lt;/em&gt; and on the functions that use it, we only have to specify it on a function, without caring about the types of the objects that are provided to the function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protocol and Generics
&lt;/h2&gt;

&lt;p&gt;You can also use Protocols together with &lt;code&gt;TypeVar&lt;/code&gt; for even more generic functions that are still type checked to some extend. One use-case that comes to mind, is when you don't care about the input type to a function, as long as it follows a protocol, but you also want to guarantee that the &lt;em&gt;output&lt;/em&gt; of the function is of the same type as the &lt;em&gt;input&lt;/em&gt;, no matter what the exact type is.&lt;/p&gt;

&lt;p&gt;This works as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MeasurementLike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'M'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MeasurementLike&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;measurement_as_timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;measurement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;measurement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;measurement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;astimezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;measurement&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we create a function that takes any object that has a &lt;code&gt;timestamp&lt;/code&gt; field and guarantees that the output will be of the same type as the input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Protocols in Python provide a nice way to use duck typing while still having some static type guarantees. You can define contracts for your functions without caring too much about the actual types of your inputs.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Update on 2021-11-23: There was a wrong type annotation in this article, as &lt;a href="https://news.ycombinator.com/item?id=29315363"&gt;pointed out by &lt;strong&gt;ragebol&lt;/strong&gt; on Hacker News&lt;/a&gt;, which is now fixed&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>typing</category>
      <category>protocols</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Add An RSS Feed to a NextJS Blog</title>
      <dc:creator>Daan Debie</dc:creator>
      <pubDate>Sun, 17 Oct 2021 17:45:41 +0000</pubDate>
      <link>https://dev.to/dandydev/how-to-add-an-rss-feed-to-a-nextjs-blog-4phg</link>
      <guid>https://dev.to/dandydev/how-to-add-an-rss-feed-to-a-nextjs-blog-4phg</guid>
      <description>&lt;p&gt;Today, I released a new version of my website with only one new feature: &lt;a href="https://en.wikipedia.org/wiki/Web_syndication"&gt;syndication&lt;/a&gt; through RSS, Atom and JSON Feed. Even though &lt;a href="https://techcrunch.com/2018/04/07/rss-is-undead/"&gt;by some accounts&lt;/a&gt;, RSS &lt;a href="https://mjtsai.com/blog/2019/12/26/apple-news-no-longer-supports-rss/"&gt;seems to be dead&lt;/a&gt;, I strongly believe RSS is an important feature in the fight to keep ownership over your own content while also increasing exposure. The way I approach publishing and sharing my content, is called &lt;a href="https://indieweb.org/POSSE"&gt;POSSE&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Publish (on your) Own Site, Syndicate Elsewhere&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;RSS is an important part of that strategy.&lt;/p&gt;

&lt;p&gt;Luckily, I'm not the only one who values RSS. &lt;a href="https://news.ycombinator.com/item?id=20813021"&gt;There&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=23212812"&gt;are&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=26014344"&gt;others&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article I want to share how I implemented syndication feeds in my &lt;a href="https://nextjs.org/"&gt;NextJS-powered&lt;/a&gt; website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Options and Requirements
&lt;/h2&gt;

&lt;p&gt;I did some Googling to see how other NextJS users were generating RSS feeds, and there turned out to be many people that had solved this particular problem &lt;a href="https://www.google.com/search?q=rss+feed+nextjs"&gt;and wrote about it&lt;/a&gt; I found a couple of different approaches to generating and rendering RSS feeds in NextJS:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generating feeds&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hand-build the required XML using templated strings 🤢&lt;/li&gt;
&lt;li&gt;Use a library to do it for you. The most popular library for this in the JS ecosystem seems to be &lt;a href="https://www.npmjs.com/package/feed"&gt;&lt;em&gt;feed&lt;/em&gt;&lt;/a&gt; 🤩&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rendering feeds&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering"&gt;Server-side rendering&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create dynamic feeds (more on that later) that can be different for each visitor&lt;/li&gt;
&lt;li&gt;Using NextJS pages to represent feeds feels natural in the NextJS way of doing things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can't generate the website statically anymore (at least parts of it remain dynamic), which reduces performance
As a side note: from a development perspective, server-side rendered pages and statically generatedpages in NextJS are so similar that the difference doesn't matter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Use &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation"&gt;Static generation&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because the feeds are generated at build time, the site remains snappy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You cannot set the &lt;code&gt;Content-Type&lt;/code&gt; header for statically generated pages, so you can't serve those pages as &lt;code&gt;application/rss+xml&lt;/code&gt;. I'm not how big of a problem this is and what black magic &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt; applies when serving my NextJS site&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Picking the requirements
&lt;/h3&gt;

&lt;p&gt;After looking at the options, I decided on the following requirements for my feeds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I want to go for statically generated feeds, to keep my site fast and the implementation simple&lt;/li&gt;
&lt;li&gt;Support for &lt;a href="https://en.wikipedia.org/wiki/RSS"&gt;RSS&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Atom_(Web_standard)"&gt;Atom&lt;/a&gt; and &lt;a href="https://www.jsonfeed.org/"&gt;JSON Feed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;I want to include the &lt;strong&gt;complete blog post contents&lt;/strong&gt; in the feeds. This is an important one because I personally really hate it when my RSS reader only shows me a summary of a post and I have to go the the website to read all of it. Caveat though: my website is built using MDX, so I might include components in the future that are not easily convertible to static HTML without Javascript enabled. In that case, readers will have to click through to my site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;As per my requirements, I wanted to generate the feeds at build time. But as mentioned before, NextJS doesn't support setting the &lt;code&gt;Content-Type&lt;/code&gt; header for statically generated pages. The alternative that many people use, is to have a separate script generate your feeds and writing them to the public folder where all other static assets such as images are stored as well. That way, the feeds would be served as static assets instead of statically generated pages -- which, from the browsers perspective doesn't make a difference!&lt;/p&gt;

&lt;p&gt;I found a &lt;a href="https://ashleemboyer.com/how-i-added-an-rss-feed-to-my-nextjs-site"&gt;good explanation by Ashlee Boyer&lt;/a&gt; of this technique.&lt;/p&gt;

&lt;p&gt;My plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write script to generate feeds, using the &lt;em&gt;feed&lt;/em&gt; library from NPM&lt;/li&gt;
&lt;li&gt;Run this script as a &lt;code&gt;postbuild&lt;/code&gt; step so it would always be invoked after building the site using &lt;code&gt;npm run build&lt;/code&gt; (this happens not only locally, but also when Vercel deploys my site)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Problem 1: running Typescript modules is hard
&lt;/h3&gt;

&lt;p&gt;I immediately hit a snag with (1), because I couldn't manage to use ES6/Typescript modules in a script run outside of my normal website code.&lt;/p&gt;

&lt;p&gt;I'm using Typescript, and apparently &lt;code&gt;ts-node&lt;/code&gt;, the tool to run Typescript files, doesn't support modules. Writing the script in Javascript wasn't really an option for me because I wanted to reuse a lot of logic that I already wrote for reading and parsing MDX files in the website itself.&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution
&lt;/h4&gt;

&lt;p&gt;I decided to follow the route that Ashlee Boyer suggests in her blog post and sneak in the function to generate my feeds as a "stowaway" in the &lt;code&gt;getStaticProps&lt;/code&gt; function of my index page. This works beautifully!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetStaticProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;generateMainFeeds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getAllPostsFrontMatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;lastPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem 2: including the full content in the feeds
&lt;/h3&gt;

&lt;p&gt;The code of my website already supported translating MDX files into valid JSX to be rendered by React. But how to generate valid HTML from that content and include it in the feeds?&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution
&lt;/h4&gt;

&lt;p&gt;I couldn't find many examples of this, but did find out about &lt;a href="https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup"&gt;&lt;code&gt;ReactDOMServer.renderToStaticMarkup&lt;/code&gt;&lt;/a&gt;. This function will take a bunch of React components and render them into HTML. This is what is used by many React server-side rendering solutions &lt;em&gt;(maybe also by NextJS?)&lt;/em&gt; and works perfectly here as well.&lt;/p&gt;

&lt;p&gt;One caveat: if your content contains internal links -- which are often relative links -- then you have to be mindful that those relative links are meaningless in the context of an RSS feed. The way I solved this is by doing some regex-based replacements on the generated HTML.&lt;/p&gt;

&lt;p&gt;The complete content generation part looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontMatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;htmlContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ReactDOMServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderToStaticMarkup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChakraProvider&lt;/span&gt; &lt;span class="nx"&gt;resetCSS&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MDXRemote&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mdxSource&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;MDXComponents&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ChakraProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/href="&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;#/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;#`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/href="&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/src="&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`src="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem 3: getting rid of style information
&lt;/h3&gt;

&lt;p&gt;My site uses &lt;a href="https://chakra-ui.com/"&gt;Chakra UI&lt;/a&gt; for theming, which uses &lt;a href="https://emotion.sh/"&gt;Emotion&lt;/a&gt; -- a CSS-in-JS library -- under the hood. Emotion will happily render tons of &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags when statically generating HTML from your React components. For most use cases where you render React on the server (statically or not), this is desirable. In the case of RSS/Atom feeds, this is pretty useless.&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution
&lt;/h4&gt;

&lt;p&gt;The solution here is to strip all the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags from the generated HTML. Rather than summoning &lt;a href="https://stackoverflow.com/a/1732454/872397"&gt;&lt;em&gt;The One whose Name cannot be expressed in the Basic Multilingual Plane&lt;/em&gt;&lt;/a&gt; by trying to use regex here, I found &lt;a href="https://www.npmjs.com/package/string-strip-html"&gt;this library&lt;/a&gt; to help me with this task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanHtmlContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stripHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;onlyStripTags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;stripTogetherWithTheirContents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The end result
&lt;/h2&gt;

&lt;p&gt;I now have serve &lt;a href="///feeds/feed.xml"&gt;RSS&lt;/a&gt;, &lt;a href="///feeds/atom.xml"&gt;Atom&lt;/a&gt; and a &lt;a href="///feeds/feed.json"&gt;JSON Feed&lt;/a&gt; for your reading pleasure. Most of the relevant code &lt;a href="https://github.com/DandyDev/daan.fyi/blob/main/lib/feeds.tsx"&gt;can be found here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Future plans
&lt;/h2&gt;

&lt;p&gt;At some point I want to diversify my writing output by not only writing about tech. And even within the topic of tech there are many sub-topics I could write about, not all of which are equally interesting to every reader (all 5 of them, including my mom 👩‍👦). I'm planning to introduce &lt;a href="https://github.com/DandyDev/daan.fyi/issues/3"&gt;tags&lt;/a&gt; to allow filtering content once I have enough of it.&lt;/p&gt;

&lt;p&gt;Once I have tags, I would like to start supporting dynamic feeds so readers can subscribe only to the stuff they actually want to read. I imagine building an endpoint like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/feeds/by-tags.xml?tags=tag1,tag2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm curious if others are doing this!&lt;/p&gt;

</description>
      <category>rss</category>
      <category>typescript</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Use Tools that Suit You and the Problem</title>
      <dc:creator>Daan Debie</dc:creator>
      <pubDate>Sun, 12 Sep 2021 18:48:29 +0000</pubDate>
      <link>https://dev.to/dandydev/use-tools-that-suit-you-and-the-problem-1ldd</link>
      <guid>https://dev.to/dandydev/use-tools-that-suit-you-and-the-problem-1ldd</guid>
      <description>&lt;p&gt;A couple of weeks ago someone shared &lt;a href="https://alexander-hansen.dev/blog/benefits-of-not-using-an-ide"&gt;an article&lt;/a&gt; on Hacker News that discussed the drawbacks of using an IDE for programming. Needless to say, when you touch people's tools, you can be sure to &lt;a href="https://news.ycombinator.com/item?id=28257116"&gt;spark a hefty discussion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(To be fair, the author did clarify that they're not against IDE's &lt;em&gt;in general&lt;/em&gt; and they actually use one on a daily basis.)&lt;/p&gt;

&lt;p&gt;When reading the discussion, one of the first things I wondered is if there are places on the internet where carpenters or plumbers furiously criticize each other's choice of tools. But &lt;em&gt;even that&lt;/em&gt; has &lt;a href="https://news.ycombinator.com/item?id=28257382"&gt;a place on Hacker News&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post I want to explore the fundamental problems with this type of discussion and how you can embrace freedom of choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's all about context
&lt;/h2&gt;

&lt;p&gt;When discussing someone else's choice of tools -- in this case the choice of editor or IDE -- it's good to be mindful of what we &lt;em&gt;don't know&lt;/em&gt; about this person and their reasons for choosing certain tools. Aside from personal preference and/or familiarity, there can be lots of extra factors to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We don't know what kind of problems they're working on&lt;/li&gt;
&lt;li&gt;We don't know the context in which they work (e.g. security requirements laid down by the company they work for, available budget)&lt;/li&gt;
&lt;li&gt;We don't know their level of experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think it's good form to voice an opinion only when we can see the whole picture. I also think that people should be encouraged to &lt;strong&gt;choose the right tool for the job&lt;/strong&gt;. And to put it simply: &lt;strong&gt;the right tool for the job is the one that fits the problem and makes you the most productive&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's also about definition
&lt;/h2&gt;

&lt;p&gt;Another issue with this kind of discussion is that everyone has a different idea of what really constitutes an "IDE". We're inclined to frame the choice of editor as a binary choice -- "pure text editor" vs "IDE", almost like a false dichotomy -- while it really is a spectrum. On this spectrum you'll not only find different editors, but even different configurations of the same editor with varying degrees of "smartness" applied through plugins.&lt;/p&gt;

&lt;p&gt;So when someone says &lt;em&gt;"why use an IDE when you can do everything an IDE does in VIM if you install the following 20 plugins"&lt;/em&gt;, is this really so different from an actual IDE (that was branded as such)?&lt;/p&gt;

&lt;h2&gt;
  
  
  Philosophies in tool choices
&lt;/h2&gt;

&lt;p&gt;When looking at how people setup their development environments, I basically see 2 different approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;"additive"&lt;/em&gt; approach: this basically follows the UNIX philosophy of composing many different tools that each do one thing well. It often involves a terminal-based editor like VIM, optionally with some plugins and supported by external tools like grep, sed and awk that can help with refactoring code. You basically stack tools until you have an "integrated" development environment&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;"subtractive"&lt;/em&gt; approach: you take one "big" editor as the basis of your stack. This is often an editor that is branded as an IDE (e.g. Eclipse, Jetbrains editors). Then you take away/replace ("subtract") the parts that work better through dedicated tools (according to personal preference of course)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To me, both approaches have their merits and uses!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create freedom of choice?
&lt;/h2&gt;

&lt;p&gt;I think the most important question around editors is not&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How to choose the right editor?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;but rather&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How to enable each member of my team to choose the editor that makes them &lt;strong&gt;most productive&lt;/strong&gt;, while enabling &lt;strong&gt;effective collaboration&lt;/strong&gt;?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's my recipe to do that:&lt;/p&gt;

&lt;p&gt;My basic starting point in teams: &lt;em&gt;everyone can use the editor/IDE of their choice&lt;/em&gt;. To enable effective collaboration, the following rules must be followed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Editor configuration is never checked into version control, unless it's generic and &lt;a href="https://editorconfig.org/"&gt;applicable across editors&lt;/a&gt;. Examples of generic editor configuration are: line endings, tabs vs spaces, max line length etc. There is some overlap with linters here&lt;/li&gt;
&lt;li&gt;Enforce common code style through external tools (i.e. linters, code formatters)&lt;/li&gt;
&lt;li&gt;Everyone should be able to build and run a project, regardless of the choice of editor, preferably through the terminal. This means that we'll always prefer external tools for this. Many languages provide tooling for this:

&lt;ul&gt;
&lt;li&gt;Java → Maven/Gradle&lt;/li&gt;
&lt;li&gt;JS/TS → NPM&lt;/li&gt;
&lt;li&gt;Go → Go&lt;/li&gt;
&lt;li&gt;Rust → Cargo&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Additionally, building and running code can be made easier and better reproducible by using Makefiles and/or Docker (combined with Docker Compose)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have to admit that in some ways I also limit choice within my teams: building and running projects often involves shell scripts which are portable between Linux and MacOS, but not to Windows. This can be solved by using WSL on Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is my setup
&lt;/h2&gt;

&lt;p&gt;My personal setup involves quite a lot of tools.&lt;/p&gt;

&lt;p&gt;For any "serious" development work (i.e. long stretches of coding and big projects) I take the aforementioned "subtractive" approach. My basis is one of the Jetbrains tools, depending on the language(s) I'm working with. I find the Jetbrains tools make &lt;em&gt;me&lt;/em&gt; really productive and their &lt;a href="https://www.jetbrains.com/all/"&gt;&lt;em&gt;All products pack&lt;/em&gt;&lt;/a&gt; offers great value for money, especially with their huge loyalty discounts after 1 or more years.&lt;/p&gt;

&lt;p&gt;I still use the terminal and external tools for many things in this case. I put quite some time in improving and maintaining &lt;a href="https://github.com/DandyDev/dotfiles"&gt;my dotfiles&lt;/a&gt; as well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I prefer to use the terminal for: building, linting, running tests, using git&lt;/li&gt;
&lt;li&gt;I prefer to use the IDE for: writing and editing code, refactoring, searching for code, find/replace through regex&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For quick and short edits and viewing files where speed is preferred and conveniences like syntax highlighting and intellisense are not important, I use Sublime Text or VIM. Example of this: editing my dotfiles.&lt;/p&gt;

&lt;p&gt;Sometimes, but rarely, I use VSCodium for editing or viewing files which are not part of a project and which I want to reformat/reindent. This doesn't work well for me in Sublime Text. For example: viewing/editing minified JSON files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;To wrap this post up, I just want to reiterate: use whatever tools make you productive and help you solve the problems you're trying to solve and please let others do the same.&lt;/p&gt;

&lt;p&gt;And here's some additional closing advice: should you ever grow unhappy with the tools you're using, just write a blog post extolling the virtues of your current tools. You can be sure people on the internet will tell you how much better their tools are, thereby providing you with potential alternatives to your current setup 😇&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks to &lt;a href="https://twitter.com/djvdorp"&gt;Dan&lt;/a&gt; for proofreading this&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>ide</category>
      <category>editors</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Pitfalls of Deploying a NextJS Frontend on AWS Amplify</title>
      <dc:creator>Daan Debie</dc:creator>
      <pubDate>Sun, 29 Aug 2021 11:06:14 +0000</pubDate>
      <link>https://dev.to/dandydev/the-pitfalls-of-deploying-a-nextjs-frontend-on-aws-amplify-1bn9</link>
      <guid>https://dev.to/dandydev/the-pitfalls-of-deploying-a-nextjs-frontend-on-aws-amplify-1bn9</guid>
      <description>&lt;p&gt;Have you ever gone above and beyond to give a product a fair chance, only giving up way past your own sanity and patience? I did. The product was &lt;a href="https://aws.amazon.com/amplify/"&gt;AWS Amplify&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's build something with NextJS
&lt;/h2&gt;

&lt;p&gt;When I started developing the primary (web) UI for the product we're building at &lt;a href="//https:/source.ag"&gt;Source&lt;/a&gt;, I quickly decided to use the &lt;a href="https://nextjs.org/"&gt;NextJS&lt;/a&gt; framework as the basis for the frontend. One thing I really like about NextJS is that it does &lt;a href="https://nextjs.org/docs/basic-features/pages"&gt;filesystem-based routing&lt;/a&gt;. Even though I like to diss on PHP as much as the next person, this particular feature of NextJS reminds me a bit of PHP &lt;em&gt;in a good way&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Another very compelling feature of NextJS, is the ability to have different rendering strategies for each different page. You can decide to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/docs/basic-features/pages#static-generation-recommended"&gt;Static generation&lt;/a&gt; for pages and content that rarely change. These pages get pre-rendered at build time and can be aggressively cached on a CDN. These pages can of-course still be highly dynamic by using client-side data fetching methods&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/docs/basic-features/pages#server-side-rendering"&gt;Server-side rendering&lt;/a&gt; for pages that need to return different content on each request and/or for each different user&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration"&gt;Incremental static regeneration&lt;/a&gt;, which is a variation on Static generation that will cache pages for a period you specify and then regenerate the pages server-side when they're considered stale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;This particular feature requires a specific hosting setup that supports the different &lt;em&gt;flavours&lt;/em&gt; of rendering. You basically want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a good CDN to cache and serve your static pages&lt;/li&gt;
&lt;li&gt;a server-side solution to serve the server-side rendered pages and facilitate incremental static regeneration. This should also take care of &lt;a href="https://nextjs.org/docs/api-routes/introduction"&gt;API routes&lt;/a&gt;, should you use them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The NextJS documentation &lt;a href="https://nextjs.org/docs/deployment"&gt;mentions a couple of options&lt;/a&gt; for deployment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt;. Vercel is the company behind NextJS. They offer a seamless experience where you can use any rendering method, and Vercel will deploy each part of your app in the right way. One neat thing they do is that they'll convert your server-side rendered pages and API routes into serverless unctions that they'll deploy to edge locations all over the globe.&lt;/li&gt;
&lt;li&gt;Setup a NodeJS server somewhere for the server-side stuff and use a CDN manually to serve the static content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At Source however, we've made a conscious decision to use AWS for all of our infrastructure needs. We're too early in the lifecycle of both our product and company to consider a hybrid cloud setup and AWS offers all the features that we need for prices that are reasonable &lt;em&gt;for now&lt;/em&gt;. Through one of our investors we also got a boat-load of AWS credits to get started, which helps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amplify
&lt;/h2&gt;

&lt;p&gt;I didn't want to manually set up a hybrid deployment with NodeJS and CDN - which I could probably do using Cloudfront and ECS - so I looked for alternatives. AWS Amplify claims to be the &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fastest, easiest way to build mobile and web apps that scale&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And they &lt;a href="https://docs.amplify.aws/guides/hosting/nextjs/q/platform/js/"&gt;claim to support NextJS&lt;/a&gt; as well as &lt;a href="https://aws.amazon.com/blogs/mobile/host-a-next-js-ssr-app-with-real-time-data-on-aws-amplify/"&gt;server-side rendering&lt;/a&gt; by virtue of &lt;a href="https://aws.amazon.com/lambda/edge/"&gt;Lambda@Edge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sounds great! So how does that all work?&lt;/p&gt;

&lt;h2&gt;
  
  
  Amplify - the setup
&lt;/h2&gt;

&lt;p&gt;The idea behind Amplify is that it lets you focus on building your app without having to worry about the infrastructure. This means that it will not only let you build and deploy your frontend easily, it will also let you hook-up backend components like &lt;a href="https://docs.amplify.aws/lib/auth/getting-started/q/platform/js/"&gt;authentication&lt;/a&gt; through &lt;a href="https://aws.amazon.com/cognito/"&gt;Cognito&lt;/a&gt;, a &lt;a href="https://docs.amplify.aws/lib/graphqlapi/getting-started/q/platform/js/"&gt;GraphQL API&lt;/a&gt; using &lt;a href="https://aws.amazon.com/appsync/"&gt;AppSync&lt;/a&gt; and a &lt;a href="https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js/"&gt;DataStore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The way Amplify manages all of this, is through the idea of "backends". A backend contains one or more of the aforementioned components. You can setup different backend environments in Amplify and couple these to different (git) branches of your frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amplify - the developer experience
&lt;/h2&gt;

&lt;p&gt;We are bringing our own database and API and wanted to use a pre-existing Cognito user pool. Amplify lets you import an existing Cognito user pool instead of creating one for you, and this worked fine... until I tried to create a second "backend environment" with a different Cognito user pool.&lt;/p&gt;

&lt;p&gt;I want to allow different users on our development environment than our production environment. These environments correspond to the &lt;code&gt;main&lt;/code&gt; branch in Git (production) and any other branches in Git (development).&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I tie an Amplify backend environment to a frontend branch?
&lt;/h3&gt;

&lt;p&gt;Interaction with Amplify is done through the web console or the&lt;br&gt;
&lt;a href="https://docs.amplify.aws/cli/"&gt;Amplify CLI&lt;/a&gt;. According to the documentation, we can create a new Amplify backend environment like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify env add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But nowhere is it made clear how this is linked to our frontend branch. The Amplify CLI tries to mimic the &lt;code&gt;git&lt;/code&gt; CLI by having &lt;code&gt;amplify push&lt;/code&gt; and &lt;code&gt;amplify pull&lt;/code&gt;. So I assume we need to &lt;code&gt;push&lt;/code&gt; our new environment to the cloud. Does this command depend on the git branch I'm on? I don't know.&lt;br&gt;
I could just as easily overwrite my existing backend environment when using this command.&lt;/p&gt;

&lt;p&gt;Eventually I was able to create a &lt;code&gt;main&lt;/code&gt; environment and a &lt;code&gt;dev&lt;/code&gt; environment, but no matter how hard I tried, I could not get those environments to use different Cognito user pools.&lt;/p&gt;
&lt;h3&gt;
  
  
  How do new team members setup an existing project?
&lt;/h3&gt;

&lt;p&gt;The Amplify CLI will create some configuration files, some of which you need to commit to git, and some of them you need to ignore. These files should make the build reproducible. It is unclear however, what a new developer needs to do to be able to run an existing Amplify project.&lt;br&gt;
The documentation mentions multiple Amplify CLI commands to setup a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify configure project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify env checkout &amp;lt;my-env&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;To be run in arbitrary order?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/60365208/how-to-import-existing-aws-amplify-back-end-into-an-empty-aws-amplify-project-lo"&gt;Three&lt;/a&gt;&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/60951924/how-do-i-continue-working-with-amplify-on-a-new-machine"&gt;similar&lt;/a&gt;&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/61104135/how-to-run-an-existing-aws-amplify-project"&gt;questions&lt;/a&gt;&lt;br&gt;
on StackOverflow give 3 different answers on this topic.&lt;/p&gt;

&lt;p&gt;When I was on-boarding new team-members, we basically tried some commands in different order until we were able to run the project locally for that engineer.&lt;/p&gt;

&lt;h4&gt;
  
  
  How do I figure out why my build failed?
&lt;/h4&gt;

&lt;p&gt;In short: &lt;a href="https://github.com/aws-amplify/amplify-console/issues/2065"&gt;you don't&lt;/a&gt;. Or you enlist the help of AWS Support (which you will have to pay for). The Amplify web console doesn't share&lt;br&gt;
detailed build logs, so you'll have to fix your problems by trial and error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amplify - the bugs
&lt;/h2&gt;

&lt;p&gt;AWS Amplify seems to have quite some bugs. For example: when you use Gitlab, you cannot put the source code of your project &lt;a href="https://github.com/aws-amplify/amplify-console/issues/1941"&gt;in a sub-group&lt;/a&gt;. This will silently fail your build, with no clear error message in sight.&lt;/p&gt;

&lt;p&gt;The aforementioned problem of using multiple Cognito user pools for different environments of one Amplify frontend, I also consider a bug.&lt;/p&gt;

&lt;p&gt;Not having access to the &lt;em&gt;actual&lt;/em&gt; build logs, I also consider a bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amplify - no help included
&lt;/h2&gt;

&lt;p&gt;The most painful issue I have with AWS Amplify - and AWS in general - is the complete lack of support. You are paying a lot of money for their services, but that does not come with any form of&lt;br&gt;
support, unless you pay extra. And when you make use of that paid support, it goes something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Contact AWS Business support through their website and try to explain your problem in a plain
text field that you cannot resize or use formatting in&lt;/li&gt;
&lt;li&gt;Wait far longer than the promised 4 hours&lt;/li&gt;
&lt;li&gt;Get a response from someone telling you they'll forward it to "the team" (why not have someone
respond who can actually help you with your problem?)&lt;/li&gt;
&lt;li&gt;Get a canned response with a solution akin to "turn it off and on again"&lt;/li&gt;
&lt;li&gt;Tell them you have an &lt;em&gt;actual problem&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Get forwarded to the next person who you have to clue in again on what your problem is&lt;/li&gt;
&lt;li&gt;Go to 2 and repeat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means that in practice, AWS Amplify is &lt;em&gt;not usable&lt;/em&gt; unless you are willing to pay extra for support and have a lot of patience.&lt;/p&gt;

&lt;p&gt;As a side note, I find it really frustrating that with big corporations like Amazon in general, you quickly feel helpless and ignored when you have problems. Stories about Google customer support (or lack thereof) are rampant on Hacker News, and I think Amazon is no different. You'd think that with so much revenue they should be able to set up actual proper customer support 🙄&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern with AWS
&lt;/h2&gt;

&lt;p&gt;I think that these problems with Amplify are endemic of a larger problem with AWS: &lt;strong&gt;their PaaS and SaaS solutions suck&lt;/strong&gt;. Aside from pricing (which is quite high compared to competitors like&lt;br&gt;
Digital Ocean), I think AWS' infrastructure-as-a-service offerings are fine. If you want to have a bunch of servers (EC2), a database here and there (RDS) and some serverless functions (Lambda), AWS&lt;br&gt;
will serve you just fine.&lt;/p&gt;

&lt;p&gt;But if you want something on top of that, like authentication (Cognito), or a PaaS solution like Amplify, be prepared for a sub-par offering compared to the competition. I think Amazon should&lt;br&gt;
focus on strengthening their core offerings instead of offering everything and the kitchen sink.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;In the end, I dropped Amplify and went with Vercel. $20/user/month is a small price to pay for actually being able to work on the product instead of fighting AWS services and support.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>amplify</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
