<?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: Daniel Bartholomae</title>
    <description>The latest articles on DEV Community by Daniel Bartholomae (@the_startup_cto).</description>
    <link>https://dev.to/the_startup_cto</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%2F438887%2F55737feb-4004-40a9-9bc9-b456b0c981c1.png</url>
      <title>DEV Community: Daniel Bartholomae</title>
      <link>https://dev.to/the_startup_cto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/the_startup_cto"/>
    <language>en</language>
    <item>
      <title>Easily identify technical debt in any repository</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Tue, 07 Mar 2023 15:23:11 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/easily-identify-technical-debt-in-any-repository-59bg</link>
      <guid>https://dev.to/the_startup_cto/easily-identify-technical-debt-in-any-repository-59bg</guid>
      <description>&lt;p&gt;Technical debt is a term used a lot whenever a codebase is not to our liking, and there's never enough time to fix all of it. What actually is technical debt, how do you quickly prioritize what to work on - and what are these GitHub Blocks that people are talking about?&lt;/p&gt;

&lt;p&gt;If you are interested in more articles and news about web product development and entrepreneurship, please feel free&lt;br&gt;
to subscribe to new articles &lt;a href="https://newsletter.startup-cto.net"&gt;via email&lt;/a&gt;, &lt;a href="https://startup-cto.net/rss.xml"&gt;via RSS&lt;/a&gt;, or to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is technical debt?
&lt;/h2&gt;

&lt;p&gt;"Technical debt" is often used as a term for basically any kind of code we don't like, be it difficult to understand or just written by someone who since left the company.&lt;br&gt;
And often, it is considered as something to avoid at all cost.&lt;/p&gt;

&lt;p&gt;But that's not what the term was meant to be originally, and neither how it is most useful. So let's start with a &lt;a href="https://en.wikipedia.org/wiki/Technical_debt"&gt;definition from Wikipedia&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Technical debt is the implied cost of future reworking required when choosing an easy but limited solution instead of a better approach that could take more time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So unless there was an &lt;em&gt;explicit choice&lt;/em&gt; to take an &lt;em&gt;easier route&lt;/em&gt;, it's not technical debt, it is &lt;em&gt;accidental complexity&lt;/em&gt;.&lt;br&gt;
Technical debt, on the other side, is a tool that can be used to move work into the future: Instead of taking the time-consuming path now, we take an easy route, knowing full well that it might lead to more work in the future.&lt;/p&gt;

&lt;p&gt;A typical example is around scalability: Especially at a startup, we often build features to attract users, and while we hope for many of them, we only have maybe a handful in the beginning.&lt;/p&gt;

&lt;p&gt;This also means that, most of the time, technical debt is a good thing: We don't know whether or how a feature will be used, before it actually is used, so if we can push work into the future, we might not even need to do it at all.&lt;br&gt;
And we can still fix the feature to be more scalable once this is actually needed (or, ideally, once we know we will need it to be scalable, but right before it actually is needed).&lt;/p&gt;

&lt;p&gt;Technical debt can run us into trouble in two ways, though:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If we are in an organization that is willing to take the trade-off, but then can't manage the cost of cleaning up when needed.&lt;/li&gt;
&lt;li&gt;If the cost of fixing the problem later massively out-scales the cost of fixing it from the get-go.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first one can be reduced by having explicit timelines, e.g. building a feature and then, 3 months later, either removing it again if it isn't used, or cleaning it up otherwise.&lt;/p&gt;

&lt;p&gt;The second one can be avoided by writing down the expected costs explicitly and think them through to make an explicit trade-off. It's less common, though, and I've seen way more situations where it was used as an argument against technical debt even though technical debt would have been the better choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what about accidental complexity?
&lt;/h2&gt;

&lt;p&gt;In reality, the term "technical debt" is often used for accidental complexity. So let's define that as well (this time my one as there is no Wikipedia article):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;accidental complexity is any complexity in the code that is not needed to model actual complexity in the business domain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice, accidental complexity mainly comes from the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not understanding the domain well enough, e.g. adding features and special cases that are never or almost never used by our users&lt;/li&gt;
&lt;li&gt;Not understanding the technology we use well enough, e.g. writing complex custom solutions for a problem that could be solved more easily with features already in the framework we use&lt;/li&gt;
&lt;li&gt;Trying to appear smart by writing complex code, e.g. to make oneself less replaceable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of that have an implicit continuation of "and not fixing the code once we learn more (about the domain/the technology/...)".&lt;/p&gt;

&lt;h2&gt;
  
  
  So how to fix technical debt?
&lt;/h2&gt;

&lt;p&gt;Once it is in place, technical debt is indistinguishable from accidental complexity, as the terms differentiate between how both get created, not what they actually are.&lt;br&gt;
This means that the following is true both for fixing technical debt and reducing accidental complexity.&lt;/p&gt;

&lt;p&gt;Fixing the complexity usually is obvious: The reason why we identify something as technical debt usually is that we have an idea of a "better" way to do it.&lt;br&gt;
Instead, the hard part is to know where to start, as reducing complexity will take a lot of time, and we usually don't have enough time to reduce all of it at once.&lt;/p&gt;

&lt;p&gt;So what should we focus on first? We need to find out&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;which files have the most accidental complexity, and&lt;/li&gt;
&lt;li&gt;which ones are most likely to need to be changed in the near future.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For complexity, there are many metrics, e.g. cyclomatic complexity, Halstead, or simply counting lines of code.&lt;br&gt;
Fortunately, &lt;a href="https://arxiv.org/pdf/1408.4523.pdf"&gt;they are all strongly correlated&lt;/a&gt;, so for practical purposes, looking at&lt;br&gt;
lines of code is often enough. In practice, these are also closely correlated with file size (which is roughly proportional to number of lines times average line length).&lt;/p&gt;

&lt;p&gt;Figuring out which files are most likely to be changed is more of a communication exercise: We can talk to management, product managers, and&lt;br&gt;
other team members to figure out which services are most likely needed for upcoming projects. Or we can just look at the service that we need to work on next.&lt;/p&gt;

&lt;p&gt;How to identify the most critical files inside a service? Thanks to git, we have a lot of data at our fingertips.&lt;br&gt;
The easiest way to predict which files will need to be changed, is to look at the files that were changed most recently.&lt;br&gt;
We can do this by counting how many commits touched a file.&lt;/p&gt;

&lt;p&gt;Also, not all files are equal. E.g. a lock-file might be big and changing often, but since it is autogenerated,&lt;br&gt;
there is not much complexity.&lt;/p&gt;

&lt;p&gt;Fortunately, we don't need to do all of this ourselves. There's a GitHub block for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interlude: What are GitHub blocks?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://blocks.githubnext.com/"&gt;GitHub Blocks&lt;/a&gt; is an update to the way we can interact with repositories.&lt;br&gt;
It allows to write custom "blocks" which get data about either a file or a folder and can display this information,&lt;br&gt;
enriched with other information e.g. from the GitHub API, any way we want.&lt;br&gt;
As of Q1 2023, it is in technical preview.&lt;/p&gt;

&lt;p&gt;This is potentially a game changer for GitHub, as it allows them to basically outsource the creation of a great UI.&lt;br&gt;
For developers, it opens up a full new world of interacting with their code. I'll write a short blog post about this soon,&lt;br&gt;
so if you want to know more, feel free to subscribe &lt;a href="https://newsletter.startup-cto.net"&gt;via email&lt;/a&gt;, &lt;a href="https://startup-cto.net/rss.xml"&gt;via RSS&lt;/a&gt;,&lt;br&gt;
or to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The technical debt block
&lt;/h2&gt;

&lt;p&gt;The first thing I thought about when I read about GitHub blocks was a way to identify technical debt in a folder:&lt;br&gt;
The &lt;a href="https://github.com/startup-cto/technical-debt-block"&gt;Technical Debt Block&lt;/a&gt; was born.&lt;/p&gt;

&lt;p&gt;If you have access to GitHub Blocks, you can use it to view a folder and get a list of files in that folder,&lt;br&gt;
sorted by technical debt as described above. Only thing that remains to do is to actually clean the top files.&lt;/p&gt;

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

&lt;p&gt;We talked about technical debt, which is born out of a conscious trade-off decision and usually is a good thing,&lt;br&gt;
as well as accidental complexity, which usually isn't. We looked at how to best prioritize which to clean with help of&lt;br&gt;
information readily available in Git, and a GitHub Block that helps to visualize this.&lt;/p&gt;

&lt;p&gt;Hit me up on &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;Twitter&lt;/a&gt;:&lt;br&gt;
How do you prioritize technical debt? And which files did you find with this method that surprised you the most?&lt;/p&gt;

</description>
      <category>github</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Rewriting my blog from scratch with NextJS</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Fri, 30 Dec 2022 15:14:03 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/rewriting-my-blog-from-scratch-with-nextjs-3b67</link>
      <guid>https://dev.to/the_startup_cto/rewriting-my-blog-from-scratch-with-nextjs-3b67</guid>
      <description>&lt;p&gt;"Writing a blog" is usually taken as "writing articles for a blog".&lt;br&gt;
I did this for a while, using &lt;a href="https://ghost.org/"&gt;Ghost&lt;/a&gt; to host the blog.&lt;br&gt;
And it mostly worked, but there were multiple reasons for me to change it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have an actually running open-source application, both for experimenting, and for my CV&lt;/li&gt;
&lt;li&gt;Try out &lt;a href="https://nextjs.org/"&gt;NextJS&lt;/a&gt;, &lt;a href="https://rushjs.io/"&gt;rush&lt;/a&gt;, and other technologies&lt;/li&gt;
&lt;li&gt;Be able to automate whatever I want around the blog &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I thought would be a project of maybe a month, turned out to take me almost a full year.&lt;br&gt;
Here's what I did and what I learned on the way.&lt;/p&gt;

&lt;p&gt;If you are interested in more articles and news about web product development and entrepreneurship, please feel free&lt;br&gt;
to subscribe to new articles &lt;a href="https://newsletter.startup-cto.net"&gt;via email&lt;/a&gt;, &lt;a href="https://startup-cto.net/rss.xml"&gt;via RSS&lt;/a&gt;, or to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;When building the blog to the current state, I ran through the following phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting up the blog in NextJS&lt;/li&gt;
&lt;li&gt;Adding an RSS feed&lt;/li&gt;
&lt;li&gt;Hosting on Amplify&lt;/li&gt;
&lt;li&gt;Starting with analytics&lt;/li&gt;
&lt;li&gt;Moving to a monorepo&lt;/li&gt;
&lt;li&gt;Using OIDC to work with AWS&lt;/li&gt;
&lt;li&gt;Publishing to dev.to&lt;/li&gt;
&lt;li&gt;The current structure&lt;/li&gt;
&lt;li&gt;What's next&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to follow along in code, this blog is open-source now, so you can roam through&lt;br&gt;
its &lt;a href="https://github.com/startup-cto/blog/commits/main?after=5046de405d32df6bc9c2036ce5aa26c3e7acc43e+454&amp;amp;branch=main&amp;amp;qualified_name=refs%2Fheads%2Fmain"&gt;commit history&lt;/a&gt;&lt;br&gt;
and follow along. I will mention relevant commits in each chapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the blog in NextJS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/startup-cto/blog/commits/main?after=5046de405d32df6bc9c2036ce5aa26c3e7acc43e+454&amp;amp;branch=main&amp;amp;qualified_name=refs%2Fheads%2Fmain"&gt;Commits start here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I decided to rewrite my blog, one of the reasons was that I wanted to learn more about &lt;a href="https://nextjs.org/"&gt;NextJS&lt;/a&gt;.&lt;br&gt;
It had already gotten some traction then, and seemed like a good fit for a blog. It was a bit overkill compared&lt;br&gt;
to other solutions like &lt;a href="https://www.gatsbyjs.com/"&gt;Gatsby&lt;/a&gt;, but I liked the idea of learning something&lt;br&gt;
that would allow me to quickly create even more complicated prototypes thanks to &lt;a href="https://nextjs.org/docs/api-routes/introduction"&gt;API routes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I decided quite quickly to store blog posts in markdown-format with yaml front matter for meta-data.&lt;br&gt;
On the one hand, this made migrating from Ghost easy, as my blog posts where already stored there as markdown files.&lt;br&gt;
On the other hand, this also allowed me to store all relevant information in one file.&lt;/p&gt;

&lt;p&gt;What didn't become obvious immediately was that storing the slug inside the file instead of using the filename was beneficial.&lt;br&gt;
This creates an additional burden, as I need to make sure by hand that I don't use the same slug twice, but it allows me to potentially change the file name I use without losing SEO juice for the existing slug.&lt;/p&gt;

&lt;p&gt;The folder structure inside NestJS also evolved over time, mainly due to more extensive use of &lt;a href="https://storybook.js"&gt;Storybook&lt;/a&gt; for &lt;a href="https://startup-cto.net/tdd-in-a-react-frontend/"&gt;visual tdd&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;content&lt;/code&gt; contains the markdown content for the blog articles&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;public&lt;/code&gt; contains additional assets like images&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/pages&lt;/code&gt; has the NextJS pages, but tries to be as lean as possible&lt;/li&gt;
&lt;li&gt;It uses functions from &lt;code&gt;src/loading-posts&lt;/code&gt; for server-side rendering, and&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/presentation/templates&lt;/code&gt; for the actual pages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/presentation&lt;/code&gt; in general contains the &lt;a href="https://main--63ae2da33e662a5a8af367b9.chromatic.com/"&gt;full component library&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding an RSS feed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/startup-cto/blog/commits/main?before=5046de405d32df6bc9c2036ce5aa26c3e7acc43e+385&amp;amp;branch=main&amp;amp;qualified_name=refs%2Fheads%2Fmain"&gt;Commits start with &lt;code&gt;chore: add empty script file to create RSS feed&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm a big fan of RSS feeds and use them to stay up-to-date on many blogs and sites, so offering an RSS feed myself was a hard requirement for the migration.&lt;br&gt;
Fortunately, Ghost has an open-source, MIT-licensed &lt;a href="https://github.com/TryGhost/Ghost/blob/85db1838d9234778edaae78ef29384cb2f7b67b8/ghost/core/core/frontend/services/rss/generate-feed.js"&gt;implementation of creating an RSS xml file&lt;/a&gt; that I could look at to ensure that my implementation does not break anything for people who might already be following via RSS.&lt;br&gt;
The actual code turned out to be &lt;a href="https://github.com/startup-cto/blog/blob/main/blog/scripts/rss/createRSSFeed.ts"&gt;quite simple&lt;/a&gt;, but it took me some time to understand, which elements where needed and why.&lt;br&gt;
This way, I also learned about the &lt;code&gt;&amp;lt;link rel="alternate" type="application/rss+xml" title="The Startup CTO" href="https://startup-cto.net/rss.xml"&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hosting on Amplify
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/startup-cto/blog/commits/main?before=5046de405d32df6bc9c2036ce5aa26c3e7acc43e+315&amp;amp;branch=main&amp;amp;qualified_name=refs%2Fheads%2Fmain"&gt;Commits start with &lt;code&gt;chore: add amplify config to repo&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I first started, I tried hosting the blog on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;.&lt;br&gt;
Unfortunately, I didn't find a way to make this work with a custom domain that was not hosted on a subdomain, as GitHub Pages requires the use of a CNAME record, and these can't be set on a root domain like &lt;code&gt;startup-cto.net&lt;/code&gt;.&lt;br&gt;
I could have decided for a subdomain like &lt;code&gt;www.startup-cto.net&lt;/code&gt;, but this would have required an additional redirect or rewrite solution, or put me in danger of losing all my existing SEO juice for &lt;code&gt;startup-cto.net&lt;/code&gt;.&lt;br&gt;
I had anyway thought about trying out &lt;a href="https://aws.amazon.com/amplify/"&gt;AWS Amplify&lt;/a&gt; at that point, so that's what I did.&lt;/p&gt;

&lt;p&gt;The hosting itself went surprisingly smoothly.&lt;br&gt;
I needed to tweak the &lt;a href="https://github.com/startup-cto/blog/blob/main/amplify.yml"&gt;&lt;code&gt;amplify.yml&lt;/code&gt; config file&lt;/a&gt; a bit, especially once I moved my repository to be a monorepo, but apart from needing to build twice, it just worked.&lt;br&gt;
I decided against running tests in Amplify, and instead to run the tests in GitHub, to separate CI and CD a bit and keep my development workflow in one place - hoping that I won't need to debug why a run fails on Amplify even though it succeeded on GitHub any time soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with analytics
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/startup-cto/blog/commits/main?before=5046de405d32df6bc9c2036ce5aa26c3e7acc43e+245&amp;amp;branch=main&amp;amp;qualified_name=refs%2Fheads%2Fmain"&gt;Commits start with &lt;code&gt;chore: add first experimental CDK web analytics setup&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was definitively one of the most interesting parts of my journey.&lt;br&gt;
I'm a big fan of building for the user, and for blog posts, the best way to understand the user - which is you, the reader - is to use analytics.&lt;br&gt;
I did not want to collect any unnecessary data, though.&lt;br&gt;
Most importantly, I did not want to help others on collecting unnecessary data.&lt;/p&gt;

&lt;p&gt;At this point, I looked into some existing analytics solutions like &lt;a href="https://analytics.google.com/"&gt;Google Analytics&lt;/a&gt; and &lt;a href="https://plausible.io/"&gt;Plausible&lt;/a&gt;, but they either collected unnecessary information, or were costing money.&lt;br&gt;
I could have self-hosted some of them, but given the little traffic I had so far, all of these options would have been significantly more expensive than what I went with now:&lt;br&gt;
Writing my own little serverless analytics tool.&lt;br&gt;
The main reason for this was to learn more about DynamoDB, though.&lt;/p&gt;

&lt;p&gt;The architecture behind my little analytics tool is quite simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An API gateway offers access to two endpoints:

&lt;ul&gt;
&lt;li&gt;One to collect an analytics event, consisting of a path and potentially utm parameters&lt;/li&gt;
&lt;li&gt;Another one to retrieve the events from the last month or so&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;A DynamoDB to save the events&lt;/li&gt;
&lt;li&gt;Some frontend code to &lt;a href="https://github.com/startup-cto/blog/blob/main/blog/src/analytics/useTrackPageView.ts"&gt;collect analytics events&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://startup-cto.net/analytics/"&gt;very simple page&lt;/a&gt; that shows me ugly graphs with the data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the infrastructure is defined via &lt;a href="https://aws.amazon.com/cdk/"&gt;AWS CDK&lt;/a&gt;, a library on top of &lt;a href="https://aws.amazon.com/cloudformation/"&gt;CloudFormation&lt;/a&gt;.&lt;br&gt;
The code is now in &lt;a href="https://github.com/startup-cto/blog/tree/main/analytics"&gt;its own project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most of the code for the backend lives in &lt;a href="https://github.com/startup-cto/blog/blob/main/analytics/src/constructs/WebAnalytics/WebAnalytics.ts"&gt;its own CDK construct&lt;/a&gt;.&lt;br&gt;
The first interesting realization was that I can easily link handlers to infrastructure code &lt;a href="https://github.com/startup-cto/blog/blob/main/analytics/src/constructs/WebAnalytics/WebAnalytics.ts#L50"&gt;via environmental variables&lt;/a&gt;, e.g. the table name, but also key names for DynamoDB.&lt;/p&gt;

&lt;p&gt;I then realized that this is even truer for validation logic.&lt;br&gt;
This way, &lt;a href="https://github.com/startup-cto/blog/blob/main/analytics/src/constructs/WebAnalytics/model/AnalyticsEventInput.ts"&gt;a single file&lt;/a&gt; can define validation logic that is used &lt;a href="https://github.com/startup-cto/blog/blob/main/analytics/src/constructs/WebAnalytics/WebAnalytics.ts#L92"&gt;in the ApiGateway validator&lt;/a&gt; as well as &lt;a href="https://github.com/startup-cto/blog/blob/main/analytics/src/constructs/WebAnalytics/WebAnalytics.CollectEventHandler.ts#L17"&gt;in the handler itself&lt;/a&gt;.&lt;br&gt;
It's a bit unnecessary to duplicate it in this case, but the performance hit is small enough for me to keep it in just to show the principle.&lt;/p&gt;

&lt;p&gt;Speaking of performance:&lt;br&gt;
I also &lt;a href="https://github.com/startup-cto/blog/blob/main/analytics/src/constructs/WebAnalytics/handler/loadEventsByMonth.ts#L30"&gt;validate the data I load from the database&lt;/a&gt;.&lt;br&gt;
I'm not sure whether I will keep this, as it does create a performance hit.&lt;br&gt;
The performance hit was significantly greater, though, when I accidentally &lt;a href="https://github.com/startup-cto/blog/commit/924c69a6a232161293e3cc90c5756fdc3fee4dcf"&gt;recompiled the validation schema on each validation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One last interesting aspect here is the DynamoDB setup with hash and sort keys.&lt;br&gt;
A NoSQL database, to be honest, isn't the best choice for a database structure for an analytics solution like this one.&lt;br&gt;
Yes, analytics databases get more write than read requests, and NoSQL databases can be better suited to writing a lot of document-like data than SQL databases.&lt;br&gt;
But, honestly, my blog getting enough hits for this to matter is far from likely.&lt;br&gt;
On the other hand, querying the data based on different combinations of paths and utm parameters is not a strength of NoSQL databases.&lt;/p&gt;

&lt;p&gt;Other systems either use &lt;a href="https://github.com/plausible/hosting/blob/master/docker-compose.yml#L7"&gt;SQL databases&lt;/a&gt; for this purpose, or multi-step pipelines that use different tools for storing and &lt;a href="https://aws.amazon.com/athena/"&gt;querying&lt;/a&gt; the data.&lt;br&gt;
I didn't want to use either solution, on the one hand, because I didn't find free solutions for low traffic, and on the other the hand because I anyway wanted to experiment a bit with DynamoDB.&lt;/p&gt;

&lt;p&gt;Specifically, DynamoDB can only be queried by fixed hash keys and parts of sort keys.&lt;br&gt;
They need to be designed with the intended queries in mind.&lt;br&gt;
In our case, we intend to query by date and maybe by path or utm parameters.&lt;br&gt;
The number of different paths will be growing slowly (very slowly, at the rate I currently write blog posts) while dates will be seasonal but roughly equally distributed, and utm parameters will have a few very frequent and a lot very infrequent entries.&lt;br&gt;
Therefore, the paths could be a good candidate for hash keys.&lt;/p&gt;

&lt;p&gt;On the other hand, there are &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-design.html"&gt;limits for write-throughput&lt;/a&gt; in the same hash key.&lt;br&gt;
To solve this, I added a scatter value as the hash key and randomly distribute all analytics events across the hash key.&lt;br&gt;
This way, I can scale up when needed, but don't need to worry about the complexity yet.&lt;/p&gt;

&lt;p&gt;I used UTC date strings as the sort key, mainly to be able to easily query all events for a certain month by just checking for keys that start with e.g. &lt;code&gt;2022-12&lt;/code&gt;.&lt;br&gt;
A more common way would be to save the date time as an epoch and check for numbers between the epochs of the beginning and the end of the month, but I hadn't thought about that when I wrote the code, and it hasn't yet been important enough to refactor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to a monorepo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/startup-cto/blog/commits/main?before=5046de405d32df6bc9c2036ce5aa26c3e7acc43e+210&amp;amp;branch=main&amp;amp;qualified_name=refs%2Fheads%2Fmain"&gt;Commits start with &lt;code&gt;Move package into sub-directory&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the analytics project in place, code became a bit more complicated and intertwined.&lt;br&gt;
To solve this — and to learn more about monorepos in practice — I decided to split the projects into multiple directories and manage them with &lt;a href="https://rushjs.io/"&gt;Rush&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The most interesting challenge was how to manage the CI pipeline.&lt;br&gt;
I decided to have one common pipeline for all packages, and call individual scripts per package.&lt;br&gt;
This way, each package can still manage the details of its pipeline, but the central pipeline manages dependencies and when to run which task of which project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using OIDC to work with AWS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/startup-cto/blog/commits/main?before=5046de405d32df6bc9c2036ce5aa26c3e7acc43e+175&amp;amp;branch=main&amp;amp;qualified_name=refs%2Fheads%2Fmain"&gt;Commits start with &lt;code&gt;Add package to manage access&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another small change was to use &lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services"&gt;OIDC to authorize GitHub towards AWS&lt;/a&gt;.&lt;br&gt;
Thanks to the monorepo setup, the code is in its own &lt;a href="https://github.com/startup-cto/blog/tree/main/access"&gt;access&lt;/a&gt; package.&lt;br&gt;
While OIDC was quite straightforward, I did learn a few more things about AWS CDK.&lt;br&gt;
Most importantly, that infrastructure should always live in a &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stage.html"&gt;Stage&lt;/a&gt;, so that the construct ids do not change when the infrastructure is deployed via a &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html"&gt;pipeline&lt;/a&gt;.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing to dev.to
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/startup-cto/blog/commits/main?before=5046de405d32df6bc9c2036ce5aa26c3e7acc43e+280&amp;amp;branch=main&amp;amp;qualified_name=refs%2Fheads%2Fmain"&gt;Commits start with &lt;code&gt;refactor: add DraftPost&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last addition to the blog (for now) is the concept of publishing posts.&lt;br&gt;
Up to this point, the deployment pipeline worked like a side-effect-free pure function:&lt;br&gt;
Take the current state of the repo from GitHub, find all articles that are in a published state, and convert them to HTML files.&lt;/p&gt;

&lt;p&gt;But a blog also has side effects that should happen only once when a post is published.&lt;br&gt;
So that's what I implemented next.&lt;/p&gt;

&lt;p&gt;The structure ended up quite simple.&lt;br&gt;
I cleaned up the different types of posts and introduced a &lt;code&gt;ToPublishPost&lt;/code&gt;, which is a post that has all the metadata needed to be published, but no &lt;code&gt;publishedAt&lt;/code&gt; yet.&lt;br&gt;
Then a script that finds all these &lt;code&gt;toPublishPosts&lt;/code&gt;, adds the current date as a &lt;code&gt;publishedAt&lt;/code&gt;, commits it to repo, and runs the side effects.&lt;br&gt;
For now, there are only two side effects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publishing the post to &lt;a href="https://dev.to/the_startup_cto"&gt;Dev.to&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Send a newsletter email via &lt;a href="https://newsletter.startup-cto.net/"&gt;Buttondown&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The current structure
&lt;/h2&gt;

&lt;p&gt;Since the blog is open-source now you can look at its current structure &lt;a href="https://github.com/startup-cto/blog/tree/803a96831826ca3556c3bbd6ff48126a5f4d9538"&gt;on GitHub&lt;/a&gt;.&lt;br&gt;
It still follows the structure I introduced when moving to a monorepo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/startup-cto/blog/tree/803a96831826ca3556c3bbd6ff48126a5f4d9538/.github"&gt;.github&lt;/a&gt; for CI config,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/startup-cto/blog/tree/803a96831826ca3556c3bbd6ff48126a5f4d9538/access"&gt;access&lt;/a&gt; to manage access credentials to AWS,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/startup-cto/blog/tree/803a96831826ca3556c3bbd6ff48126a5f4d9538/analytics"&gt;analytics&lt;/a&gt; with all the code for my custom analytics solution,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/startup-cto/blog/tree/803a96831826ca3556c3bbd6ff48126a5f4d9538/blog"&gt;blog&lt;/a&gt; with the actual blog, and&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/startup-cto/blog/tree/803a96831826ca3556c3bbd6ff48126a5f4d9538/util"&gt;util&lt;/a&gt; with helper packages used in multiple projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are more and smaller changes that I didn't mention in the rest of the article, but if you read up to this point,&lt;br&gt;
you should have a good grasp of the overall situation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;There are obviously &lt;a href="https://github.com/startup-cto/blog/blob/main/TODOS.md"&gt;a lot more ideas&lt;/a&gt; that I want to do with the&lt;br&gt;
blog. The blog isn't only a way for me to manage and share my own knowledge, but now it is also a sandbox&lt;br&gt;
for trying out different web technologies in a production app that won't cause me much pain if it is down due to my mistakes.&lt;/p&gt;

&lt;p&gt;For now, I will most likely focus on writing articles again, but there are some changes I might work on&lt;br&gt;
from time to time, like automatically publishing new articles on Twitter. I'm sure what I learn there&lt;br&gt;
will lead to new articles on its own.&lt;/p&gt;

&lt;p&gt;If you want to see what else will happen, you can subscribe to new articles&lt;br&gt;
&lt;a href="https://newsletter.startup-cto.net"&gt;via email&lt;/a&gt;, &lt;a href="https://startup-cto.net/rss.xml"&gt;via RSS&lt;/a&gt;, or to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>nextjs</category>
      <category>aws</category>
    </item>
    <item>
      <title>How to read any startup's source code</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Sat, 13 Feb 2021 11:00:22 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/how-to-read-any-startup-s-source-code-l6h</link>
      <guid>https://dev.to/the_startup_cto/how-to-read-any-startup-s-source-code-l6h</guid>
      <description>&lt;p&gt;So you are in interviews as an engineer with an SaaS-startup and want to impress them with your deep knowledge of their tech stack? Or you have been working on learning projects but want to see some real-world code? Here are some tricks I have used in the past to know more about a startup's technology than their recruiter.&lt;/p&gt;

&lt;p&gt;You could also use these tricks for market research and spying on your competition, just keep in mind that in early stage it is way more important to understand your customer than your competitors. And that some things might be plainly illegal.&lt;/p&gt;

&lt;p&gt;I also want to point out that I personally wouldn't consider these loopholes a security problem by itself, but they can be if you are not careful, so you might want to avoid some of them in your own company. And yes, there are ways to avoid them, so, contrary to the click-baity title, you might run into situations, where they don't work.&lt;/p&gt;

&lt;p&gt;If you are interested in more articles and news about web product development and entrepreneurship, please feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To understand a startup's tech stack, I&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;find all (sub-)domains for all the applications they use,&lt;/li&gt;
&lt;li&gt;analyze them for tell-tale signs of their tech stack, and&lt;/li&gt;
&lt;li&gt;skim their source code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Finding their apps
&lt;/h2&gt;

&lt;p&gt;Usually, there will be at least some of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A landing-page builder for landing pages&lt;/li&gt;
&lt;li&gt;A content management system for a blog&lt;/li&gt;
&lt;li&gt;An authentication system&lt;/li&gt;
&lt;li&gt;A web app for the actual product&lt;/li&gt;
&lt;li&gt;A backend for the actual product&lt;/li&gt;
&lt;li&gt;One or multiple native apps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good starting point is the homepage itself. This often is created in a landing-page builder or a similar tool like WordPress, or with a static page builder.&lt;/p&gt;

&lt;p&gt;Sometimes, blog or landing pages are hosted separately. The blog is usually linked from the homepage. A typical subdomain is &lt;code&gt;blog&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finding the landing-page builder can be slightly more tricky. Try to find ads, e. g. by using a traffic analysis tool like &lt;a href="https://www.similarweb.com/" rel="noopener noreferrer"&gt;SimilarWeb&lt;/a&gt;, or a specific ads tool like the &lt;a href="https://ads.google.com/aw/keywordplanner/home" rel="noopener noreferrer"&gt;Google Ads keyword plnaner&lt;/a&gt; if you suspect them to use these kinds of ads. Then search for the keywords from these tools. If you see an ad, this will lead to a landing page on their landing page builder. Keep in mind that not all startups rely on these kinds of apps and not all use landing page builders.&lt;/p&gt;

&lt;p&gt;Next is getting to the authentication system and to the web app. Just search for a login button on the homepage. Most startups I've seen do not use a separate authentication system, but simply integrate it into their web app, so when you see the login screen, you are already on the right subdomain. If they use an external solution like AWS Cognito, then you might have to create an account to find the actual subdomain. A typical subdomain name for the authentication system is &lt;code&gt;auth&lt;/code&gt;, and for the web app is &lt;code&gt;app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is also a good point to check for a staging environment that might be hosted e. g. at &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;dev&lt;/code&gt;, or &lt;code&gt;staging&lt;/code&gt;, and sometimes on a sub-level of the app, so e. g. &lt;code&gt;dev.app&lt;/code&gt; or &lt;code&gt;app.dev&lt;/code&gt;. The staging environment will most likely be very similar code but might have more debugger friendly settings.&lt;/p&gt;

&lt;p&gt;When you open the web app, this also allows you to find the backend by opening the dev tools and monitoring XHR requests going out. A typical subdomain name for the backend is &lt;code&gt;api&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finding the native apps usually is quite easy: They are either mentioned on the homepage, or you find them by entering the startup's name in the app store search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analyzing their infrastructure
&lt;/h2&gt;

&lt;p&gt;Most tools leave distinctive telltale signals when they are in use. Fortunately, there are companies out there which specialize in analyzing these. I personally use &lt;a href="https://www.wappalyzer.com/" rel="noopener noreferrer"&gt;Wappalyzer&lt;/a&gt; to analyze each subdomain found above. There is also a free plugin for Chrome and Firefox, just keep in mind that if something is free, then you are the product: It most likely tracks the pages you visit to add more information to the Wappalyzer database.&lt;/p&gt;

&lt;p&gt;This step gives you insights into which frameworks are used in the frontend, which kind of backend is running, and even more. But there is one more step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading their source code
&lt;/h2&gt;

&lt;p&gt;It should not be possible for you to access any backend source code from a different company. If it is, immediately let them know. This is a massive security problem.&lt;/p&gt;

&lt;p&gt;Frontend code, on the other hand, is executed in the browser. Therefore it is accessible to anyone who can open the web app. Just open your DevTools, go to the sources tab, and there it is. On your way, you can even check for warnings or errors that the app emits.&lt;/p&gt;

&lt;p&gt;But there is one more trick to it: The source code you see is most likely bundled and minified, which puts everything into one file and replaces all variable names with single letters. This makes it really hard to understand the code.&lt;/p&gt;

&lt;p&gt;This also makes it really hard to understand error messages. That's why many companies have source maps enabled, which link each character in the scrambled file to the corresponding character in the original source. This way, an error that points to line 1 character 542 in the scrambled file can instead be traced to line 30 character 12 in the &lt;code&gt;Login.tsx&lt;/code&gt; file.&lt;br&gt;
These source maps are either used together with the original code in another tool or, and in my experience, this is more common, the source code is saved as inline source maps next to the minified code.&lt;/p&gt;

&lt;p&gt;What does this mean for us? It means that the original source code is shipped with the application. You can find it when opening the web app in your browser's dev tools and open the "Sources" tab. You might have to search around a bit, but somewhere in there, you can find the source maps with all the juicy code.&lt;/p&gt;

&lt;p&gt;Here's an example from &lt;a href="https://startup-cto.github.io/todos/" rel="noopener noreferrer"&gt;my little todo app&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fstartup-cto.net%2Fcontent%2Fimages%2F2021%2F02%2Fsource-maps.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fstartup-cto.net%2Fcontent%2Fimages%2F2021%2F02%2Fsource-maps.PNG" alt="Source maps in Chrome dev tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, sometimes this doesn't work. But don't give up your hope yet! It might be that source maps are disabled on production. But did you try the staging environment you found in the first step? It might have source maps enabled.&lt;br&gt;
And maybe the code is lazy loaded in, so you don't have access to all source maps right away, but if you create an account and navigate through the app, you should get more and more insights.&lt;/p&gt;

&lt;p&gt;What kind of things can you learn from here? Since the advent of &lt;a href="https://en.wikipedia.org/wiki/Single-page_application" rel="noopener noreferrer"&gt;Single Page Applications&lt;/a&gt;, a lot of business logic is living in web applications. You will be able to understand code structure, tools used, important features, features in development that are only rolled out to some of the users, backend structure, external tools used, ...&lt;/p&gt;

&lt;p&gt;What did you learn with help of these methods? Feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt; and reach out with your learnings.&lt;/p&gt;

</description>
      <category>startup</category>
      <category>career</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Why take-home assignments might not be as bad as their reputation</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Sun, 07 Feb 2021 19:13:17 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/why-take-home-assignments-might-not-be-as-bad-as-their-reputation-584f</link>
      <guid>https://dev.to/the_startup_cto/why-take-home-assignments-might-not-be-as-bad-as-their-reputation-584f</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gLxeY8xL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i58eibg76kwy1osak1vc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gLxeY8xL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i58eibg76kwy1osak1vc.jpg" alt="Take-home assignments can be frustrating"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have dropped out of recruiting processes in the past after they asked me to do a take-home assignment. Still, a take-home assignment is part of almost every recruiting process I define. Here I'll write about why most take-home assignments suck, and how I design my own to avoid the same mistakes.&lt;/p&gt;

&lt;p&gt;I'm focusing mostly on hiring developers in this blog post, but in my experience, most will hold true also for hiring other people for in-demand jobs.&lt;/p&gt;

&lt;p&gt;If you are interested in more articles and news about web product development and entrepreneurship, please feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I reject most take-home assignments
&lt;/h2&gt;

&lt;p&gt;Most take-home assignments suck. You spend hours, days, sometimes weeks, working on a problem no one cares about, just to be told you didn't get the job from someone who might not even have looked at your code. And then do it all over again for the next job application.&lt;/p&gt;

&lt;p&gt;I've done my share of take-home assignments. The worst was algorithmic challenges. I work on web applications, and, except for maybe database queries, readability of the code usually is significantly more important than writing performant algorithms. But there were also some generic "create a full-stack app in 2 hours" tasks. Not as bad as the algorithmic ones. But still usually a waste of time.&lt;/p&gt;

&lt;p&gt;And I am not only talking about myself. Especially the algorithmic challenges are not only a waste of the candidates' time but also a dangerous signal to the company hiring. I do not doubt that there is a correlation between performance on an algorithmic task and later on the job, but it is an indirect one. Those who studied computer science or specifically prepare for interviews, and spend a lot of time on their current occupation will both perform well on these challenges, as well as most likely invest time to learn the skills for their specific new job.&lt;/p&gt;

&lt;p&gt;But it will also sort out those with unorthodox careers, limited time for preparation, and, worst of all (from the company's perspective), those that have the specific skills for the job and no longer need to spend time on grooming interview-only skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  And why I still use them in recruiting
&lt;/h2&gt;

&lt;p&gt;So why do I still use take-home assignments when hiring myself? If done correctly, they are actually not only effective but also help to avoid weeding out candidates that fit the job but don't fit the usual recruiting process. There's a couple of guidelines for good take-home assignments that I try to follow:&lt;/p&gt;

&lt;h3&gt;
  
  
  Right candidate motivation
&lt;/h3&gt;

&lt;p&gt;Recruiting is like any kind of product work: If the customers (the candidates) think that your product is too expensive (they don't want to spend the time on a take-home challenge), then what this really tells you is that the product doesn't deliver enough value to them (they don't see a good reason to change from their current job to this new one).&lt;/p&gt;

&lt;p&gt;Therefore the most important part of using take-home assignments is to first make sure that the job you are offering is really of interest to your candidates, and that they know this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relevant task
&lt;/h3&gt;

&lt;p&gt;Instead of some made-up challenge, I choose a task we had to solve in the past at the company, and that the candidate would have worked on, had they already been there back then. I make sure that the task description matches what we thought the task was about back then, not what we figured out along the way.&lt;/p&gt;

&lt;p&gt;So far I have found no better predictor on how well someone will perform on their job than seeing them perform on the exact job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relevant work environment
&lt;/h3&gt;

&lt;p&gt;I usually tell the candidates to act for the challenge as if they were already working for the company, just from home. This includes googling, using tools, asking questions, and everything else they would be doing on the job normally. When they ask questions, I don't use that as an opportunity to test their knowledge, but try to help as much as possible instead. The same as I would for a colleague or employee.&lt;/p&gt;

&lt;p&gt;There are some caveats to this, as the time a candidate works on a task usually isn't during working hours. So far I have mostly bit the bullet and answered those questions outside of my usual working hours, but this has challenges of its own, including the appearance that outside-hours work might be normal at the company.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small scope
&lt;/h3&gt;

&lt;p&gt;I slim down the task until it takes me less than 1 hour to do it. For someone without background, this can still mean a multi-hour task. I believe that when working with a company for a year or longer, it makes sense for the candidate to spend some time on the company. But there are limits, especially when hiring someone who already has a job and might not easily find a full day that early in the recruiting process.&lt;/p&gt;

&lt;p&gt;With the right scope, this also brings the advantage of being more accessible to those who otherwise would get nervous in a one-on-one or group interview setting.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this fits in the overall recruiting process
&lt;/h2&gt;

&lt;p&gt;I am usually hiring in startup contexts. Usually the process I set up looks something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I always have a really detailed job description including salary ranges, tools used, typical tasks, and as much information as possible to make sure that candidates know even before applying whether the job is for them.&lt;/li&gt;
&lt;li&gt;For each candidate I check the CV whether it could match. Here I am quite liberal. It usually would be worse for me to sort out a good candidate too early than to sort out a bad candidate "too late". My estimate is that about 50% of candidates make it to the next step.&lt;/li&gt;
&lt;li&gt;Next is a (video-)call. The sole purpose of this call is to convince the candidate that they should work for us. I also ask questions, because that is what candidates expect and without these, they would be uncomfortable, but I don’t really care about them. I’ve in the past actually outsourced this to a sales agent with quite a good success. Here I lose about 5% of candidates who don’t want to do the next step.&lt;/li&gt;
&lt;li&gt;Now comes the take-home assignment. I tell the candidate about a dozen times (literally, it is really important to stress this) that the task can only be solved if they ask questions, and that this is totally fine and that they should just imagine they were already working for us and would be working from home for one day. This is the main cut-off, only about 5% of candidates make it through this.&lt;/li&gt;
&lt;li&gt;Next is a trial half-day of working with the team. Before the 'Rona this happened in the office, with plenty of breaks, lunch, and opportunities to socialize. Remotely it is one or two longer video calls. The candidate needs to sign an NDA. The tasks here are not made up either, but just whatever the team is currently working on. This step loses another 5-10%.&lt;/li&gt;
&lt;li&gt;At his point, I’m offering a contract and hire the candidate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of the steps is required for take-home assignments to make sense. If the candidates do not know enough about the job or are obviously a bad fit, then asking them to do a take-home assignment isn't fair to them and their time. And even though the take-home assignment is in my experience quite a good measure for job performance, there is still a need to ensure team fit.&lt;/p&gt;

&lt;p&gt;What are your experiences with take-home assignments from both sides? Feel free to jump into a discussion with me &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;on Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>10 bad TypeScript habits to break this year</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Thu, 21 Jan 2021 21:01:40 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/10-bad-typescript-habits-to-break-this-year-1kd5</link>
      <guid>https://dev.to/the_startup_cto/10-bad-typescript-habits-to-break-this-year-1kd5</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjw8fp5nec3novjva5jjr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjw8fp5nec3novjva5jjr.jpg" alt="Bad habits die hard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TypeScript and JavaScript have steadily evolved over the last years, and some of the habits we built over the last decades have become obsolete. Some might never have been meaningful. Here's a list of 10 habits that we all should break.&lt;/p&gt;

&lt;p&gt;If you are interested in more articles and news about web product development and entrepreneurship, please feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Onto the examples! Please note that each "What it should look like" only fixes the issue discussed, even if there are further code smells that should be addressed.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Not using &lt;code&gt;strict&lt;/code&gt; mode
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Using a &lt;code&gt;tsconfig.json&lt;/code&gt; without strict mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES2015"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;Just enable &lt;code&gt;strict&lt;/code&gt; mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES2015"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;Introducing stricter rules in an existing codebase takes time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;Stricter rules will make it easier to change code in the future, so the time spent on fixing the code is returned and then some when working on the repository in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Defining default values with &lt;code&gt;||&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Falling back with &lt;code&gt;||&lt;/code&gt; for optional values:&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;function&lt;/span&gt; &lt;span class="nf"&gt;createBlogPost&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&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;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;Use the new &lt;code&gt;??&lt;/code&gt; operator, or, even better, define the fallback right at the parameter level.&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;function&lt;/span&gt; &lt;span class="nf"&gt;createBlogPost&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;date&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;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;??&lt;/code&gt; operator has just been introduced last year, and when using values in the middle of a long function it might be hard to set them already as parameter defaults.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;??&lt;/code&gt;, unlike &lt;code&gt;||&lt;/code&gt;, falls back only for &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;, not for all falsy values. Also, if your functions are so long that you cannot define defaults at the beginning, then splitting them might be a good idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Using &lt;code&gt;any&lt;/code&gt; as type
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;any&lt;/code&gt; for data when you are unsure about the structure.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadProducts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.mysite.com/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;In almost every situation where you type something as &lt;code&gt;any&lt;/code&gt;, you should type it as &lt;code&gt;unknown&lt;/code&gt; instead.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadProducts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.mysite.com/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Product&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;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;any&lt;/code&gt; is convenient, as it basically disables all type checks. Often, &lt;code&gt;any&lt;/code&gt; is used even in official typings (e. g. &lt;code&gt;response.json()&lt;/code&gt; from the example above is typed as &lt;code&gt;Promise&amp;lt;any&amp;gt;&lt;/code&gt; by the TypeScript team).&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;It basically disables all type checks. Anything that comes in via &lt;code&gt;any&lt;/code&gt; will completely forego any type-checks. This leads to hard to catch bugs, as code will fail only when our assumptions about type structure are relevant to the runtime code.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;val as SomeType&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Forcefully telling the compiler about a type that it cannot infer.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadProducts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.mysite.com/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Product&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;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;That's what type guards are for.&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;function&lt;/span&gt; &lt;span class="nf"&gt;isArrayOfProducts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isProduct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isProduct&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadProducts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.mysite.com/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isArrayOfProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Received malformed products API response&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;When converting from JavaScript to TypeScript, the existing codebase often makes assumptions about types that cannot be deduced automatically by the TypeScript compiler. In these cases, throwing in a quick &lt;code&gt;as SomeOtherType&lt;/code&gt; can speed up the conversion without having to loosen the settings in tsconfig.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;Even if the assertion might be save right now, this might change when someone moves code around. The type guard will ensure that all checks are explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. &lt;code&gt;as any&lt;/code&gt; in tests
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Creating incomplete stand-ins when writing tests.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createEmailText returns text that greats the user by first name&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createEmailText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&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;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;If you need to mock data for your tests, move the mocking logic next to the thing you mock and make it reusable.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MockUser&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john@doe.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createEmailText returns text that greats the user by first name&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="o"&gt;=&amp;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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createEmailText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&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;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;When writing tests in a codebase that doesn't have great test coverage yet, there are often complicated big data structures, but only parts of it are needed for the specific functionality under test. Not having to worry about the other properties is easier in the short term.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;Foregoing the creation of a mock will bite us, latest when one of the properties changes and we need to change it in all tests instead of one central location. Also, there will be situations where the code under test relies on properties that we did not deem important before, and then all tests for that functionality need to be updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Optional properties
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Marking properties as optional that are sometimes there and sometimes not.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;digital&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;physical&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;weightInKg&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;sizeInMb&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;Explicitly model which combinations exist and which don't.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;digital&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;physical&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;DigitalProduct&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;digital&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;sizeInMb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PhysicalProduct&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;physical&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;weightInKg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;Marking properties as optional instead of splitting out types is easier and produces less code. It also requires a deeper understanding of the product being built and might limit usage of code if assumptions about the product change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;The big benefit of type systems is that they can replace runtime checks with compile-time checks. With more explicit typing, it is possible to get compile-time checks for bugs that otherwise might have gotten unnoticed, e. g. by making sure that every &lt;code&gt;DigitalProduct&lt;/code&gt; has a &lt;code&gt;sizeInMb&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. One letter generics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Naming a generic with one letter&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;function&lt;/span&gt; &lt;span class="nf"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;arr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arr&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;Giving a full descriptive type name.&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;function&lt;/span&gt; &lt;span class="nf"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Element&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;arr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arr&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;This habit grew I guess because &lt;a href="https://www.typescriptlang.org/docs/handbook/generics.html" rel="noopener noreferrer"&gt;even the official docs use one-letter names&lt;/a&gt;. It is also quicker to type and requires less thinking to press &lt;code&gt;T&lt;/code&gt; instead of writing a full name. &lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;Generic type variables are variables, like any other. We have abandoned the idea of describing the technicalities of variables in their names when IDEs started to just show us these technicalities. E. g. instead of &lt;code&gt;const strName = 'Daniel'&lt;/code&gt; we now only write &lt;code&gt;const name = 'Daniel'&lt;/code&gt;. Also, one letter variable names are generally frowned upon because it can be hard to decipher what they mean without looking at their declaration.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Non-boolean boolean checks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Checking whether a value is defined by passing the value directly to an &lt;code&gt;if&lt;/code&gt; statement.&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;function&lt;/span&gt; &lt;span class="nf"&gt;createNewMessagesResponse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You have &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new messages`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: Could not retrieve number of new messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;Explicitly checking for the condition we care about.&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;function&lt;/span&gt; &lt;span class="nf"&gt;createNewMessagesResponse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You have &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new messages`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: Could not retrieve number of new messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;Writing the check in short looks more succinct and allows us to avoid thinking about what we actually want to check.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;Maybe we should think about what we actually want to check. The examples above for example handle the case of &lt;code&gt;countOfNewMessages&lt;/code&gt; being &lt;code&gt;0&lt;/code&gt; differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. The Bang Bang operator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;Converting a non-boolean value to boolean.&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;function&lt;/span&gt; &lt;span class="nf"&gt;createNewMessagesResponse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You have &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new messages`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: Could not retrieve number of new messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;Explicitly checking for the condition we care about.&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;function&lt;/span&gt; &lt;span class="nf"&gt;createNewMessagesResponse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You have &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new messages`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: Could not retrieve number of new messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;To some, understanding &lt;code&gt;!!&lt;/code&gt; is like an initiation ritual to the world of JavaScript. It looks short and succinct, and if you are already used to it, then you know what it is about. It is a shortcut to convert any value to a boolean. Especially if, in a codebase, there is no clear semantic separation between falsy values like &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;undefined&lt;/code&gt;, and &lt;code&gt;''&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;Like many shortcuts and initiation rituals, using &lt;code&gt;!!&lt;/code&gt; obfuscates the true meaning of the code by promoting insider knowledge. This makes the codebase less accessible to new developers, be it new to developing in general, or just new to JavaScript. It's also quite easy to introduce subtle bugs. The problem with &lt;code&gt;countOfNewMessages&lt;/code&gt; being &lt;code&gt;0&lt;/code&gt; from "Non-boolean boolean checks" persists with &lt;code&gt;!!&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. &lt;code&gt;!= null&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What it looks like
&lt;/h3&gt;

&lt;p&gt;The little sister of the bang bang operator, &lt;code&gt;!= null&lt;/code&gt; allows us to check for &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; at the same time.&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;function&lt;/span&gt; &lt;span class="nf"&gt;createNewMessagesResponse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You have &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new messages`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: Could not retrieve number of new messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it should look like
&lt;/h3&gt;

&lt;p&gt;Explicitly checking for the condition we care about.&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;function&lt;/span&gt; &lt;span class="nf"&gt;createNewMessagesResponse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You have &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;countOfNewMessages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new messages`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: Could not retrieve number of new messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why we do it
&lt;/h3&gt;

&lt;p&gt;If you got here, your codebase and your skills are already in quite good shape. Even most linting rulesets that enforce using &lt;code&gt;!==&lt;/code&gt; over &lt;code&gt;!=&lt;/code&gt; make an exemption for &lt;code&gt;!= null&lt;/code&gt;. If there is no clear distinction in the codebase between &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt;, then &lt;code&gt;!= null&lt;/code&gt; helps to shorten a check for both possibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we shouldn't
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;null&lt;/code&gt; values were quite a hassle in the early days of JavaScript, with TypeScript in &lt;code&gt;strict&lt;/code&gt; mode, they can become a valuable member of the language's tool belt. A common pattern I have seen is defining &lt;code&gt;null&lt;/code&gt; values as things not being there, and &lt;code&gt;undefined&lt;/code&gt; as not things that are unknown, e. g. &lt;code&gt;user.firstName === null&lt;/code&gt; might mean that the user literally does not have a first name, while &lt;code&gt;user.firstName === undefined&lt;/code&gt; just means that we haven't asked that user yet (and &lt;code&gt;user.firstName === ''&lt;/code&gt; would mean the first name literally is &lt;code&gt;''&lt;/code&gt; - you'd be surprised &lt;a href="https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/" rel="noopener noreferrer"&gt;which kind of names actually exist&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>codequality</category>
    </item>
    <item>
      <title>TDD in a React frontend</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Tue, 19 Jan 2021 22:39:43 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/tdd-in-a-react-frontend-1g4n</link>
      <guid>https://dev.to/the_startup_cto/tdd-in-a-react-frontend-1g4n</guid>
      <description>&lt;p&gt;Nowadays, only a few professional developers are left that seriously doubt the value of test-driven-development and test-driven-design (tdd). But the reality of many codebases I have seen is that tdd is often limited to the backend, where the "business logic" lives.&lt;/p&gt;

&lt;p&gt;Part of this is due to a stigma that frontend development is not "real software development", even though in most cases a fully functional backend is completely unusable without the matching frontend. But part of it is due to missing skills on how to tdd in the frontend. This is what this article is about.&lt;/p&gt;

&lt;p&gt;I'm taking React as an example because it is the framework I am most familiar with and the declarative style makes it easier to some of the tests than when using pure JavaScript, HTML, and CSS. But most of the ideas from this article hold also in other contexts.&lt;/p&gt;

&lt;p&gt;If you are interested in more articles and news about web product development and entrepreneurship, please feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is frontend testing harder than backend?
&lt;/h2&gt;

&lt;p&gt;It is not always laziness that drives frontend engineers away from tdd. This becomes especially obvious when watching full-stack engineers, who religiously practice tdd for their backend code, not write a single test in the frontend.&lt;/p&gt;

&lt;p&gt;In my experience, the differences boil down to three points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the frontend, features usually have significantly bigger interfaces. While a backend API in its simplest version might be defined by a simple JSON structure, even the simplest frontend feature will be defined not only by functionality but also by often thousands of pixels rendered to the screen.&lt;/li&gt;
&lt;li&gt;Even worse, we don't yet have a good way to explain to a machine which of these pixels matter. For some, changing the pixels doesn't really make any difference, but change the wrong ones, and the feature becomes completely unusable.&lt;/li&gt;
&lt;li&gt;For a long time, tooling did not allow for integration tests that run in seconds. Instead, tests either had to be limited to pure business logic or run in the browser with often minutes of setup time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So how do we fix this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing testable frontend code
&lt;/h2&gt;

&lt;p&gt;Similar to how you often need to split backend code and introduce dependency injection to be able to test it, frontend code also should be split to make it easier to test. There are roughly three categories of frontend code, each of them with a different way to test them.&lt;/p&gt;

&lt;p&gt;Let's take a &lt;a href="https://startup-cto.github.io/todos" rel="noopener noreferrer"&gt;classical React todo app&lt;/a&gt; as an example. I recommend to open &lt;a href="https://github.com/startup-cto/todos" rel="noopener noreferrer"&gt;the repository&lt;/a&gt; on a second screen and follow along. I have added code excerpts to this article for those who might read on the mobile phone or otherwise don't have access to the repository while reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Glue code
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/startup-cto/todos/blob/main/src/App/App.tsx" rel="noopener noreferrer"&gt;App component&lt;/a&gt; and the &lt;a href="https://github.com/startup-cto/todos/blob/main/src/App/useTodos.ts" rel="noopener noreferrer"&gt;useTodos hook&lt;/a&gt; are what I like to call glue code. It "glues" together the rest of the code to bring the functionality to life:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TodoApp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionComponent&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;completeTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deleteTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTodos&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="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoList&lt;/span&gt;
        &lt;span class="na"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onCompleteTodo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;completeTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onDeleteTodo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;deleteTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AddTodo&lt;/span&gt; &lt;span class="na"&gt;onAdd&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todosReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialTodos&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="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createAddTodoAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;completeTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createCompleteTodoAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createDeleteTodoAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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;p&gt;Similar to a controller in the backend, this is best tested with &lt;a href="https://github.com/startup-cto/todos/blob/main/src/App/App.test.tsx" rel="noopener noreferrer"&gt;integration tests&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TodoApp&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shows an added todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New todo&lt;/span&gt;&lt;span class="dl"&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;todoDescription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My new todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todoDescription&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;addTodoButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addTodoButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoDescription&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&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;p&gt;The reason why I am talking about these tests first is that this is usually the first kind of test that I write. The difference between a web app and a landing page is that the web app, without any of its functionality and just with its looks, has no value. These tests describe the behavior and allow me to keep focused so that I only implement what is needed.&lt;/p&gt;

&lt;p&gt;These kinds of integration tests should be as independent of the technology used as possible. The test examples above are dependent on React (if I were to rewrite the app without React, I would have to change the tests as well), but that's about it. The same tests would work irrespective of whether I am using functional components, class components, Redux state management, an external form library, or whether I use 3 or 300 components to build the todo app. This is very important, as it means that I can safely refactor the code without touching the tests.&lt;/p&gt;

&lt;p&gt;The reason for this is that the tests are written from a user perspective: Find something labeled "New todo", type the new todo into it, press the "Add todo" button and check that the todo I just wrote now shows up on the screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Business logic
&lt;/h3&gt;

&lt;p&gt;These are the tests that folks coming from backend testing are most familiar with. The &lt;a href="https://github.com/startup-cto/todos/tree/main/src/model" rel="noopener noreferrer"&gt;business logic of our todo app&lt;/a&gt; takes care of creating, removing, and marking todos as done. The exact same could also be used in the backend.&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;function&lt;/span&gt; &lt;span class="nf"&gt;todosReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoAction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TodoActionType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddTodo&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="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TodoActionType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CompleteTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TodoActionType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeleteTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;p&gt;&lt;a href="https://github.com/startup-cto/todos/blob/main/src/model/reducer.test.ts" rel="noopener noreferrer"&gt;Tests for this kind of code&lt;/a&gt; are deceivingly simple:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todo reducer&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addTodoAction&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;adds a new todo to the list&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="o"&gt;=&amp;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;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is a todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;todosReducer&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nf"&gt;createAddTodoAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toContainEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;objectContaining&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;description&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;does not remove an existing todo&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="o"&gt;=&amp;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;existingTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TodoMock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;todosReducer&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;existingTodo&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;createAddTodoAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is a todo&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="nf"&gt;toContainEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingTodo&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;p&gt;The hard part about testing business logic is not to write the tests, but to separate the business logic from the rest of the code. Let's have a look at &lt;a href="https://github.com/startup-cto/todos/blob/main/src/App/useTodos.ts" rel="noopener noreferrer"&gt;useTodos&lt;/a&gt;, which is the glue code bringing this reducer into React:&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;function&lt;/span&gt; &lt;span class="nf"&gt;useTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todosReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialTodos&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="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createAddTodoAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;completeTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createCompleteTodoAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createDeleteTodoAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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;p&gt;The danger here would be to write the business logic so that it can only be tested by testing the full hook. Using the hook just to glue together the reducer and action creators with React logic saves us from all that pain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Presentational components
&lt;/h3&gt;

&lt;p&gt;Last, but not least, let's look at &lt;a href="https://github.com/startup-cto/todos/tree/main/src/components" rel="noopener noreferrer"&gt;the presentational code&lt;/a&gt;. These components define the interface to the user, but do not contain any business logic on their own. This is where most of the problems I mentioned at the beginning of the article come to pass. And, to be quite honest, I haven't found a perfect solution to all of them. But there is a concept that gets close:&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;story&lt;/strong&gt; is the visual equivalent of a unit test. The main remaining shortcoming is that the step of asserting whether or not the test was successful has to be done manually.&lt;/p&gt;

&lt;p&gt;Here's a &lt;a href="https://github.com/startup-cto/todos/blob/main/src/components/shared/Button.stories.tsx" rel="noopener noreferrer"&gt;story for a button&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actionArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onClick&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&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;actionArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Click me!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonColor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Success&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;p&gt;and here is &lt;a href="https://github.com/startup-cto/todos/blob/main/src/components/shared/Button.tsx" rel="noopener noreferrer"&gt;the button itself&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;ButtonColor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Success&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="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Reset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onClick&lt;/span&gt;&lt;span class="p"&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="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colorStyles&lt;/span&gt; &lt;span class="o"&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;ButtonColor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Alert&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#b33 solid 1px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2px 2px 2px rgba(100,0,0,0.8)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#a00&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ButtonColor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3b3 solid 1px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2px 2px 2px rgba(0,100,0,0.8)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#0a0&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="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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;colorStyles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.2rem 0.5rem&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="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;The story renders the button in isolation. I can first write the story, which allows me to think about the intended interface for this component, and only implement the component itself afterward. If any implementation details change, then as long as the interface stays the same, I won't have to change the story. And I can look at the rendered story in isolation whenever I want to verify that it still looks as intended (this is the "manual" part I mentioned above). As soon as I have a version I am happy with, I can even set up automated regression testing with help of a visual regression tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fstartup-cto.net%2Fcontent%2Fimages%2F2021%2F01%2Fstorybook.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fstartup-cto.net%2Fcontent%2Fimages%2F2021%2F01%2Fstorybook.PNG" alt="storybook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The All Together
&lt;/h2&gt;

&lt;p&gt;What would it look like in practice, developing this todo app tdd-style?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write an integration test that the text "No todos" should be visible if there are no todos&lt;/li&gt;
&lt;li&gt;Fulfill the test by implementing the App component so that it just returns "No todos"&lt;/li&gt;
&lt;li&gt;Extract "No todos" into its own component&lt;/li&gt;
&lt;li&gt;Add a story for it&lt;/li&gt;
&lt;li&gt;Use the story to drive visual changes until the "No todos" part looks like it should&lt;/li&gt;
&lt;li&gt;Add an integration test about adding a todo&lt;/li&gt;
&lt;li&gt;Start implementing the test and realize that I will need some kind of state management&lt;/li&gt;
&lt;li&gt;Comment out the integration test&lt;/li&gt;
&lt;li&gt;Write a unit test for the state reducer&lt;/li&gt;
&lt;li&gt;Fulfill the test by writing a simple first version of the reducer&lt;/li&gt;
&lt;li&gt;Write a story for displaying the list of todos&lt;/li&gt;
&lt;li&gt;Use the story to drive the implementation of a TodoList component&lt;/li&gt;
&lt;li&gt;Comment the integration test back in&lt;/li&gt;
&lt;li&gt;Fulfill the integration test by gluing together the reducer and the component&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Obviously, there are many other ways to go about this. But hopefully, this shows one potential workflow to use tdd in the frontend.&lt;/p&gt;

&lt;p&gt;If you are interested in more articles and news about web product development and entrepreneurship, please feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;. And please send me a tweet about your experiences with tdd in the frontend!&lt;/p&gt;

</description>
      <category>react</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Automating Open-Source on GitHub</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Fri, 08 Jan 2021 20:59:01 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/automating-open-source-on-github-4ikm</link>
      <guid>https://dev.to/the_startup_cto/automating-open-source-on-github-4ikm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZuJ9FsnK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pn0qv0ljpa45mzzdji49.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZuJ9FsnK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pn0qv0ljpa45mzzdji49.jpg" alt="Open-source Maintenance is like changing oil on a car"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maintaining open source is a noble, but can be time-consuming endeavor. It doesn't have to be, though. During Hacktoberfest 2020 I used one of my repositories to experiment with automation around contribution and maintenance of &lt;a href="https://github.com/dbartholomae/jsx-readme"&gt;jsx-readme&lt;/a&gt;. This is what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it easy to contribute
&lt;/h2&gt;

&lt;p&gt;The benefit of open-source project is that whenever someone has a feature request or finds a bug, they can fix it themselves by raising a PR. At least in theory. In practice, it is really hard to contribute to many of the open-source projects out there. To be able to contribute, you need to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understand the codebase,&lt;/li&gt;
&lt;li&gt;make it run locally, and&lt;/li&gt;
&lt;li&gt;know which standards to adhere to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this becomes significantly simpler with a good &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;. I've looked through multiple, both from big as well as small projects, and created &lt;a href="https://github.com/dbartholomae/jsx-readme/blob/main/CONTRIBUTING.md"&gt;a simple one&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But this is not all. The code should also be structured so that it is easy to understand, and well tested so that contributors can be sure they didn't break anything.&lt;br&gt;
Furthermore, having as many rules as possible codified in an automated fashion, e. g. via linting, makes it easy for a contributor to just open a PR and get feedback. This is why in my projects I always have a pre-commit hook that runs &lt;code&gt;eslint --cache --fix&lt;/code&gt; with a config that includes &lt;code&gt;prettier&lt;/code&gt;. Which brings us to...&lt;/p&gt;
&lt;h2&gt;
  
  
  Review PRs automatically
&lt;/h2&gt;

&lt;p&gt;Being able to review a PR without having to check out and run the code locally, makes the whole process significantly better. With my setup, I think I have so far only had one PR which I had to check out locally to do some adjustments on my own, and for the rest I felt confident in accepting them after just reading the code changes. Why?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I enforce that there has to be an issue before a PR. This way I can first discuss &lt;strong&gt;what&lt;/strong&gt; to do and use the PR only to check the &lt;strong&gt;how&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;There is 100% test coverage, and it is enforced automatically. Usually, 100% test coverage might not be that helpful, but for open source it means that contributors will have to test-cover the code they add. The CI will tell me both if they covered 100%, as well as whether their tests pass. So when reviewing, I usually focus on the tests first: Do they test everything discussed in the issue? Then I review the rest of the code for maintainability. As a last step, I go back to the tests, because sometimes reading the code brings to mind some edge cases that I might not be sure about. With 100% test coverage, propablity is high that they are covered.&lt;/li&gt;
&lt;li&gt;I do not review code for style, this is taken care already by linting and prettification.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How does the automation work in practice? I've used TravisCI and CircleCI in the past, but have switched to GitHub Workflows last year. I run &lt;code&gt;tsc&lt;/code&gt;, &lt;code&gt;eslint&lt;/code&gt;, &lt;code&gt;jest&lt;/code&gt;, and &lt;code&gt;pkg-ok&lt;/code&gt; against every PR. &lt;code&gt;tsc&lt;/code&gt; builds and compiles everything and surfaces type errors. &lt;code&gt;eslint&lt;/code&gt; checks not only for linting, but also for style, via the &lt;code&gt;prettier&lt;/code&gt; plugin. &lt;code&gt;jest&lt;/code&gt; runs both unit tests, which test the source code, as well as integration tests, which run on the compiled code to make sure that everything is compiled into the right spot. And &lt;code&gt;pkg-ok&lt;/code&gt; makes sure that the entry points defined in &lt;code&gt;package.json&lt;/code&gt; exist.&lt;/p&gt;
&lt;h2&gt;
  
  
  Synchronize documentation and code
&lt;/h2&gt;

&lt;p&gt;When writing an open source library, usually the hope is for others to use it. This requires a good external documentation. A good starting point is a README file with an example. But for most libraries you will need more.&lt;br&gt;
Unfortunately, documentation has the tendency of getting out-of-sync with the code it is describing quite quickly. And when maintaining an open-source repo, ideally you can make changes quickly without having to worry about documentation too much.&lt;br&gt;
So for &lt;code&gt;jsx-readme&lt;/code&gt;, I used &lt;a href="https://typedoc.org/"&gt;TypeDoc&lt;/a&gt;, a TypeScript documentation library based on the &lt;a href="https://jsdoc.app/"&gt;JSDoc standard&lt;/a&gt; (which also lead me to discover something about &lt;a href="https://startup-cto.net/writing-markdown-with-jsx/"&gt;how React and JSX work together&lt;/a&gt;). This way, my documentation is next to the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Defines a regex to search with and a string that should be used
 * to replace the results found.
 */&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Replacement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;find&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** @internal */&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;replacements&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Replacement&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** Displays a code file with a heading and a codeblock. */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CodeFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="cm"&gt;/** The file's content. */&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * A list of replacements to be made in the file's content,
   * e. g. for replacing relative import paths.
   * */&lt;/span&gt;
  &lt;span class="nx"&gt;replacements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;executeReplacements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;replacements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;replacedStr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;replacedStr&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="nx"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;replacement&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="nx"&gt;str&lt;/span&gt;
    &lt;span class="p"&gt;);&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CodeBlock&lt;/span&gt; &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;executeReplacements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trimEnd&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CodeBlock&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LineBreak&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;Thanks to TypeScript and good naming, not everything needs a long description. A &lt;code&gt;fileName&lt;/code&gt; that is a &lt;code&gt;string&lt;/code&gt; should be understandable without additional documentation. For the rest, we can add comments right next to the code, and &lt;a href="https://dbartholomae.github.io/jsx-readme/modules/_components_codefile_.html"&gt;the documentation will be created accordingly&lt;/a&gt;. This way, when I (or a contributor that comes in for the first time), e. g. adds a new prop, it will show up in the documentation, and the existing documenting comment right next to it will inspire me to add all needed documentation there as well.&lt;/p&gt;

&lt;p&gt;But even more important than the documentation deeper down in the code, is the README on top of it. This is where &lt;code&gt;jsx-readme&lt;/code&gt; itself comets in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Readme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Component&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Create a header with title, badges and description inferred from package.json */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TitleFromPkg&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BadgesFromPkg&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Add additional badges. */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LineBreak&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DescriptionFromPkg&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Create an example section based on all files from the example directory set up in package.json */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExamplesFromPkg&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Create a section linking to the homepage from package.json */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomepageFromPkg&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Create a section linking to the contributing guidelines file */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ContributingSection&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Create a section linking to the license file. */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LicenseFromPkg&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;renderToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./README.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Readme&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will be autocreated from &lt;code&gt;package.json&lt;/code&gt; and the examples file, so if either one changes, the README changes with them. In addition, I used the example files as &lt;a href="https://github.com/dbartholomae/jsx-readme/blob/main/test/README.md.test.tsx"&gt;part of the integration tests&lt;/a&gt;, so I always know that the examples actually work.&lt;/p&gt;

&lt;p&gt;Last but not least, building the documentation should happen on each merge automatically. This is done with help of a script&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;typedoc &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;docs/.nojekyll &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ts-node examples/README.md.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is run via a GitHub Workflow that we will look at later in this article.&lt;/p&gt;

&lt;p&gt;One thing that I haven't yet set up, is an automatic check of documentation coverage. I had used that in previous open-source projects, but it lead to meaningless documentation just to fulfill the coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically create changelogs
&lt;/h2&gt;

&lt;p&gt;Another part of documentation is the changelog. To simplify processes here, I am using &lt;a href="https://semver.org/"&gt;semantic versioning&lt;/a&gt; which means that I describe all changes in a machine-readable format in my commit messages. The big advantage is, that I do not need to maintain additional documentation: I create the changelog automatically based of the commits.&lt;/p&gt;

&lt;p&gt;This also has a disadvantage, though: Git isn't the most user-friendly tool, and writing meaningful commit messages is a rare skill even for an experienced developer. I have a check in the CI to ensure that all commit messages of a PR are in the right format, but updating commits has so far been the most time-consuming part of PR discussions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release on push
&lt;/h2&gt;

&lt;p&gt;Now that I know that the PR is mergable, all I have to do is approve the review. Everything else is automated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mergify.io/"&gt;mergify&lt;/a&gt; waits for the CI to be green and the PR to be approved, and then automatically merges it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/semantic-release/semantic-release"&gt;Semantic Release&lt;/a&gt; reads through the commit messages and figures out whether a new release is warranted (if at least one commit contains a new feature, a fix, or a breaking change) and which kind of version bump should happen. It also creates the changelog, commits all changed files into a release commit and tags the commit with the release version. Last but not least it releases to npm. All of this aided by a plethora of plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep dependencies up-to-date
&lt;/h2&gt;

&lt;p&gt;Last but not least, maintaining an open-source repository also means keeping dependencies up-to-date. The best way around this obviously is to not have dependencies in the first place, and especially in the JavaScript world this approach is still underused: For each dependency think about whether it is really needed, as it will not only show up in your project, but then in every project that uses your library. So for each dependency that your users will have to install, think twice whether you really need it.&lt;/p&gt;

&lt;p&gt;For the remaining dependencies, the setup we have so far make it really easy to keep up-to-date: I set up &lt;a href="https://github.com/dbartholomae/jsx-readme/blob/main/.github/dependabot.yml"&gt;dependabot&lt;/a&gt; to automatically create PRs with new package versions. Thanks to 100% test-coverage I know that if all tests still pass, I can just merge the PR. Which means, that I can actually &lt;a href="https://github.com/dbartholomae/jsx-readme/blob/main/.github/mergify.yml"&gt;configure mergify&lt;/a&gt; to do this for me automatically. And then the GitHub workflow will automatically create the appropriate release.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Overall, the experiment during Hacktoberfest was quite interesting, and I am overall really happy with my setup. My next step on this will be to create a project template out of it to make it easier for me to create new open-source projects. If you don't yet, feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt; for the latest news on that and for more on web technology and entrepreneurship.&lt;br&gt;
If you do, please tweet to me what your experiences are on maintaining open-source projects, and how to make it as painless as possible?&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>github</category>
    </item>
    <item>
      <title>What writing my own JSX renderer taught me about React</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Tue, 05 Jan 2021 22:13:40 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/what-writing-my-own-jsx-renderer-taught-me-about-react-2gah</link>
      <guid>https://dev.to/the_startup_cto/what-writing-my-own-jsx-renderer-taught-me-about-react-2gah</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9GcUrx3C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/21ztfq4g6hw7z6cj8d1m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9GcUrx3C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/21ztfq4g6hw7z6cj8d1m.jpg" alt="Learning in sunshine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the reasons that React got so popular is definitively the syntax it introduced: Writing HTML-like code to declaratively describe components just feels good. But this brought me to the question: Why is this syntax only used for React, basically for describing HTML?&lt;/p&gt;

&lt;p&gt;My first realization hit after a few minutes of research: It isn't. And at least one of them you have most likely already seen in action: &lt;a href="https://github.com/vadimdemedes/ink"&gt;Ink&lt;/a&gt;. It is used to declaratively build CLIs, e. g. for Gatsby, Yarn or Terraform.&lt;/p&gt;

&lt;p&gt;This gave me the courage to try something like this on my own, a journey that lead to &lt;a href="https://github.com/dbartholomae/jsx-readme"&gt;jsx-readme&lt;/a&gt; and the underlying &lt;a href="https://github.com/dbartholomae/jsx-md"&gt;jsx-md&lt;/a&gt;. In this post, I will lead you along my journey and the learnings this sparked about JSX, and about React.&lt;/p&gt;

&lt;p&gt;If you are interested in more on tech, entrepreneurship, and how to bring these two together, feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our goal
&lt;/h2&gt;

&lt;p&gt;We want to be able to write code like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Readme&lt;/span&gt;&lt;span class="p"&gt;()&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LineBreak&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;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;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;examples/README.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Readme&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to create markdown like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# jsx-md&lt;/span&gt;
Generate markdown files with a React&lt;span class="se"&gt;\-&lt;/span&gt;like syntax.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later on, this will allow us to write more complicated components from these building blocks. But now: Let's start with some fundamentals. You can skip everything you know and follow the headlines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown
&lt;/h2&gt;

&lt;p&gt;Markdown is a markup language. It allows to add formatting like &lt;em&gt;italics&lt;/em&gt; or &lt;strong&gt;bold&lt;/strong&gt; with help of text characters. E. g. this blog post is written in Markdown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Markdown&lt;/span&gt;

Markdown is a markup language. It allows to add formatting like &lt;span class="ge"&gt;*italics*&lt;/span&gt; or __bold__ with help of text characters. E. g. this blog post is written in Markdown:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also used for documentation of code, e. g. README files.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSX
&lt;/h2&gt;

&lt;p&gt;JSX is syntactic sugar for JavaScript. It is compiled down to pure JavaScript, and therefore can only be used if a compile step is available, for example via webpack or TypeScript. For compiling down, it needs to know which pragma to use. By default, most compilers use React, e. g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'primary'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Writing Markdown with JSX&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;One of the reasons that React got so popular...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article&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;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&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;Writing Markdown with JSX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;One of the reasons that React got so popular...&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but you can tell the compiler to use a different function &lt;code&gt;someOtherFunction&lt;/code&gt; instead of &lt;code&gt;React.createElement&lt;/code&gt; by adding the pragma &lt;code&gt;/** @jsx someOtherFunction */&lt;/code&gt; to the beginning of the file.&lt;/p&gt;

&lt;p&gt;In the end, JSX is just syntactic sugar for function calls&lt;/p&gt;

&lt;h2&gt;
  
  
  First try: Returning plain strings
&lt;/h2&gt;

&lt;p&gt;So if JSX is syntactiv sugar for functions, and markdown is just a special kind of string, then why not just write a function that returns a string? This was the first approach I tried and lead to code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* @jsx createElement */&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createElement&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;typeOrComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;typeOrComponent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;typeOrComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Heading&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;md&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;md&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Test&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;# Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I got this to a &lt;a href="https://github.com/dbartholomae/jsx-md/tree/b7552c6ac56ce97f0cd2c430c0670ccd4f3f12d8/src"&gt;working version&lt;/a&gt;. But why, then, is there no release with this?&lt;/p&gt;

&lt;p&gt;Before releasing, I wanted to add documentation. And for documentation, I wanted to use &lt;a href="https://typedoc.org/"&gt;TypeDoc&lt;/a&gt;, so I can put the documentation right in the code and create pretty HTML files automatically.&lt;/p&gt;

&lt;p&gt;The problem? TypeDoc uses React and imports the React types into global space. So with &lt;code&gt;&amp;lt;Heading&amp;gt;Test&amp;lt;/Heading&amp;gt;&lt;/code&gt; returning a string, I was met with a TypeScript error.&lt;br&gt;
This left me with two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get rid of TypeDoc and hope that no one ever tries using my new library in the same repository as React&lt;/li&gt;
&lt;li&gt;Adjust my structure to the way that React does it&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Interlude: How does React do it?
&lt;/h2&gt;

&lt;p&gt;To figure out how React actually does this, I looked into two sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts"&gt;The React type definitions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/facebook/react/blob/master/packages/react-dom/src/server/ReactPartialRenderer.js"&gt;The code for rendering React DOM to strings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To summarize: &lt;code&gt;&amp;lt;div className='test'&amp;gt;Test&amp;lt;/div&amp;gt;&lt;/code&gt; (which is syntactic sugar for &lt;code&gt;React.createElement('div', { className: 'test' }, 'Test')&lt;/code&gt;) returns a so called element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For nested props (e. g. children), the element simply becomes a nested JSON structure.&lt;/p&gt;

&lt;p&gt;The render function then takes this structure and converts it into HTML, or, in case of the string renderer, into a string containing this HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second try: Returning elements
&lt;/h2&gt;

&lt;p&gt;Instead of directly returning the markdown string from createElement, we now return an element and then render the element in a separate render function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* @jsx createElement */&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createElement&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&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="kd"&gt;type&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="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt;
      &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;children&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="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;renderNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid element&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Heading&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;md&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;md&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Test&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;# Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yoyu can find the full, unabbreviated code as &lt;a href="https://github.com/dbartholomae/jsx-md/blob/v1.1.0/src/"&gt;version 1.1.0&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-life application of jsx-md
&lt;/h2&gt;

&lt;p&gt;When starting with jsx-md, I already had an application in mind. One of the first open source projects I wrote, was a script in CoffeeScript that created README files for open source projects. With jsx-md, I can now describe the components of a README file declaratively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kr"&gt;package&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DescriptionFromPkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LineBreak&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LineBreak&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;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;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;README.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DescriptionFromPkg&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kr"&gt;package&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall this lead me to write &lt;a href="https://github.com/dbartholomae/jsx-readme"&gt;jsx-readme&lt;/a&gt;, a library for describing README files in JSX.&lt;/p&gt;

&lt;h2&gt;
  
  
  But what about hooks?
&lt;/h2&gt;

&lt;p&gt;Nowadays, it is quite uncommon to write about React and never mention hooks even once. So what about hooks?&lt;/p&gt;

&lt;p&gt;Hooks are a solution by React to solve two problems: First, that the declarative description of a component is executed on every render, but some side effects shouldn't. And second, that there should be a way to tell an individual component to rerender with new data, without having to pass this information through the full component tree.&lt;/p&gt;

&lt;p&gt;Both are not that relevant to rendering a static markdown file - it doesn't really have side effects, and changing it happens on timeframes far too great to have the render function running continuously. But when working on jsx-readme, I did run into something that would be solved with hooks in React and that I couldn't yet solve with jsx-md:&lt;/p&gt;

&lt;p&gt;What if the data that I need to first render a component needs to be fetched asynchronously?&lt;/p&gt;

&lt;p&gt;This, fortunately, did no require a full implementation of hooks, or even of context. Instead, all I had to do, was make the rendering asynchronous and allow promises as children of elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* @jsx createElement */&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createElement&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&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="kd"&gt;type&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="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt;
      &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;children&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="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;renderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolv&lt;/span&gt;&lt;span class="p"&gt;(&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;renderNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mdAwait&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid element&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Heading&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;md&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;md&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;renderAsync&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Test&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&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;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strictEqual&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;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;# Test&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What now?
&lt;/h2&gt;

&lt;p&gt;First of all, if you found the article interesting and would like to hear more about tech, entrepreneurship, and how to bring the two together, then please feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Both &lt;a href="https://github.com/dbartholomae/jsx-md"&gt;jsx-md&lt;/a&gt; as well as &lt;a href="https://github.com/dbartholomae/jsx-readme"&gt;jsx-readme&lt;/a&gt; are open source and hopefully in a state where the code is easy to understand, so feel free to roam around a bit.&lt;/p&gt;

&lt;p&gt;And if you are interested – maybe you want to contribute to these repositories to allow even more markdown shenangians, and learn about the core of React on the way?&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Summarized – Tech and business books I read in 2020</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Mon, 04 Jan 2021 20:29:58 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/summarized-tech-and-business-books-i-read-in-2020-3kej</link>
      <guid>https://dev.to/the_startup_cto/summarized-tech-and-business-books-i-read-in-2020-3kej</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cy28akAi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gto1ysfe4if29alcgmdf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cy28akAi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gto1ysfe4if29alcgmdf.jpg" alt="Books"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In 2020 I thought I would find less time to read, due to the missing commute, my main reading time for 2019. This lead me to explicitly take time to read, and actually finish more books than in the year before. And most of them were great books, too! Here's a summary of what I learned from each.&lt;/p&gt;

&lt;p&gt;Overall there's three themes in the books I read:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ideation and Customer Development&lt;/strong&gt;, which I read up on due to my time testing out different startup ideas for half a year,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agile Software Architecture&lt;/strong&gt;, based on my general interest in how to get away without any planning and still not end up in a hot mess, and&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Product/Engineering Organization&lt;/strong&gt;, which I used mostly end of year when working with startups to scale their development teams.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've added affiliate links for everyone who wants to support me, but please do not feel pressured to use them. Feel free to also &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt; for more startup and tech related content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideation and Customer Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/2JHrGuo"&gt;Lean Customer Development (by Cindy Alvarez)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The first book I read about how to get to a working idea was also the best one from my perspective. The main idea is to talk to potential customers to find and validate ideas. It goes into detail how to find customers to talk to, which questions to ask and how to best ask them, how to find an MVP, and how to validate it with minimal effort. From a content perspective it is similar to The Mom Test (see below), but I found it to be a bit more practical.&lt;/p&gt;

&lt;p&gt;If you are building product in an early-stage startup (or any other agile organization), this is a &lt;strong&gt;must-read&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/2JGEwci"&gt;Loonshots (by Safi Bahcall)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;While the other books in this category are aimed (at least partially) at startups, Loonshots by Safi Bahcall is taking more of a bird's-eye view on the topic of innovation. The main idea from the book is that an organization can only grow to a certain size and remain innovative, because managing a big organization requires a different mindset. It proposes to split your organization in two, one part that is innovative and one that is operational, and tie them together close enough so ideas can permeate.&lt;/p&gt;

&lt;p&gt;It's an interesting read due to the examples for innovation, but not really necessary if you are building a startup.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/2LoCfDm"&gt;Play Bigger (by Al Ramadan)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Play Bigger introduces the idea of category design. Instead of just building a product, you build a new market demand, while at the same time building a product that fulfills the market demand and an organization that builds the product. The idea of doing everything at the same time didn't resonate that well with me, but finding a customer problem and then promoting the problem instead of your own solution is a very powerful tool.&lt;/p&gt;

&lt;p&gt;This book can bring you in the right mindset for selling a big vision as a CEO.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/35ajudD"&gt;The Mom Test (by Rob Fitzpatrick)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The Mom Test is on the recommended reading list by EF, the program I joined in March 2020. It's basically the same content as Lean Customer Development: Ask your customers what to build, but ask them in a way that does not incentivize them to lie. This is achieved by asking about how they currently do things and extrapolating from there, instead of asking how they would do things in the future.&lt;/p&gt;

&lt;p&gt;It's a good alternative to Lean Customer Development. It's a bit more famous, but personal I liked it slightly less. Feel free to choose either one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agile Software Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/2XgqH7v"&gt;Domain-Driven Design (by Eric Evans)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A modern classic in software development, Domain-Driven Design introduces ideas on how to structure code based on the business domain. There is a lot of very good insights, but for me the strongest idea is the domain language: Things in the code should not be called by technical terms, but by business terms instead. Ideally, the code written on the business logic should be understandable by someone who knows the business, but does not know how to code.&lt;/p&gt;

&lt;p&gt;It's a good fundamental read, but there are some signs of stiff Java structures in their that might not be that relevant with modern programming languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Clean Coder (by Robert C. Martin)
&lt;/h3&gt;

&lt;p&gt;The Clean Coder by Uncle Bob is one of the wider known standards in computer literature. Still, it is the only book in this list that I will not add a link to. The reason for this lie in the person of Robert C. Martin who I do not want to support financially in any way due to sexism and racism (see for example "The Politics" in &lt;a href="https://blog.wesleyac.com/posts/robert-martin"&gt;this article&lt;/a&gt;). Unfortunately I only became aware of this after I had already bought this book.&lt;/p&gt;

&lt;p&gt;After having read Clean Code (there's a good &lt;a href="https://github.com/ryanmcdermott/clean-code-javascript"&gt;JavaScript based summary&lt;/a&gt; if you haven't read that one and don't want to buy it), this was on my reading list for some time. The main ideas are true, and easily summarized: Be professional and don't let non-coders take code decisions for you. Say no if a manager pushes you to churn out code quickly that you know will break. And rely on good practices like tdd and practice.&lt;/p&gt;

&lt;p&gt;Even though I like the ideas mentioned in the book, I currently cannot recommend it for the reasons given above. My goal for this year is to find a book on this topic from a different author that I can recommend.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/2MxPFNP"&gt;Refactoring Databases (by Scott W. Ambler)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;One of the biggest obstacles I had over the last years when working in an agile way on projects, was adapting state-full applications like databases. Other than a state-less application, these cannot simply be replaced, instead they have to be migrated. This lead me to read Refactoring Databases. I did not fully read it, as it went into detail that I did not yet need, but the general ideas of how to refactor databases still stick with me, especially using views and database procedures to support multiple different interfaces (e. g. table structures) at the same time.&lt;/p&gt;

&lt;p&gt;If you mainly work with databases, it is a &lt;strong&gt;must read&lt;/strong&gt;. Otherwise, just read it if you struggle in this specific area.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/3ohTMeI"&gt;Building Evolutionary Architectures (by Neal Ford)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The other obstacle I still face a lot when working agile is the question of how much architecture needs to be defined top-down and how much can develop organically. Building Evolutionary Architectures by Neal Ford applies the idea of test-driven design to architecture, but instead of testing functionality, it introduces the idea of fitness functions to test for the boundary conditions that should come from the architecture, like performance, cost, or coupling. This way, assumptions and reasoning are made transparent, and the CI pipeline can enforce future adherence to these rules, while leaving open the opportunity for developers to evolve the architecture within these bounds.&lt;/p&gt;

&lt;p&gt;It's a very good read for anyone working as software architect, with the risk of changing how you view your job.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/35asCPr"&gt;Patterns of Enterprise Application Architecture (by Martin Fowler)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Another classic in this list, Patterns of Enterprise Application Architecture unfortunately was quite a dull read for me. Similar to Domain-Driven Design, the stale stench of Java emitted from the book took some getting used to, and that it is more a list of patterns than an interwoven story made it even harder to read. I still think that I learned a lot from it, so I would recommend to at least have a look at the book before dismissing it completely, just be prepared for a tough read.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/3rNc4XD"&gt;Living Documentation (by Cyrille Martraire)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The last book I read from this category was way more specialized than the others, but also the best of the bunch. In Cyrille Martraire's Living Documentation he poses the question how to transfer knowledge in an organization and which role documentation can play. The main idea is to not separate documentation from code, but instead to bring them as close together as possible, so that it becomes as hard as possible to change one without the other. Some ideas that especially stuck with me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Automatically create diagrams from code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add onboarding examples to the codebase to show how things are done around here&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do not duplicate documentation, but instead link to it, e. g. instead of describing the build process in the readme, link to the commented build config file&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a &lt;strong&gt;must read&lt;/strong&gt; for anyone who needs to introduce others to a codebase more than once per year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Product/Engineering Organizations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://basecamp.com/shapeup"&gt;Shape Up (by Ryan Singer)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Basecamp is known for some unconventional management techniques. With Shape Up, they published a free book on their product management philosophy. It revolves around 6 week blocks of time, so called bets, where teams work on their own on topics and find the best solution possible within this limited time. The main idea is to fully commit to an idea for 6 weeks and give full freedom to the team to implement during this time, but to reevaluate priorities without thinking about sunk cost after.&lt;/p&gt;

&lt;p&gt;The book is a short read, and if you are building a product organization will give you helpful ideas.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/3oinwsa"&gt;Team Topologies (by Matthew Skelton)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;While Shape Up is already almost old, Team Topologies is still the rage in tech organization literature. There are already quite a few blog posts summarizing the book (e. g. &lt;a href="https://medium.com/swlh/how-to-structure-teams-for-building-better-software-products-91e4dea021d"&gt;this one&lt;/a&gt;), but in a sentence, it is about the following ideas: Build teams that can create full customer solutions start to finish on their own and teams that create tools for enabling the first group.&lt;br&gt;
If you are building a product development organization of more than 10 people, read this book before it becomes too late.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/3hE3824"&gt;Radical Candor (by Kim Scott)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Those who know me (or have read my &lt;a href="http://github.com/dbartholomae"&gt;GitHub profile&lt;/a&gt;) know how highly I value transparency and honesty. This book is right up my alley: As a leader, be honest and direct about your feedback to your employees. Be polite, and, more importantly, empathic, but bring out any problems you might encounter as soon as possible, so people actually get a chance to change.&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;must-read&lt;/strong&gt; for anyone in a leadership position.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://amzn.to/2X7jdUz"&gt;Dynamic Reteaming (by Heidi Helfand)&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The last book I read this year I stumbled upon via this infographic which mentioned a lot of good books I had already read – and this one. Even though it talks about tech, it is actually a quite general book on organization and how to form and disband teams. It's a collection of methods to use when splitting, merging, building, or disbanding teams to make the process more effective and reduce the time needed for teams to grow together. Apart from some specific workshop formats, this also emphasized the idea for me that teams do not only need a ritual when forming but also when disbanding.&lt;/p&gt;

&lt;p&gt;If you are in the process of reorganizing your org, or if you have a lot of team churn, you should have a look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Others
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.smashingmagazine.com/printed-books/form-design-patterns/"&gt;Form Design Patterns (by Adam Silver)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The first outlier this year is a book about writing forms in HTML, CSS, and JavaScript. I stumbled upon it in &lt;a href="https://www.smashingmagazine.com/"&gt;Smashing Magazine&lt;/a&gt; (who also published it) and fell in love with the idea of an in-depth book about a small part of software engineering. There are a lot of great learnings in the book: How to make form elements that work in HTML, but work better if JavaScript is enabled. Or how to deal with accessibility. But it is not all sunshine. The HTML examples are hard to follow (I was reading it on my phone as all other books), so I skipped them. And some opinions are strongly colored by the author working mainly in frontend design, e. g. when he argues that usability trumps security.&lt;/p&gt;

&lt;p&gt;If you are interested in frontend development, this is a good read.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://amzn.to/355OJX9"&gt;Never Split the Difference (by Chris Voss)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The last book in this list is one of the, if not the best book I read in 2020. Never Split the Difference by Chris Voss is a book about negotiation, written by an FBI hostage negotiator. The main idea: Just say no. As long as you know you will get an offer that is at least as good, decline offer after offer until you get an offer that is too good to say no to. There's obviously more to it, but for the details, you will have to read the book.&lt;/p&gt;

&lt;p&gt;It's a &lt;strong&gt;must-read&lt;/strong&gt; for everyone, especially if you are negotiating your salary for a new job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outlook
&lt;/h2&gt;

&lt;p&gt;For next year, I have a couple of books on my digital shelf. Currently, I'm reading the &lt;a href="https://amzn.to/3pT1UTz"&gt;AWS Certified Solutions Architect Study Guide&lt;/a&gt;, and there are reading samples for a lot more books waiting for me. If you want to follow along, feel free to &lt;a href="https://twitter.com/intent/follow?original_referer=https%253A%252F%252Fstartup-cto.net%252F&amp;amp;ref_src=twsrc%5Etfw&amp;amp;region=follow_link&amp;amp;screen_name=The_Startup_CTO&amp;amp;tw_p=followbutton"&gt;follow me on Twitter&lt;/a&gt; for more startup and tech related content. And if you do, please tweet to me: What books did you read in 2020? And what did you learn from them?&lt;/p&gt;

</description>
      <category>books</category>
    </item>
    <item>
      <title>Preparing your repositories for Hacktoberfest</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Sat, 26 Sep 2020 13:46:50 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/preparing-your-repositories-for-hacktoberfest-1f4g</link>
      <guid>https://dev.to/the_startup_cto/preparing-your-repositories-for-hacktoberfest-1f4g</guid>
      <description>&lt;p&gt;Hacktoberfest is upon us. Starting 1st of October, we will all be celebrating Open Source contributions with &lt;a href="https://hacktoberfest.digitalocean.com/"&gt;tons of swag&lt;/a&gt;. But while contributors still have to wait for the 1st of October to get their contributions counted, now is still Preptember: The time to prepare your repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you should participate
&lt;/h2&gt;

&lt;p&gt;Open source lives from contribution, but contributing to repositories can be daunting. Hacktoberfest is a great opportunity to clean up your repository and make it as easy as possible for others to contribute.&lt;/p&gt;

&lt;p&gt;It is a great chance to get people who just entered our field into open source and use this to maybe also teach about some best practices like well-written commit messages or test coverage.&lt;/p&gt;

&lt;p&gt;Also, it might help you to get some more hands on features or bugs you just didn't get around to fix yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prepare your repo
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is officially needed
&lt;/h3&gt;

&lt;p&gt;You can find all official requirements on &lt;a href="https://hacktoberfest.digitalocean.com/details#maintainers"&gt;the Hacktoberfest homepage&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Basically there is no need to do any special setup to be eligable for Hacktoberfest. Any PR to an open-source project will count for contributors towards their swag-goal of 4 PRs, as it isn't obviously playing the rules or gets invalidated by the repo owner with help of the &lt;code&gt;invalid&lt;/code&gt; label.&lt;/p&gt;

&lt;h3&gt;
  
  
  Labels
&lt;/h3&gt;

&lt;p&gt;It is recommended to mark issues with the right labels, though, so participants can discover them more easily. So far I have seen mainly four labels in use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hacktoberfest&lt;/code&gt; is the label recommended by the Hacktoberfest team to mark any issue that is meant to be worked on during Hacktoberfest. Personally I've set the label color to #FF8AE2 which seems to be the main brand color used this year.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;help wanted&lt;/code&gt; indicates that this issue is open to outsiders and not only to existing maintainers of your repo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;good first issue&lt;/code&gt; shows that this issue is specifically meant for new contributors. Not all issues avaiable to outsiders fall in this category and you should put extra care into writing them to make it easy to get into your repo&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Make your repository ready for contribution
&lt;/h3&gt;

&lt;p&gt;When you have been working on your repository for a while, you have everything set up and are already familiar with all quirks. Neither is true for a fresh contributor.&lt;/p&gt;

&lt;p&gt;The established way to communicate this information is via a &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; file in the repositories root directory. If you don't have one yet, now is a good opportunity to create one. Here's &lt;a href="https://github.com/dbartholomae/jsx-readme/blob/main/CONTRIBUTING.md"&gt;one of mine for inspiration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But even if you already have contributing guidelines - are you sure they are up to date? Best to fully erase the repository from your disk, uninstall all special tools, and then follow your contributing guidelines yourself, until you have it running (including all steps required in your build pipeline like linting, building and testing). This will show you steps missing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create good first issues
&lt;/h3&gt;

&lt;p&gt;Creating a "good first issue" is easy, but creating a good "good first issue" is hard. You have to put yourself in the shoes of someone who just discovered your repository and knows literally nothing about it. Your issue description needs to&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give them enough background to understand what your project tries to achieve,&lt;/li&gt;
&lt;li&gt;explain in detail what the goal of this specific issue is,&lt;/li&gt;
&lt;li&gt;show them a starting point where they can start their work, and&lt;/li&gt;
&lt;li&gt;sell them to the idea of taking on this issue in particular.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a couple of examples I wrote specifically for Hacktoberfest:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dbartholomae/jsx-readme/issues/7"&gt;https://github.com/dbartholomae/jsx-readme/issues/7&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/dbartholomae/jsx-readme/issues/14"&gt;https://github.com/dbartholomae/jsx-readme/issues/14&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/dbartholomae/jsx-md/issues/1"&gt;https://github.com/dbartholomae/jsx-md/issues/1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional idea&lt;/strong&gt; : If you can take the time, then setting up a link where contributors can schedule a quick call with you for questions, or even 30 minutes of pair programming, might make it even easier for others to join in.&lt;/p&gt;

&lt;h3&gt;
  
  
  The legal stuff
&lt;/h3&gt;

&lt;p&gt;Depending on your organization's policies you might have to also set up Contributor License Agreements. I'm currently using &lt;a href="https://cla-assistant.io/"&gt;CLA assistant&lt;/a&gt; for this, but there are also &lt;a href="https://colineberhardt.github.io/cla-bot/"&gt;other bots&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Promoting your repo
&lt;/h2&gt;

&lt;p&gt;There already is &lt;a href="https://github.com/zenika-open-source/promote-open-source-project#readme"&gt;a good guide on promoting open source&lt;/a&gt;, so in this article I will focus on Hacktoberfest specifically.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a hacktoberfest label to the issues you want to be found.&lt;/li&gt;
&lt;li&gt;Post your project in the official Discord in the #issue-sharing channel.&lt;/li&gt;
&lt;li&gt;Tweet about it including #Hacktoberfest&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Have fun!
&lt;/h2&gt;

&lt;p&gt;In the end, even though it is noble to make open source contribution easier to others, remember that by maintaining an open source repo you already volunteered your time to the cause of open source. It is admirable if you can do even more by making it easy for others to contribute, but don't beat yourself up over it if you don't find the time to do everything mentioned in the article.&lt;/p&gt;

&lt;p&gt;The most important part is that you have fun with your work and do your best :)&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>Automatically generating README files with jsx-readme</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Thu, 24 Sep 2020 15:52:38 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/automatically-generating-readme-files-with-jsx-readme-8a1</link>
      <guid>https://dev.to/the_startup_cto/automatically-generating-readme-files-with-jsx-readme-8a1</guid>
      <description>&lt;p&gt;It's the first thing that greets you when you look at an npm package, be it on npm itself or in the source on GitHub: The README.md file. But if you are like me, then writing documentation never was your strong suite - let alone maintaining and updating it.&lt;/p&gt;

&lt;p&gt;What if, instead of having to update the README file manually whenever your code changes, the README could just stay up-to-date auto-magically? What if instead of having to remember which badges you need to add, this would all be done for you?&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing jsx-readme
&lt;/h2&gt;

&lt;p&gt;Here's an example of using &lt;code&gt;jsx-readme&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// We need to tell the JSX transpiler that in this file,
// instead of React we use the custom createElement and Fragment
// functions from jsx-readme
/* @jsx MD */
/* @jsxFrag Fragment */
import type { Component } from "jsx-readme";
import MD, {
  BadgesFromPkg,
  CodecovBadge,
  DescriptionFromPkg,
  ExamplesFromPkg,
  Fragment,
  GithubWorkflowBadge,
  HomepageFromPkg,
  renderToFile,
  TitleFromPkg,
} from "jsx-readme";
import { Heading, InlineCode, LineBreak } from "jsx-md";
import pkg from "./package.json";

const Readme: Component = () =&amp;gt; (
  &amp;lt;Fragment&amp;gt;
    {/* Create a header with title, badges and description inferred from package.json */}
    &amp;lt;TitleFromPkg pkg={pkg} /&amp;gt;
    &amp;lt;BadgesFromPkg pkg={pkg} /&amp;gt;
    {/* Add additional badges. */}
    &amp;lt;CodecovBadge pkg={pkg} /&amp;gt;
    &amp;lt;GithubWorkflowBadge pkg={pkg} workflowName="Build and deploy" /&amp;gt;
    &amp;lt;LineBreak /&amp;gt;
    &amp;lt;DescriptionFromPkg pkg={pkg} /&amp;gt;
    {/* You can use the components from jsx-md to build custom markdown. */}
    &amp;lt;Heading level={2}&amp;gt;Installation&amp;lt;/Heading&amp;gt;
    Add &amp;lt;InlineCode&amp;gt;jsx-readme&amp;lt;/InlineCode&amp;gt; to your{" "}
    &amp;lt;InlineCode&amp;gt;devDependencies&amp;lt;/InlineCode&amp;gt; and install it. I recommend using
    it with &amp;lt;InlineCode&amp;gt;ts-node&amp;lt;/InlineCode&amp;gt;. Then all you need to do is add a
    file like in the example below and run it via{" "}
    &amp;lt;InlineCode&amp;gt;ts-node&amp;lt;/InlineCode&amp;gt; whenever you want to create a new version
    of the &amp;lt;InlineCode&amp;gt;README&amp;lt;/InlineCode&amp;gt;.
    &amp;lt;LineBreak /&amp;gt;
    &amp;lt;LineBreak /&amp;gt;
    {/* Create an example section based on all files from the example directory set up in package.json */}
    &amp;lt;ExamplesFromPkg pkg={pkg} /&amp;gt;
    {/* Create a section linking to the homepage from package.json */}
    &amp;lt;HomepageFromPkg pkg={pkg} /&amp;gt;
  &amp;lt;/Fragment&amp;gt;
);

void renderToFile("./README.md", &amp;lt;Readme /&amp;gt;);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see, &lt;code&gt;jsx-readme&lt;/code&gt; uses a React-like JSX syntax to define Markdown. It is built on top of &lt;code&gt;jsx-md&lt;/code&gt; which you can also use independently, or together with &lt;code&gt;jsx-readme&lt;/code&gt;, depending on your needs.&lt;/p&gt;

&lt;p&gt;Since JSX is basically JavaScript, we can do in this file whatever we want. In this simple case we just import the local &lt;code&gt;package.json&lt;/code&gt; file to read from it, but why not e. g. query the GitHub API to figure out who contributed and then adding them to your Readme?&lt;/p&gt;

&lt;p&gt;And since it is just a TypeScript file, you can simply run it via e. g. &lt;code&gt;ts-node&lt;/code&gt; instead of having to learn yet another CLI tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what does it look like?
&lt;/h2&gt;

&lt;p&gt;The best way to understand the result of the script above is to just look at &lt;a href="https://github.com/dbartholomae/jsx-readme/"&gt;the package itself&lt;/a&gt; - &lt;code&gt;jsx-readme&lt;/code&gt; uses it to create its own README.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Moist code - Why code should not be completely DRY</title>
      <dc:creator>Daniel Bartholomae</dc:creator>
      <pubDate>Fri, 21 Aug 2020 13:08:00 +0000</pubDate>
      <link>https://dev.to/the_startup_cto/moist-code-why-code-should-not-be-completely-dry-4dgg</link>
      <guid>https://dev.to/the_startup_cto/moist-code-why-code-should-not-be-completely-dry-4dgg</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cYwFaUI4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://startup-cto.net/content/images/2020/08/BA6C6787-4DD8-498B-B4ED-1E810C771B38.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cYwFaUI4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://startup-cto.net/content/images/2020/08/BA6C6787-4DD8-498B-B4ED-1E810C771B38.jpeg" alt="Moist code - Why code should not be completely DRY"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At some point, each developer encounters the term “dry code”. It comes from the acronym DRY for “Don’t repeat yourself”. But if code is too dry, it easily can become brittle. To keep the code moldable, it is helpful to leave in a bit of repetition - a concept I personally like to call “Moist code”.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple example
&lt;/h2&gt;

&lt;p&gt;The core idea behind “Don’t repeat yourself” is, as the name suggests, to not repeat code. Let’s look at a simple example in TypeScript React:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;&amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;&amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;This is the homepage&amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So far, there isn't much repetition. Let's add a contact page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  if (location === '/contact') {
    return &amp;lt;&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
        &amp;lt;ul&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;main&amp;gt;
        This is the homepage
      &amp;lt;/main&amp;gt;
    &amp;lt;/&amp;gt;
  }

  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      Contact us!
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we have a a bit of duplication. Time to split our paths and see the difference between &lt;strong&gt;dry&lt;/strong&gt; and &lt;strong&gt;moist&lt;/strong&gt; code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dry code
&lt;/h2&gt;

&lt;p&gt;Let's remove as much repetition as possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation /&amp;gt;
    &amp;lt;Router location={location} /&amp;gt;
  &amp;lt;/&amp;gt;
 }

function Navigation () {
  return (
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        {
          [{
            children: 'Home',
            to: '/'
          }, {
            children: 'Contact',
            to: '/contact'
          }].map(props =&amp;gt; &amp;lt;NavLink ...props key={to} /&amp;gt;)
        }
     &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
  )
}

function NavLink ({ to, children }) {
  return &amp;lt;li&amp;gt;&amp;lt;Link to={to}&amp;gt;{children}&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
}

function Router ({ location }) {
  return &amp;lt;main&amp;gt;&amp;lt;Content location={location} /&amp;gt;&amp;lt;/main&amp;gt;
}

function Content ({ location }) {
  if (location === '/profile') {
    return 'Contact us!'
  }
  return 'This is the homepage'
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We have removed every repetition we could find. Time to add the next feature. We want a login page, and users that are not logged in, should automatically see the login form instead of any other page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation /&amp;gt;
    &amp;lt;Router location={location} /&amp;gt;
  &amp;lt;/&amp;gt;
 }

function Navigation () {
  return (
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        {
          [{
            children: 'Home',
            to: '/'
          }, {
            children: 'Contact',
            to: '/contact'
          }, {
            children: 'Login',
            to: '/login'
          }].map(props =&amp;gt; &amp;lt;NavLink ...props key={to} /&amp;gt;)
        }
     &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
  )
}

function NavLink ({ to, children }) {
  return &amp;lt;li&amp;gt;&amp;lt;Link to={to}&amp;gt;{children}&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
}

function Router ({ location }) {
  return &amp;lt;main&amp;gt;&amp;lt;Content location={location} /&amp;gt;&amp;lt;/main&amp;gt;
}

function Content ({ location }) {
  const isUserLoggedIn = useLoggedInState()
  if (!isUserLoggedIn || location === '/login') {
    return 'This is a login form'
  }
  if (location === '/profile') {
    return 'Contact us!'
  }
 return 'This is the homepage'
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Thanks to the strict removal of duplication, we only had to add another link object in the NavBar and another if statement with new content in the &lt;code&gt;Content&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;Our app is growing, and we have a UX team now. After some user testing they figured out that it is really confusing to see the &lt;code&gt;Navigation&lt;/code&gt; on the login screen, even though seemingly none of the links work. So let's fix this by not showing any links in the &lt;code&gt;NavBar&lt;/code&gt; on the login page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation location={location} /&amp;gt;
    &amp;lt;Router location={location} /&amp;gt;
  &amp;lt;/&amp;gt;
 }

function Navigation ({ location }) {
 return (
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      { location !== '/login' &amp;amp;&amp;amp; &amp;lt;NavLinks /&amp;gt; }
   &amp;lt;/nav&amp;gt;
  )
}

function NavLinks () {
  return &amp;lt;ul&amp;gt;
    {
      [{
        children: 'Home',
        to: '/'
      }, {
        children: 'Contact',
        to: '/contact'
      }, {
        children: 'Login',
        to: '/login'
      }].map(props =&amp;gt; &amp;lt;NavLink ...props key={to} /&amp;gt;)
    }
  &amp;lt;/ul&amp;gt;
}

function NavLink ({ to, children }) {
  return &amp;lt;li&amp;gt;&amp;lt;Link to={to}&amp;gt;{children}&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
}

function Router ({ location }) {
  return &amp;lt;main&amp;gt;&amp;lt;Content location={location} /&amp;gt;&amp;lt;/main&amp;gt;
}

function Content ({ location }) {
  const isUserLoggedIn = useLoggedInState()
  if (!isUserLoggedIn || location === '/login') {
    return 'This is a login form'
  }
  if (location === '/contact') {
    return 'Contact us!'
  }
 return 'This is the homepage'
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Again, thanks to the small amount of duplication, the change is small and easy. But something starts to feel off about the code. Let's see what happens after the next feature.&lt;/p&gt;

&lt;p&gt;Our marketing team just called. They had this great idea for a special easter promotion: They want to hide a special easter egg on the page, where if you click the login link on the contacts page, it will instead redirect you to the promotion website. It's just a change in a link, so how hard can it be?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation location={location} /&amp;gt;
    &amp;lt;Router location={location} /&amp;gt;
  &amp;lt;/&amp;gt;
 }

function Navigation ({ location }) {
 return (
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      { location !== '/login' &amp;amp;&amp;amp; &amp;lt;NavLinks location={location} /&amp;gt; }
   &amp;lt;/nav&amp;gt;
  )
}

function NavLinks ({ location }) {
  return &amp;lt;ul&amp;gt;
    {
      [{
        children: 'Home',
        to: '/'
      }, {
        children: 'Contact',
        to: '/contact'
      }, {
        children: 'Login',
        to: location === '/contact'
          ? 'https:///our-special-promotion.com'
          : '/login'
      }].map(props =&amp;gt; &amp;lt;NavLink ...props /&amp;gt;)
    }
  &amp;lt;/ul&amp;gt;
}

function NavLink ({ to, children }) {
  return &amp;lt;li&amp;gt;&amp;lt;Link to={to}&amp;gt;{children}&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
}

function Router ({ location }) {
  return &amp;lt;main&amp;gt;&amp;lt;Content location={location} /&amp;gt;&amp;lt;/main&amp;gt;
}

function Content ({ location }) {
  const isUserLoggedIn = useLoggedInState()
  if (!isUserLoggedIn || location === '/login') {
    return 'This is a login form'
  }
  if (location === '/contact') {
    return 'Contact us!'
  }
 return 'This is the homepage'
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's look at our moist code team, to see how they are doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moist code
&lt;/h2&gt;

&lt;p&gt;In moist space, we still have the starting code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  if (location === '/contact') {
    return &amp;lt;&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
        &amp;lt;ul&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;main&amp;gt;
        This is the homepage
      &amp;lt;/main&amp;gt;
    &amp;lt;/&amp;gt;
  }

  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      Contact us!
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We don't mind a bit of repetition, so instead of refactoring right away, let's add the next feature first, the login page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  const isUserLoggedIn = useLoggedInState()
  if (!isUserLoggedIn) {
    return &amp;lt;&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;main&amp;gt;
        This is a login form
      &amp;lt;/main&amp;gt;
    &amp;lt;/&amp;gt;
  }

  if (location === '/login') {
    return &amp;lt;&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;main&amp;gt;
        This is a login form
      &amp;lt;/main&amp;gt;
    &amp;lt;/&amp;gt;
  }

  if (location === '/contact') {
    return &amp;lt;&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
        &amp;lt;ul&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;Link to='/login'&amp;gt;Login&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;main&amp;gt;
        Contact us!
      &amp;lt;/main&amp;gt;
    &amp;lt;/&amp;gt;
  }

  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/login'&amp;gt;Login&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      This is the homepage
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's extract some of the concepts that seem to emerge, but not too many:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  const isUserLoggedIn = useLoggedInState()
  if (!isUserLoggedIn) {
    return &amp;lt;LoginPage /&amp;gt;
  }

  return &amp;lt;Router /&amp;gt;
}

function Router ({ location }) {
  if (location === '/login') {
    return &amp;lt;LoginPage /&amp;gt;
  }

  if (location === '/contact') {
    return &amp;lt;ContactPage /&amp;gt;
  }

  return &amp;lt;HomePage /&amp;gt;
}

function LoginPage () {
  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/login'&amp;gt;Login&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
   &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      This is a login form
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function ContactPage () {
  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/login'&amp;gt;Login&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      Contact us!
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function HomePage () {
  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/login'&amp;gt;Login&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      This is the homepage
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There is still some repetition left and our fingers might be itching to remove it, but let's add the next feature first: Not showing links on the login page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  const isUserLoggedIn = useLoggedInState()
  if (!isUserLoggedIn) {
    return &amp;lt;LoginPage /&amp;gt;
  }

  return &amp;lt;Router /&amp;gt;
}

function Router ({ location }) {
  if (location === '/login') {
    return &amp;lt;LoginPage /&amp;gt;
  }

  if (location === '/contact') {
    return &amp;lt;ContactPage /&amp;gt;
  }

  return &amp;lt;HomePage /&amp;gt;
}

function LoginPage () {
  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
   &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      This is a login form
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function ContactPage () {
  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/login'&amp;gt;Login&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      Contact us!
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function HomePage () {
  return &amp;lt;&amp;gt;
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/'&amp;gt;Home&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/contact'&amp;gt;Contact&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;Link to='/login'&amp;gt;Login&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    &amp;lt;main&amp;gt;
      This is the homepage
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That was easy. We literally just had to delete a couple lines. With our new knowledge about how our &lt;code&gt;Navigation&lt;/code&gt; can change, let's now extract it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  const isUserLoggedIn = useLoggedInState()
  if (!isUserLoggedIn) {
    return &amp;lt;LoginPage /&amp;gt;
  }

  return &amp;lt;Router /&amp;gt;
}

function Router ({ location }) {
  if (location === '/login') {
    return &amp;lt;LoginPage /&amp;gt;
  }

  if (location === '/contact') {
    return &amp;lt;ContactPage /&amp;gt;
  }

  return &amp;lt;HomePage /&amp;gt;
}

function LoginPage () {
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation /&amp;gt;
    &amp;lt;main&amp;gt;
      This is a login form
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function ContactPage () {
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation
      links={[{
        children='Home'
        to='/'
      }, {
        children='Contact'
        to='/contact'
      }, {
        children='Login'
        to='/login'
      }]}
    /&amp;gt;
    &amp;lt;main&amp;gt;
      Contact us!
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function HomePage () {
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation
      links={[{
        children='Home'
        to='/'
      }, {
        children='Contact'
        to='/contact'
      }, {
        children='Login'
        to='/login'
      }]}
    /&amp;gt;
    &amp;lt;main&amp;gt;
      This is the homepage
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function Navigation ({ links }) {
  return (
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      { links &amp;amp;&amp;amp;
        &amp;lt;ul&amp;gt;
          {
            links.map({ children, to } =&amp;gt;
              &amp;lt;li key={to}&amp;gt;&amp;lt;Link to={to}&amp;gt;{children}&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;)
          }
       &amp;lt;/ul&amp;gt;
      }
    &amp;lt;/nav&amp;gt;
  )
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Even though there is a lot more repetition in this code then in the last version (e. g. each page having to explicitly use &lt;code&gt;main&lt;/code&gt;), it already feels a bit cleaner and more lightweight. Let's see what happens with the marketing request to have a special link for the easter egg hunt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function App () {
  const location = useLocation()
  const isUserLoggedIn = useLoggedInState()
  if (!isUserLoggedIn) {
    return &amp;lt;LoginPage /&amp;gt;
  }

  return &amp;lt;Router /&amp;gt;
}

function Router ({ location }) {
  if (location === '/login') {
    return &amp;lt;LoginPage /&amp;gt;
  }

  if (location === '/contact') {
    return &amp;lt;ContactPage /&amp;gt;
  }

  return &amp;lt;HomePage /&amp;gt;
}

function LoginPage () {
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation /&amp;gt;
    &amp;lt;main&amp;gt;
      This is a login form
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function ContactPage () {
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation
      links={[{
        children='Home'
        to='/'
      }, {
        children='Contact'
        to='/contact'
      }, {
        children='Login'
        to='https:///our-special-promotion.com'
      }]}
    /&amp;gt;
    &amp;lt;main&amp;gt;
      Contact us!
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function HomePage () {
  return &amp;lt;&amp;gt;
    &amp;lt;Navigation
      links={[{
        children='Home'
        to='/'
      }, {
        children='Contact'
        to='/contact'
      }, {
        children='Login'
        to='/login'
      }]}
    /&amp;gt;
    &amp;lt;main&amp;gt;
      This is the homepage
    &amp;lt;/main&amp;gt;
  &amp;lt;/&amp;gt;
}

function Navigation ({ links }) {
  return (
    &amp;lt;nav&amp;gt;
      &amp;lt;h2&amp;gt;Navigation&amp;lt;/h2&amp;gt;
      { links &amp;amp;&amp;amp;
        &amp;lt;ul&amp;gt;
          {
            links.map({ children, to } =&amp;gt;
              &amp;lt;li key={to}&amp;gt;&amp;lt;Link to={to}&amp;gt;{children}&amp;lt;/Link&amp;gt;&amp;lt;/li&amp;gt;)
          }
       &amp;lt;/ul&amp;gt;
      }
    &amp;lt;/nav&amp;gt;
  )
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;One link replaced. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened?
&lt;/h2&gt;

&lt;p&gt;The main difference between our two approaches is that with the moist code approach, we gave the code more time to dry. Instead of forcing it into a specific form early on based on assumptions only, we first collected a bit more requirements and only then started removing repetition.&lt;/p&gt;

&lt;p&gt;This lead us to use the pattern of composition instead of inheritance for the &lt;code&gt;Navigation&lt;/code&gt;, which made it easier to individualize the &lt;code&gt;Navigation&lt;/code&gt; by route and gave us more flexibility for changing the code in the places that actually mattered for our new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;When writing code we usually don't know from the beginning every requirement that will come ahead. Especially for web applications the requirements might never fully settled and always keep in motion. If we optimize our code only step-by-step as we discover the requirements, we can keep it moist and malleable. If we aim for perfectly DRY code, it will become brittle and inflexible to change.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
