<?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>Forem: IT Minds</title>
    <description>The latest articles on Forem by IT Minds (@itminds).</description>
    <link>https://forem.com/itminds</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%2Forganization%2Fprofile_image%2F796%2F47de057a-d004-4bff-ad08-2066b179011c.png</url>
      <title>Forem: IT Minds</title>
      <link>https://forem.com/itminds</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/itminds"/>
    <language>en</language>
    <item>
      <title>Legacy code: Why it happens and how to handle it</title>
      <dc:creator>Christoffer Bjerge</dc:creator>
      <pubDate>Wed, 01 Mar 2023 14:33:22 +0000</pubDate>
      <link>https://forem.com/itminds/legacy-code-why-it-happens-and-how-to-handle-it-3lm7</link>
      <guid>https://forem.com/itminds/legacy-code-why-it-happens-and-how-to-handle-it-3lm7</guid>
      <description>&lt;h1&gt;
  
  
  Legacy or just old?
&lt;/h1&gt;

&lt;p&gt;Ask five developers for a definition of legacy code, and you’ll get six different answers. We all have different opinions on what constitutes legacy code, but most can agree on this: it's old code still in use, and it's hard to work with.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We could spend a lot more time trying to define it, but smarter people than me have done that more eloquently than I could, so I'll link some good articles at the end for further reading. They also go into more depth than me, so I recommend checking them out after this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However you want to define it, we all know it when we see it, and nobody likes working on legacy code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why does code become legacy?
&lt;/h1&gt;

&lt;p&gt;So.. If everyone can recognize it, and nobody likes working on it, why does it happen? Well, a few things: feature bloat, tech debt, time and lack of testing are the main culprits. So, taking them one at a time...&lt;/p&gt;

&lt;h3&gt;
  
  
  Time
&lt;/h3&gt;

&lt;p&gt;This is kind of the big one of the group that often leads directly or indirectly to the other points. Legacy code is also old code which could mean a few things in my experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current fundamentals like Entity Framework or web frameworks were maybe not available or widely adopted at the time, which resulted in custom-made or outdated/deprecated solutions we have to adapt to and basically learn from scratch.&lt;/li&gt;
&lt;li&gt;The original developers were likely used to working in a totally different paradigm which means it can look either odd or illogical to our modern sensibilities.&lt;/li&gt;
&lt;li&gt;Inertia - The longer a product is in use the harder it becomes to make meaningfully large changes, making it easier to focus on things in the short-term like:&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Just one more feature
&lt;/h3&gt;

&lt;p&gt;When a product is successful for many years, it will inevitably gain more and more features along the road because features drive sales/profits/etc. &lt;br&gt;
This means that the older the product, the more stacked (bloated) with features it might become. This is great in the short term because each feature makes the product more marketable, but if not planned correctly this will make the codebase more and more unmanageable.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Tech debt
&lt;/h3&gt;

&lt;p&gt;Another thing that will often happen over time is tech debt. The reality of creating any kind of product is the need to drive sales which means features like I mentioned above, but this and tight deadlines will often mean neglecting what doesn't directly drive sales: maintenance. &lt;br&gt;
On a vehicle, this means changing the oil, checking the tires or the like, but for a codebase this means refactoring, updating to newer versions, actually going back over the quick fixes and to-dos that will accumulate over time and so on. All of these things don't affect profitability in a noticeable way in the short term, but in the long run it is what makes a codebase become unmaintainable and eventually legacy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of tests
&lt;/h3&gt;

&lt;p&gt;We know we should all write more and better tests (check out &lt;a href="https://dev.to/itminds/writing-good-tests-part-1-what-makes-a-test-bad-4gfd"&gt;this article&lt;/a&gt; on writing good tests by one of my colleagues), but often it's something we'll postpone for some other day. When lack of testing becomes a year's long worth of lack, it can be hard to know what breaks when we have to make changes to the code.&lt;/p&gt;

&lt;h1&gt;
  
  
  So.. How do we handle it?
&lt;/h1&gt;

&lt;p&gt;I am by no means an expert in this subject, but I will give my two cents on what I have experienced to be effective and necessary when working with legacy code and especially when you're coming in from outside as a consultant or new hire.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long or short term
&lt;/h3&gt;

&lt;p&gt;My perspective is going to be influenced by my position as a consultant and is therefore on a limited scope and timeframe, but if a codebase has reached the legacy stage, it will usually be necessary in the long term to eventually rewrite it using best practices from scratch. &lt;/p&gt;

&lt;h3&gt;
  
  
  Communication and estimation
&lt;/h3&gt;

&lt;p&gt;Working on legacy code is difficult, but one of the things I have found most difficult when working on legacy code is estimation which is sometimes more of an art than a science - and with legacy code it is always more challenging. &lt;br&gt;
The answer to this is cliché for a reason: communication. Be open and up front about your estimates, and don't be afraid to readjust when you run into problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leave it better than you found it
&lt;/h3&gt;

&lt;p&gt;When adding functionality to a class that is already 8000 lines long and when the if-statement is already nested several levels beyond what is comfortable, it's tempting to think &lt;em&gt;oh well, what's another few lines&lt;/em&gt;. &lt;br&gt;
But, that's just making the problem slightly worse for next time / the next developer. You should always try to leave it slightly better than you found it - if there isn't enough time to do a proper refactor of the class/whatever you're working in, you should at least not make it worse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code style
&lt;/h3&gt;

&lt;p&gt;This may mostly be relevant for developers in a consultant role like myself, but I'm including it still because I think it's important.&lt;br&gt;
When working on - and especially adding to - legacy code, it's tempting to rewrite older code completely in a much more efficient style. And I'm not saying you shouldn't do this, but I think it's very important to keep in mind who else will be working on the code. What I mean is that you should be looking for a good middle ground between something you can be proud of doing and something that isn't too foreign to developers used to working in older code. &lt;br&gt;
You can either try to document and comment your code in such a sufficient manner that the next developer knows how to handle your code, or you can try to adapt to the existing code style. This again comes down to the above point of communication: Talk to your lead/fellow developers etc., and figure out the best solution.&lt;/p&gt;

&lt;p&gt;In the end, it doesn't matter how clever your improvements are, as they are useless if the next developer to look at it won't know what they're looking at. Like making a speech, you have to know your audience and adjust your choice of words accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  To sum up
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Legacy status is almost inevitable for any product over time unless it is actively avoided and planned for.&lt;/li&gt;
&lt;li&gt;Communication is key - legacy is difficult to work with and estimating tasks for legacy doubly so. Make sure to communicate any problems, questions or ideas you have along the way.&lt;/li&gt;
&lt;li&gt;Don't take the easy way out - try to leave it better than you found it, otherwise you're just making the problem worse for the next person.&lt;/li&gt;
&lt;li&gt;The code you add or change will have to be understood and expanded upon by other developers - adjust accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below are a few good articles both defining legacy code and going more in depth with handling legacy code - among other things the best ways to implement testing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://understandlegacycode.com/blog/key-points-of-working-effectively-with-legacy-code/" rel="noopener noreferrer"&gt;Key Points of working effectively with legacy code&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://enkonix.com/blog/legacy-code/" rel="noopener noreferrer"&gt;Everything You Need to Know About Legacy Code&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.infoq.com/podcasts/working-effectively-legacy-code/" rel="noopener noreferrer"&gt;Podcast (transcript) with Michael Feathers on legacy code&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Is Lottie a Hottie? Going Through the Basics of the Great Tiny Animation Format that is Lottie-files</title>
      <dc:creator>Carsten Eliasen</dc:creator>
      <pubDate>Wed, 01 Jun 2022 09:08:35 +0000</pubDate>
      <link>https://forem.com/itminds/is-lottie-a-hottie-lets-go-through-the-basics-of-this-great-tiny-animation-format-4h9i</link>
      <guid>https://forem.com/itminds/is-lottie-a-hottie-lets-go-through-the-basics-of-this-great-tiny-animation-format-4h9i</guid>
      <description>&lt;p&gt;Vectorized animations and 3.000 times smaller than a GIF? That sounds almost utopian. However, the Lottie-file (or, simply a Lottie) is a reality, and for me as a UX Designer, it allows me to create custom animations that can easily be implemented in code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xNyEaDHF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ct86hebp9tzuaq9og2gr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xNyEaDHF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ct86hebp9tzuaq9og2gr.gif" alt="Image description" width="480" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this blogpost, I am going to explore which building blocks we can use in order to build a bridge between UX and development. Lottie-files might be only one example of a such building block, but all bricks are important when building a strong foundation. Moreover, although I am writing about this subject as a designer, it is equally as relevant for developers. &lt;/p&gt;

&lt;h2&gt;
  
  
  So, what is Lottie?
&lt;/h2&gt;

&lt;p&gt;Lottie is a JSON-based format that makes it easy for me as a designer to export my custom animations without losing any quality, while maintaining full scalability.&lt;br&gt;
It makes animations easy to implement, and nice-to-have elements like these could easily become need-to-have in the future as it adds dynamic and gamification aspects, which might not only enhance User Experience, but also Developer Experience as well. &lt;/p&gt;

&lt;h2&gt;
  
  
  What do developers think of Lottie?
&lt;/h2&gt;

&lt;p&gt;Seeing as I am not a developer myself, I thought it wise to ask one of my friends, who is a Tech Lead at IT Minds, what he thinks of Lottie as he has worked with it on an internal project. &lt;/p&gt;

&lt;p&gt;In general, he's a big fan of Lottie, among other things, because it's easy to work with both as a developer and as a designer. The only downside is the bundle size:&lt;/p&gt;

&lt;p&gt;"It's easy for both developer and designer, but it comes at the expense of bundle size and performance. So it works best when you are working with more complex animations"&lt;/p&gt;

&lt;h2&gt;
  
  
  So, how easy is it?
&lt;/h2&gt;

&lt;p&gt;Luckily for this blog post, I have a great deal of friends at IT Minds. As a trial run, I asked one of our software developers, Stefan, to give it a go at implementing a Lottie-animation in a React environment. This is what he said about the experience:&lt;/p&gt;

&lt;p&gt;"As someone who had not worked with Lotties before, it was a very easy and straight-forward process to implement them in a new or existing project. The Lottie-React library had fine tools for using and customizing the Lotties for specific scenarios and interactions."&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it easy for a UX designer to make the animations?
&lt;/h2&gt;

&lt;p&gt;Some of the questions I have been asked from my developer-colleagues revolves around whether it is easy for me to make these animations. The truth is that you need some experience in Adobe Illustrator and Aftereffects, but apart from that it really is quite easy. &lt;br&gt;
These are the 'simple' steps you have to go through, when making an animation:&lt;/p&gt;

&lt;p&gt;1) Design an icon, such as the one below, in Illustrator&lt;br&gt;
2) Import it into Aftereffects, and create the animation&lt;br&gt;
3) Export the animation as a JSON-file, by using a plugin called Bodymovin&lt;/p&gt;

&lt;p&gt;Below you can see an example of a gif of an animation I have made on the basis of these steps. You can see the Lottie-file right &lt;a href="https://lottiefiles.com/share/yr1jd6za"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x170w9P3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/urm5si5on35tnaghbars.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x170w9P3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/urm5si5on35tnaghbars.gif" alt="Image description" width="480" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Lottie a Hottie?
&lt;/h2&gt;

&lt;p&gt;My conclusion is yes! I have not yet talked to anyone who can see any major downsides. Easy implementable animations makes the design – and development experience - better even if they only constitute one of the many bricks on the bridge between design and development.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Beginner's Guide to Penetration Testing (Part 2)</title>
      <dc:creator>Hjalte Abelskov</dc:creator>
      <pubDate>Wed, 04 May 2022 11:23:20 +0000</pubDate>
      <link>https://forem.com/itminds/a-beginners-guide-to-penetration-testing-part-2-45h8</link>
      <guid>https://forem.com/itminds/a-beginners-guide-to-penetration-testing-part-2-45h8</guid>
      <description>&lt;p&gt;Hey everyone! I’m back at it with another blogpost about information-security or, more specifically, penetration testing.&lt;/p&gt;

&lt;p&gt;This blogpost constitutes the second part in my series on penetration testing. If you are not familiar with penetration testing in general, I highly recommend checking out my first post above where I go over penetration testing methodology and show you some tools that can be useful when enumerating a target.&lt;/p&gt;

&lt;p&gt;To finish off this mini series on penetration testing, my blogpost today will go over a target from HackTheBox. To be more precise, we'll be looking at one of their retired boxes named &lt;strong&gt;Blocky&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hu81GEk3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v64v4yxrck6iravvzqoz.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hu81GEk3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v64v4yxrck6iravvzqoz.PNG" alt="Image description" width="302" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For those of you who are new to HackTheBox, think of it as a big hacking playground with lots of targets varying in difficulty. For every target out there, you need to first &lt;strong&gt;gain a foothold&lt;/strong&gt; and then &lt;strong&gt;escalate your privileges&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Two .txt files exist on the target, and your goal is to submit the contents of both. The first .txt file is named user.txt, and submitting that will prove to HackTheBox that you’ve achieved &lt;strong&gt;foothold&lt;/strong&gt;. The second .txt file is named root.txt (even on Windows), and submitting that will prove to HackTheBox that you’ve achieved &lt;strong&gt;escalated privileges&lt;/strong&gt;, a.k.a root privileges (Administrator for Windows).&lt;/p&gt;

&lt;p&gt;The foothold flag is usually the hardest to obtain, or at least the one that requires the most steps, since it requires combing through lots of information about the services running on the server. I have intentionally not included some of the dead ends I ran into while doing this box.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 1: Foothold
&lt;/h1&gt;

&lt;p&gt;We are given the following IP-address: &lt;strong&gt;10.10.10.37&lt;/strong&gt;&lt;br&gt;
First thing I did was to run an &lt;strong&gt;nmap scan&lt;/strong&gt; to see which services are running on the target. I specified -sV for versions and -p- for all ports. -oN tells nmap to output in “nmap format” to the specified file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X4qqLCmu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ec60kfaz9ap9r3hflu3r.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X4qqLCmu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ec60kfaz9ap9r3hflu3r.PNG" alt="Image description" width="880" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we see ftp, ssh, http and a minecraft server.&lt;br&gt;
Let’s go ahead and visit the website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8tgX4EEQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0b7wjnqy2bcdehytlwpq.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8tgX4EEQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0b7wjnqy2bcdehytlwpq.PNG" alt="Image description" width="880" height="573"&gt;&lt;/a&gt;&lt;br&gt;
It looks like a blog of some sort. Some might recognize that it resembles the standard Twenty Seventeen Wordpress theme. To find out more, I started a subdirectory-scan with &lt;strong&gt;Gobuster&lt;/strong&gt; while continuing to explore the site manually. Here, I specified -u for url and -w for wordlist.&lt;/p&gt;

&lt;p&gt;Looking at the source code, network traffic, etc. did not yield anything particularly useful, but eventually my Gobuster scan finished and showed me the following information:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ncRfIYI3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z99tx7jtua3cmg3nh0ii.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ncRfIYI3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z99tx7jtua3cmg3nh0ii.PNG" alt="Image description" width="880" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aha! Here, we see a bunch of sites with 403 (Forbidden) and some 301s (Redirects/Hits).&lt;br&gt;
Also, we see /phpmyadmin and /plugins as well as some wordpress sites.&lt;br&gt;
We can navigate to phpmyadmin and try some default credentials, such as root:root, admin:admin, root:admin etc.&lt;br&gt;
We don’t get a hit, and since we don’t know any users, we are unable to progress much further in this direction right now.&lt;/p&gt;

&lt;p&gt;However, there is a great tool for Wordpress sites called wpscan. It automatically looks through posts, authors, themes, assets etc. to spot anything vulnerable or out of the ordinary.&lt;/p&gt;

&lt;p&gt;By running a command such as &lt;code&gt;wpscan --url http://10.10.10.37/ --enumerate u&lt;/code&gt;, we find a user named notch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KBMr7MRX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8r6dinr9pp3k89dfv0xi.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBMr7MRX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8r6dinr9pp3k89dfv0xi.PNG" alt="Image description" width="734" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Meanwhile, if we visit /plugins we see two .jar files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RmEC3gbB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aw30f2xdfzprni5hg80c.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RmEC3gbB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aw30f2xdfzprni5hg80c.PNG" alt="Image description" width="757" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I downloaded the first one, BlockyCore.jar, and unzipped it to find a BlockyCore.class file. Using a Java decompiler, I was able to read the contents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7_bKOXJM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgkhc23myefu1r4oamhw.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7_bKOXJM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgkhc23myefu1r4oamhw.PNG" alt="Image description" width="702" height="535"&gt;&lt;/a&gt;&lt;br&gt;
Looks like we found some credentials!&lt;br&gt;
We now have two users: root and notch.&lt;br&gt;
Going back to the website, we can visit the /phpmyadmin endpoint and try logging in. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g6Gaqaah--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yc4bubaj31gwmx3g2yz8.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g6Gaqaah--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yc4bubaj31gwmx3g2yz8.PNG" alt="Image description" width="714" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It worked! We now have access to phpMyAdmin and can view the underlying database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eUGURBm1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tswy5rph18jlmrx90iz4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eUGURBm1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tswy5rph18jlmrx90iz4.PNG" alt="Image description" width="847" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Usually, when gaining access to admin panels, you want to look for ways to upload files, read credentials, update permissions and such. Since phpmyadmin is using SQL, we can query the database and such. We can try uploading a webshell, which would allow us to execute commands on the server. The following command takes an input and writes it to /phpmyadmin/cmd.php (assuming the folder exists under /var/www/).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT "&amp;lt;?php if($_GET['cmd']) {system($_GET['cmd']);} ?&amp;gt;"&lt;br&gt;
 INTO OUTFILE '/var/www/phpMyAdmin/cmd.php&lt;/code&gt;&lt;br&gt;
We are denied access though. But it was worth a shot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tOik3MUu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9p1xmup6ervt01ujx67i.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tOik3MUu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9p1xmup6ervt01ujx67i.PNG" alt="Image description" width="642" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So far so good…
&lt;/h2&gt;

&lt;p&gt;Okay, so where are we with all the gathered information? Well, we have obtained two usernames and a password from diving into port 80. We were able to log into phpmyadmin but unable to run any SQL queries from there.&lt;/p&gt;

&lt;p&gt;However, a common scenario when dealing with lazy developers is the reuse of passwords. Don’t do this, people. Get a password manager and generate a new unique password every time, please.&lt;br&gt;
Anyhow, once we get credentials, it is usually a good idea to look for other services where we could try those credentials as well.&lt;/p&gt;

&lt;p&gt;Let’s remind ourselves what the other services were:&lt;br&gt;
Our &lt;strong&gt;nmap scan&lt;/strong&gt; showed us that the services running were ftp, ssh, http and minecraft.&lt;/p&gt;

&lt;p&gt;We try using the notch username with the password from before on the ftp server and manage to successfully log in.&lt;br&gt;
Here, we see the user.txt file. We can use the “get” command to download files over ftp.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FbJ_DgJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9y6khvosf2x6o698hm9.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FbJ_DgJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9y6khvosf2x6o698hm9.PNG" alt="Image description" width="623" height="307"&gt;&lt;/a&gt;&lt;br&gt;
And we are rewarded with our first flag! &lt;strong&gt;Foothold achieved!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gVm_4oox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lmdxacfw6mjd6aurnakx.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gVm_4oox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lmdxacfw6mjd6aurnakx.PNG" alt="Image description" width="656" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 2: Privilege Escalation
&lt;/h1&gt;

&lt;p&gt;With the SAME notch credentials used for FTP, I was also able to SSH into the box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sXEjHzjp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jl2ywxokifb5ahby2vk.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sXEjHzjp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jl2ywxokifb5ahby2vk.PNG" alt="Image description" width="611" height="270"&gt;&lt;/a&gt;&lt;br&gt;
One of the first things to enumerate, when looking to escalate privileges on a system, is which rights you have as a user. Are you able to run some commands with elevated privileges?&lt;br&gt;
An easy way to see this on Linux is to run &lt;code&gt;sudo -l&lt;/code&gt; which will list all your sudo rights.&lt;br&gt;
Here, we see that we may run (ALL: ALL) ALL. This means we can run any command as the superuser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0Jm6Ux3g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31uv5osxzc2ujg6prphe.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Jm6Ux3g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31uv5osxzc2ujg6prphe.PNG" alt="Image description" width="714" height="183"&gt;&lt;/a&gt;&lt;br&gt;
This makes for a &lt;strong&gt;&lt;u&gt;very&lt;/u&gt;&lt;/strong&gt; easy win with &lt;code&gt;sudo su root&lt;/code&gt;. Enter the credentials for notch and bam! We’re root.&lt;br&gt;
We can see the root.txt file, readable by our user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YsN-uM-s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u7cvaxxh9urfu0h4dpwq.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YsN-uM-s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u7cvaxxh9urfu0h4dpwq.PNG" alt="Image description" width="513" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that is both of the proofs we need to submit to gain full points on the box!&lt;/p&gt;

&lt;p&gt;Were you able to follow along and understand everything that happened there?&lt;br&gt;
Let’s quickly recap the steps we took:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We scanned the site with &lt;strong&gt;nmap&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;On port 80, we found a website and performed a subdirectory-listing scan with &lt;strong&gt;Gobuster&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;We saw Wordpress files and used &lt;strong&gt;wpscan&lt;/strong&gt; to find a user named notch&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;/plugins&lt;/strong&gt; folder, we found a .jar file with some credentials&lt;/li&gt;
&lt;li&gt;We tried reusing the credentials on the other services (&lt;strong&gt;foothold&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;We were able to &lt;strong&gt;SSH&lt;/strong&gt; into the box&lt;/li&gt;
&lt;li&gt;With &lt;strong&gt;sudo -l&lt;/strong&gt; we listed our privileges and saw that we could run any command as root&lt;/li&gt;
&lt;li&gt;We switched to root user with the &lt;code&gt;sudo su root&lt;/code&gt; command which gave us root flag&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alright, that wraps up my second post and this series on penetration testing!&lt;br&gt;
This post was a bit longer and a bit more technical than the first post, but translating theoretical knowledge into practical usage is often what helps you truly understand something new. &lt;br&gt;
I hope you learned something from this mini series on penetration testing - or at least enjoyed the reading :-) Feel free to post your questions below! &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>penetrationtesting</category>
      <category>hacking</category>
      <category>hackthebox</category>
    </item>
    <item>
      <title>A Beginner's Guide to Penetration Testing (Part 1)</title>
      <dc:creator>Hjalte Abelskov</dc:creator>
      <pubDate>Thu, 21 Apr 2022 11:52:13 +0000</pubDate>
      <link>https://forem.com/itminds/a-beginners-guide-to-penetration-testing-part-1-4ki0</link>
      <guid>https://forem.com/itminds/a-beginners-guide-to-penetration-testing-part-1-4ki0</guid>
      <description>&lt;h2&gt;
  
  
  Disclaimer:
&lt;/h2&gt;

&lt;p&gt;Before we start, I feel the need to write a short disclaimer: remember that these are real hacking tools that should never be used without a formal agreement in place between the parties involved. These posts are meant to be a teaser with the goal of giving you, the reader, a sense of what penetration testing is and what to be aware of when building your applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a pentest?
&lt;/h2&gt;

&lt;p&gt;A pentest is essentially a simulated cyberattack on a computer system with the purpose of evaluating the security of the system. A pentest can be whitebox, meaning that the attacker has access to background and system information, or blackbox, meaning that the attacker has little or no information about the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we pentest?
&lt;/h2&gt;

&lt;p&gt;The UK National Cyber Security Center describes pentesting as &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A method for gaining assurance in the security of an IT system by attempting to breach some or all of that system’s security, using the same tools and techniques as an adversary might&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let me start by going over some quick glossary to ensure that we are all on the same page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Target&lt;/strong&gt;: A target is typically a single IP address, but could also be a range of IP addresses.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Port&lt;/strong&gt;: A communication endpoint for the computer, numbered between 0-65535. Ports can be either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OPEN (If you send a SYN, you get back a SYN/ACK)&lt;/li&gt;
&lt;li&gt;UNFILTERED (The special ACK scan, used for mapping firewall rule sets, can sometimes return RST. This means that the port is accessible, but we can’t determine if it is open or closed).&lt;/li&gt;
&lt;li&gt;FILTERED (Target is behind some sort of firewall and packets get dropped / no response)&lt;/li&gt;
&lt;li&gt;CLOSED (When you send a SYN, it responds back with a RST).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;CVE&lt;/strong&gt;: Short for “Common Vulnerabilities and Exposures”. This is a method of indexing and referencing publicly known software vulnerabilities. There are roughly between 15,000 - 20,000 new CVEs reported each year.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&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%2Fuploads%2Farticles%2Fn7veomwcoh1bb9rwz4qk.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7veomwcoh1bb9rwz4qk.png" alt="ports below 1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Phases of a pentest
&lt;/h2&gt;

&lt;p&gt;You should know that there are 5 phases to professional pentesting, namely&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Planning&lt;/li&gt;
&lt;li&gt;Scanning&lt;/li&gt;
&lt;li&gt;Gaining Access&lt;/li&gt;
&lt;li&gt;Persistent Access&lt;/li&gt;
&lt;li&gt;Reporting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this post, however, I want to focus mainly on phase 2 and 3 since that's where the exciting stuff happens.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Scanning
&lt;/h1&gt;

&lt;p&gt;When we scan a target, we are looking for information about which operating system and software might be running on the computer. Scanning for open ports tells us how the target can be interacted with, which often lets us infer a lot of information about the target. This is typically done with a tool called nmap.&lt;/p&gt;

&lt;p&gt;A scan could look like the following:&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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6ft8mzcp3ywyhoyopin.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6ft8mzcp3ywyhoyopin.png" alt="Example nmap scan"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we see two open ports, namely port 22 (ssh) and port 80 (http). This indicates that the target is probably hosting one or more websites and has SSH access enabled for remote configuration. We can see it is running OpenSSH 7.4 and the website is Apache httpd 2.4.25. From this information alone, we may start looking for well-known vulnerabilities, also known as CVEs. Mitre has a CVE database (&lt;a href="https://cve.mitre.org/" rel="noopener noreferrer"&gt;https://cve.mitre.org/&lt;/a&gt;) we can look in. A website such as &lt;a href="https://www.exploit-db.com/" rel="noopener noreferrer"&gt;https://www.exploit-db.com/&lt;/a&gt; even goes as far as to provide working code exploits for a lot of known CVEs.&lt;/p&gt;

&lt;p&gt;On a different scan we might find a Domain Controller in an Active Directory which would be indicated by ports 53 (dns), 88 (kerberos), 135 (msrpc), 139 (netbios-ssn), 389 (ldap), 445 (file replication service), 464 (kerberos password change), 3268 and 3269 (ldap) being open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt; &lt;em&gt;Scanning the target and enumerating the attack vectors that come to mind is an iterative process and should be done continuously throughout the pentest.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After our initial port scan, we might do more scans depending on what we find. In order to be as effective as possible, and to gather as much information as possible, pentesters are often running multiple scans simultaneously on a target. There are hundreds of tools out there for every service imaginable. Some of the tools worth mentioning are wpscan (&lt;a href="https://wpscan.com/wordpress-security-scanner" rel="noopener noreferrer"&gt;https://wpscan.com/wordpress-security-scanner&lt;/a&gt;) for Wordpress sites or sqlmap (&lt;a href="https://sqlmap.org/" rel="noopener noreferrer"&gt;https://sqlmap.org/&lt;/a&gt;) for automatic SQL injection. For a more extensive list of tools check out &lt;a href="https://0xcybery.github.io/ehtk/" rel="noopener noreferrer"&gt;https://0xcybery.github.io/ehtk/&lt;/a&gt; or &lt;a href="https://github.com/enaqx/awesome-pentest" rel="noopener noreferrer"&gt;https://github.com/enaqx/awesome-pentest&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Gaining access
&lt;/h1&gt;

&lt;p&gt;This step will vary a lot based on the results of the second phase, but let’s assume a very common case: we see a website on port 80/443.&lt;br&gt;
First, we want to identify which technologies are being used. For this, there are several tools, but we can also poke at it manually.&lt;/p&gt;

&lt;p&gt;We could try to..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the source code&lt;/li&gt;
&lt;li&gt;See if we get a hit on a well-known endpoint such as /wp-content/ for Wordpress, /user/login/ for Drupal, or /manager/html for Tomcat.&lt;/li&gt;
&lt;li&gt;Inspect the server responses and look for technologies used. This will sometimes also yield version numbers which makes it easy to look for existing exploits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we’re lazy we can use tools such as Nikto, WhatWeb, BuiltWith or Wappalyzer that will analyze which technologies are being used by the website for us.&lt;/p&gt;

&lt;p&gt;We can also use tools such as Gobuster to scan websites for subpages.&lt;br&gt;
Gobuster has a ton of features, but we will be using the url parameter and provide it with a wordlist of words to search for. Operating systems such as Kali Linux or ParrotOS come pre-installed with wordlists, but there are plenty of useful wordlists on Github we can use - check out &lt;a href="https://github.com/danielmiessler/SecLists" rel="noopener noreferrer"&gt;https://github.com/danielmiessler/SecLists&lt;/a&gt;. An example of this could be: &lt;br&gt;
&lt;code&gt;Gobuster -u https://site-we-want-to-scan/ -w /path/to/wordlist -t threads -o gobuster_scan_from_website_root&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;An output could look something like this:&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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fastc00dftkievtscn94v.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fastc00dftkievtscn94v.png" alt="Initial gobuster scan example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If our wordlist is well-chosen, we now have a good overview of the site, and we can continue our attack based on what we find. As mentioned earlier, enumeration is a continuous process.&lt;/p&gt;

&lt;p&gt;With this new information, we could try a gobuster scan on &lt;a href="https://site-we-want-to-scan/images" rel="noopener noreferrer"&gt;https://site-we-want-to-scan/images&lt;/a&gt;, &lt;a href="https://site-we-want-to-scan/uploads" rel="noopener noreferrer"&gt;https://site-we-want-to-scan/uploads&lt;/a&gt; or &lt;a href="https://site-we-want-to-scan/assets" rel="noopener noreferrer"&gt;https://site-we-want-to-scan/assets&lt;/a&gt; to gain more information about which artifacts exist on the site.&lt;/p&gt;

&lt;p&gt;Well, this wraps up my first post on penetration testing!&lt;br&gt;
Thank you for reading! &lt;br&gt;
In the next post, I will get more hands-on as I walk you through how I hacked a target from the famous website HackTheBox.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>penetrationtesting</category>
      <category>hacking</category>
    </item>
    <item>
      <title>Improve Your Deep Learning Models with Image Augmentation</title>
      <dc:creator>Tord Kvifte</dc:creator>
      <pubDate>Wed, 06 Apr 2022 06:17:55 +0000</pubDate>
      <link>https://forem.com/itminds/improve-your-deep-learning-models-with-image-augmentation-4j7f</link>
      <guid>https://forem.com/itminds/improve-your-deep-learning-models-with-image-augmentation-4j7f</guid>
      <description>&lt;p&gt;Computer vision with deep convolutional neural networks has seen a boom during recent years, and it is now applied in everything from computer-aided medical diagnosis to Unmanned Aerial Vehicles, Augmented Reality and visual search engines. Despite the amazing capabilities of modern deep learning techniques, certain challenges remain, such as getting enough data. One of the remedies is data augmentation. In this blog post, I will briefly explain a handful of data augmentation techniques in image recognition and show some examples of how to easily implement these with the &lt;a href="https://albumentations.ai/docs/" rel="noopener noreferrer"&gt;Albumentations&lt;/a&gt; library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Augmentation - What is it good for?
&lt;/h2&gt;

&lt;p&gt;One major issue is that the performance of deep learning models is highly dependent on the amount of data available for training. Lack of data can result in overfitting of our model, and since relevant, good-quality data can be difficult and/or expensive to obtain, we need to squeeze out every drop of nectar from the data we have on hand. Data augmentation has proven to be particularly useful in this regard. &lt;/p&gt;

&lt;p&gt;When we perform data augmentation, we create manipulated versions of the images in our training dataset so that we end up with many altered versions of the same images. By doing this, we achieve two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We increase the amount of samples in our training data.&lt;/li&gt;
&lt;li&gt;By exposing the model to images with a higher variety of features, it gets forced to ignore non-relevant features, making it generalize better.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: Choose and use image augmentation techniques with care. Some methods may not be applicable to your use case. Wrong use and overuse can harm the performance of the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Albumentations
&lt;/h2&gt;

&lt;p&gt;There are many good options when it comes to tools and libraries for implementing data augmentation into our deep learning pipeline. You could for instance do your own augmentations using NumPy or Pillow. Some of the most popular dedicated libraries for image augmentation include Albumentations, &lt;a href="https://github.com/aleju/imgaug" rel="noopener noreferrer"&gt;imgaug&lt;/a&gt;, and &lt;a href="https://github.com/mdbloice/Augmentor" rel="noopener noreferrer"&gt;Augmentor&lt;/a&gt;. Both &lt;a href="https://www.tensorflow.org/tutorials/images/data_augmentation" rel="noopener noreferrer"&gt;TensorFlow&lt;/a&gt; and &lt;a href="https://pytorch.org/vision/stable/transforms.html" rel="noopener noreferrer"&gt;PyTorch&lt;/a&gt; even come with their own packages dedicated to image augmentation. &lt;/p&gt;

&lt;p&gt;Dedicated libraries for image augmentation provide many advantages that makes our lives easier. For one, they allow us to declare our data augmentation pipeline in a single place for use through a unified interface. In Albumentations, this interface is available as &lt;code&gt;A.Compose()&lt;/code&gt; which lets us define the augmentation pipeline with the list of augmentations we want to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;albumentations&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;

&lt;span class="c1"&gt;# Load image
&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_image_path.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define augmentation pipeline
&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Compose&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;
        List of augmentation methods.
    &lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get an image with the augmentations applied to it, we can simply run &lt;code&gt;transformed = transform(im)&lt;/code&gt; with the image as input.&lt;/p&gt;

&lt;p&gt;Image augmentation libraries also make it easy to apply the same augmentations on an input image and the respective mask when working with image segmentation tasks. In addition, libraries allow for simple control of the probabilities and magnitudes for each of our transformations. For instance, if we want rotation to be applied 50% of the time, with a maximum of 45 degrees, we can write it as &lt;code&gt;A.Rotate(limit=45, p=0.5)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Albumentations, which I will use for the examples in this post, supports 60 different image augmentations while also allowing us to add other augmentations to the pipeline. They also boast with the highest performance in terms of speed among the most popular augmentation libraries. A simple &lt;code&gt;pip install albumentations&lt;/code&gt; and we are ready to go!&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining an augmentation pipeline
&lt;/h3&gt;

&lt;p&gt;The Compose class lets us define the augmentation pipeline with the list of augmentations we want to use. Calling Compose returns a function that applies the image augmentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;albumentations&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;

&lt;span class="c1"&gt;# Load image
&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_image_path.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define augmentation pipeline
&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Compose&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;
        List of augmentation methods.
    &lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Apply augmentations
&lt;/span&gt;&lt;span class="n"&gt;transformed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Affine transformations
&lt;/h3&gt;

&lt;p&gt;Affine transformations involve geometric transformations to images such as translation, rotation, scaling (zoom) and shearing. ShiftScaleRotate lets us apply several affine transformations and adjust their respective magnitudes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Load image
&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;robin.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define augmentation pipeline
&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Compose&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ShiftScaleRotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shift_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0625&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                       &lt;span class="n"&gt;scale_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                       &lt;span class="n"&gt;rotate_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                       &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Apply augmentations
&lt;/span&gt;&lt;span class="n"&gt;transformed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F504fhkt5ibvdxhmump7p.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F504fhkt5ibvdxhmump7p.png" alt="Affine transformations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Noise
&lt;/h3&gt;

&lt;p&gt;There are a lot of ways we can inject different types of noise into an image, including blur, gaussian noise, shuffling of channels in a color image, changes in brightness, colors, contrast, and the list goes on. Below is just a few examples, but Albumentations allows many more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Load image
&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;robin.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define augmentation pipeline
&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Compose&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ColorJitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brightness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contrast&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saturation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MotionBlur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blur_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GaussNoise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var_limit&lt;/span&gt;&lt;span class="o"&gt;=&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="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Apply augmentations
&lt;/span&gt;&lt;span class="n"&gt;transformed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jf49hwszec3mmm3lr7d.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jf49hwszec3mmm3lr7d.png" alt="Noise"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dropout transformations
&lt;/h3&gt;

&lt;p&gt;We can remove part of the information in an image using dropout transformations. This includes, but is not limited to, replacing regions of the image with zero or random values and removing a channel if it is a color image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Load image
&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;robin.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define augmentation pipeline
&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Compose&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CoarseDropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_holes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ChannelDropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Apply augmentations
&lt;/span&gt;&lt;span class="n"&gt;transformed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmg43p5hcg3z1s74nzeik.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmg43p5hcg3z1s74nzeik.png" alt="Dropout"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Spatial Distortions
&lt;/h3&gt;

&lt;p&gt;Another cool way to augment an image is to use different distortion techniques for altering the shapes portrayed in the image. I added a grid to the illustrations to better visualize the augmentations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Load image
&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;robin.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define augmentation pipeline
&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Compose&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GridDistortion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distort_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OpticalDistortion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distort_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shift_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Apply augmentations
&lt;/span&gt;&lt;span class="n"&gt;transformed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6sgw9gwjqssr3zk2msb1.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6sgw9gwjqssr3zk2msb1.png" alt="Spatial"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, gathering all the augmentations from this blog post into an augmentation pipeline would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Compose&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ShiftScaleRotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shift_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0625&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rotate_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ColorJitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brightness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contrast&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saturation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MotionBlur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blur_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GaussNoise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var_limit&lt;/span&gt;&lt;span class="o"&gt;=&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="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CoarseDropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_holes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ChannelDropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GridDistortion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distort_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OpticalDistortion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distort_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shift_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&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;
  
  
  Time to try it yourself!
&lt;/h3&gt;

&lt;p&gt;Hopefully, you now have a basic understanding of what data augmentation is and how we can create augmentation pipelines for use in training deep learning models. There are of course a lot more augmentations that could be useful beyond the examples in this blog post, many of which are available in Albumentations. This means, it is time for you to try it out yourself. Experiment with different methods and see how they impact the performance of your models!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further reading:&lt;/strong&gt;&lt;br&gt;
If you want to dig deeper into image augmentation techniques or look at more examples with Albumentations, I recommend to start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://link.springer.com/article/10.1186/s40537-019-0197-0?code=a6ae644c-3bfc-43d9-b292-82d77d5890d5" rel="noopener noreferrer"&gt;Connor Shorten &amp;amp; Taghi M. Khoshgoftaar, A survey on Image Data Augmentation for Deep Learning (2019)&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://albumentations.ai/docs/examples/" rel="noopener noreferrer"&gt;Albumentations examples&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>deeplearning</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>A Beginner's Guide to Mobile Development in React Native with Expo</title>
      <dc:creator>Anders Aaen Springborg</dc:creator>
      <pubDate>Wed, 23 Mar 2022 15:34:06 +0000</pubDate>
      <link>https://forem.com/itminds/a-beginners-guide-to-mobile-development-in-react-native-with-expo-4f2o</link>
      <guid>https://forem.com/itminds/a-beginners-guide-to-mobile-development-in-react-native-with-expo-4f2o</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;This is a guide getting you through setting up a React Native project. However, this guide won't go in details with React, so if you're new to React, I recommend you to check out this &lt;a href="https://beta.reactjs.org/" rel="noopener noreferrer"&gt;getting started with react guide.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/IT-Minds/first-mobile-app/tree/main" rel="noopener noreferrer"&gt;Here is a link for what your code should look like, when you are done.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  React Native and Expo
&lt;/h2&gt;

&lt;p&gt;React Native is a framework for writing cross-platform native apps. &lt;/p&gt;

&lt;p&gt;Expo is a set of tools and services built around React Native and native platforms that help you develop, build and deploy iOS and Android apps from the same JavaScript/TypeScript codebase. The alternative would be to build each app in xCode and Android studio, which is very cumbersome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;You need to have &lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;node and npm installed&lt;/a&gt;, and &lt;a href="https://classic.yarnpkg.com/en/docs/install#mac-stable" rel="noopener noreferrer"&gt;yarn&lt;/a&gt; is optional, but recommended.&lt;/p&gt;

&lt;p&gt;Then we have to install Expo, through npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the command line tools&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; expo-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we're ready to make a project. If you installed yarn, write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;expo init first-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or with npm&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;expo init first-app &lt;span class="nt"&gt;--npm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, you will be prompted with a few options for your project. I recommend choosing a managed workflow as this enables us to release the app in Expo, without an Apple developer account for iOS. I chose the template "blank (typescript)", as can be seen in the picture below. If you're new, you could also use the "tab" template. This has built-in navigation.&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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6st9i4kdksc3721ouyb7.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6st9i4kdksc3721ouyb7.png" alt="Init expo app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You now have a project. In your terminal, you can navigate to the project's directory with 'cd first-app'. Your project structure should look like this: &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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zm4excvr0ynziwouhne.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zm4excvr0ynziwouhne.png" alt="File structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can run the project by writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it will look something like this:&lt;br&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%2Fuploads%2Farticles%2Fmsfw5uoswjecam322oh9.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsfw5uoswjecam322oh9.png" alt="App template iPhone"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can either press w, to see your app in your browser, or you can scan the QR code which redirects you to download an app called &lt;a href="https://expo.dev/client" rel="noopener noreferrer"&gt;Expo go&lt;/a&gt;. This allows you to view the app while you develop it, which is something I would recommend. &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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl370yjyi80gfritav8r6.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl370yjyi80gfritav8r6.png" alt="Initial app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You now have a running app in React Native. In the next section, we are going to look at how native APIs work.  &lt;/p&gt;
&lt;h2&gt;
  
  
  Native APIs
&lt;/h2&gt;

&lt;p&gt;As we choose a "managed workflow", we're building everything with Expo. This comes with some &lt;a href="https://docs.expo.dev/introduction/why-not-expo/" rel="noopener noreferrer"&gt;limitations&lt;/a&gt;. If you need a native API which isn't on the &lt;a href="https://docs.expo.dev/versions/latest/" rel="noopener noreferrer"&gt;list of supported APIs&lt;/a&gt; (there is 100+ native APIs), you can switch to a bare workflow in which you can write your own native code. &lt;br&gt;
Below is a comparison between a managed and bare workflow: &lt;br&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%2Fuploads%2Farticles%2F1yk3bvoeroi2ayfzq73f.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1yk3bvoeroi2ayfzq73f.png" alt="Managed and bare workflow comparison"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementing native APIs
&lt;/h3&gt;

&lt;p&gt;As an example, let's make a page that shows our battery percentage and GPS location.&lt;/p&gt;

&lt;p&gt;Let's start with a &lt;a href="https://docs.expo.dev/versions/v44.0.0/sdk/battery" rel="noopener noreferrer"&gt;battery&lt;/a&gt; component. We install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;expo &lt;span class="nb"&gt;install &lt;/span&gt;expo-battery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First off, we'll work on showing the battery percentage. We need the API 'getPowerStateAsync'. We'll call when a component mount, and voilá - we have the phone's battery percentage:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setBattery&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Battery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBatteryLevelAsync&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setBattery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&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;We can show it in our view like this:&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="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;View&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="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&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;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Battery &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;battery&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&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="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;StatusBar&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt; &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;black&lt;/span&gt;&lt;span class="dl"&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="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrt12szsca44mq34sk2i.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrt12szsca44mq34sk2i.png" alt="Battery 42%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's make a component that looks at the power mode of a phone. Imagine that you have a power-hungry feature which you want to disable when in low power mode (like animation).&lt;br&gt;
We can get the power state from the same API, like this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lowPoweredState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLowPoweredState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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;gt;&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="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Battery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPowerStateAsync&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setLowPoweredState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lowPowerMode&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;We can now carry out some conditional rendering, based on the power state:&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowPoweredState&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="k"&gt;return&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;Getting info from your phone...&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="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;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Battery state: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;lowPoweredState&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;i am a static text replacement&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="s2"&gt;I AM A POWER HUNGRY ANIMATION&lt;/span&gt;&lt;span class="dl"&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;Text&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;&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%2Fuploads%2Farticles%2Fft4z9v54ilim0xcifac8.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fft4z9v54ilim0xcifac8.png" alt="Rendering based on power state"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To stay up to date, when the phone changed from a low powered state to a normal one, we can add a listener in useEffects like this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batteryModeSubscriber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Battery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addLowPowerModeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setLowPoweredState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lowPowerMode&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;batteryModeSubscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our app will now always render based on the power state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing our app
&lt;/h2&gt;

&lt;p&gt;Normally, you think of publishing an app to App Store and Google Play store. In order to do this, you need an &lt;a href="https://developer.apple.com/programs/" rel="noopener noreferrer"&gt;Apple developer account&lt;/a&gt;(100$ yearly subscription) or a &lt;a href="https://play.google.com/apps/publish/signup" rel="noopener noreferrer"&gt;Google Play developer account&lt;/a&gt;(25$ one time fee) account. You can read more about building and releasing your app &lt;a href="https://docs.expo.dev/build/setup/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  You're an app developer
&lt;/h2&gt;

&lt;p&gt;You have now started on your first React Native app in Expo. This is the same tool which is used for creating apps like Facebook, Instagram, Coinbase, shopify, Tesla, Uber Eats and many more. You can read more on Expo here: &lt;a href="https://docs.expo.dev/" rel="noopener noreferrer"&gt;https://docs.expo.dev/&lt;/a&gt; or check out an open source app here: &lt;a href="https://github.com/withspectrum/spectrum" rel="noopener noreferrer"&gt;https://github.com/withspectrum/spectrum&lt;/a&gt;&lt;br&gt;
and checkout an enterprise boilerplate here: &lt;a href="https://github.com/infinitered/ignite" rel="noopener noreferrer"&gt;https://github.com/infinitered/ignite&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>typescript</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Writing Good Tests (Part 2) - What Makes a Test Good</title>
      <dc:creator>MichaelFack1</dc:creator>
      <pubDate>Mon, 14 Mar 2022 12:05:38 +0000</pubDate>
      <link>https://forem.com/itminds/writing-good-tests-part-2-what-makes-a-test-good-289d</link>
      <guid>https://forem.com/itminds/writing-good-tests-part-2-what-makes-a-test-good-289d</guid>
      <description>&lt;p&gt;In the previous post, I ranted about what &lt;em&gt;not&lt;/em&gt; to do when writing tests and how to detect a bad test. In this post, I'm going to attempt to provide some pointers as to how you go about writing good tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes a test good?
&lt;/h2&gt;

&lt;p&gt;Just like good code is a bit hard to pin down, so is a good test, but you will not be in any doubt when you read a good test, especially if it fails as it achieves its purpose - it will tell you exactly what went wrong and what needs fixing!&lt;/p&gt;

&lt;p&gt;Let us demonstrate this by having a look at a failure. Consider our first example &lt;code&gt;Test1&lt;/code&gt;. If it fails, you will be presented with text similar to "UnitTest1.Test1 failed: false != true". &lt;em&gt;"This is true"&lt;/em&gt; said the tautalog. Instead consider the following test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;CmsService&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICmsReadRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;readRepoMock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICmsWriteRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;writeRepoMock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CmsServiceUnitTest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;writeRepoMock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICmsWriteRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;readRepoMock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICmsReadRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CmsService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CmsService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(),&lt;/span&gt; &lt;span class="n"&gt;readRepoMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;writeRepoMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&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;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;InvalidKeyDuoInvalidChar&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"An invalid key e&amp;gt;&amp;lt;ample"&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="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ReadSectionAsync_InvalidCharKey_ThrowsInvalidCharException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="nf"&gt;DefaultSetup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Act&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;taskExpectedToThrow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadSectionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InvalidKeyDuoInvalidChar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Assert&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ThrowsAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InvalidCharException&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;taskExpectedToThrow&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;And the following message &lt;br&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%2Fuploads%2Farticles%2F987p6vqnwlc2yfqypsg6.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F987p6vqnwlc2yfqypsg6.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
This tells you that an &lt;code&gt;InvalidCharException&lt;/code&gt; was expected from the &lt;code&gt;CmsService&lt;/code&gt; when calling &lt;code&gt;ReadSectionAsync&lt;/code&gt;, with a key containing an invalid &lt;code&gt;char&lt;/code&gt;. Now, in this case, a few things could have gone wrong. Perhaps we no longer indicate failure to read with exceptions anymore but use the optional pattern instead, or perhaps we forgot to validate the key. I happen to know that we did not switch to the optional pattern which means the latter is true. Whoopsie.&lt;/p&gt;

&lt;p&gt;All of this, I was able to infer from the test case, thus saving me tons of time I could have wasted Bughunting.&lt;/p&gt;

&lt;p&gt;Furthermore, with a nice test coverage % and the most important and most common features under test, I can rest easy at night knowing my software is functional and not just a stinking heap of-. And, I also save time not manually testing which is time well spent in my opinion.&lt;/p&gt;

&lt;h2&gt;
  
  
  The purpose of a test
&lt;/h2&gt;

&lt;p&gt;Without getting too metatheoretical, I would like to briefly discuss what "the purpose" of a test is, explicitly.&lt;/p&gt;

&lt;p&gt;Say, I have a CMS service I want to test. I have some assumptions with respect to its functionality: It should be safe, practical and efficient. Moreover, say, that to be safe, it shouldn't allow all characters but only a specific subset of characters. Now I want to assert that my CmsService doesn't accept any of the illegal characters. Therefore, I write a unit test of which the purpose is to verify this. It initially fails, but after the introduction of a guard-clause, the test passes and I &lt;em&gt;believe&lt;/em&gt; everything is good. The next day, I want to demonstrate this new behavior to my team, and I open the webpage utilizing the service to update the entries. You input an illegal character, press save and ... It saves!&lt;br&gt;
You dive in to the database to discover a strange sequence of letters prefixed with an &lt;code&gt;&amp;amp;&lt;/code&gt;. Somewhere there was a mismatch between the purpose of the test and the actual test.&lt;/p&gt;

&lt;p&gt;This is obviously a very constructed example, but my point is that the test did not fulfill its purpose. A more appropriate test to write when verifying that the system does not accept invalid characters in its frontend is a frontend test, or perhaps a system test. Had I written a frontend test, I might have discovered that some middleware translates user input into safe strings that cannot be utilized in injection attacks. Had I written a system test, I might have discovered that the underlying CMS API has protection. It would have been more appropriate to write a unit test when asserting unit behavior, such as "I expect the &lt;code&gt;CmsService&lt;/code&gt; to call the &lt;code&gt;CmsApiClient&lt;/code&gt; when &lt;code&gt;GetSectionAsync&lt;/code&gt; is called".&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In the first post we discussed that if a test is not informative and exact, simple, timely and repeatable, then it's a bad test. In this post, we expanded on the concept of a test being good by discussing what a test needs to do; achieve its purpose of validating an assertion that we make about how some part of, or the whole, system works.&lt;/p&gt;

&lt;p&gt;We also determined that this can only be done when we know what the assertion is and create a test based on this. In particular, we want the type of the test to match the layer of abstraction. If only there was some literature on the different kinds tests types paradigms that exist...&lt;/p&gt;

&lt;h2&gt;
  
  
  Remark
&lt;/h2&gt;

&lt;p&gt;If you have any questions, disagree with anything, or if you would like something added to this blog post - leave a comment!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;My personal experience,&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices&lt;/a&gt;&lt;br&gt;
&lt;a href="https://martinfowler.com/bliki/GivenWhenThen.html" rel="noopener noreferrer"&gt;https://martinfowler.com/bliki/GivenWhenThen.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29" rel="noopener noreferrer"&gt;https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29&lt;/a&gt; (Clean Code by Robert C. Martin summary)&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
    </item>
    <item>
      <title>Writing Good Tests (Part 1) - What Makes a Test Bad</title>
      <dc:creator>MichaelFack1</dc:creator>
      <pubDate>Thu, 03 Mar 2022 10:51:23 +0000</pubDate>
      <link>https://forem.com/itminds/writing-good-tests-part-1-what-makes-a-test-bad-4gfd</link>
      <guid>https://forem.com/itminds/writing-good-tests-part-1-what-makes-a-test-bad-4gfd</guid>
      <description>&lt;p&gt;Unless you're some kind of demi-god, I believe you too have tried writing some new fresh code that didn't execute as you expected at first. Perhaps you forgot a guard-clause, some underlying dependency didn't behave as anticipated or, for some other reason, a bug was introduced to your precious code. What is more important (at least with regards to this blog post) is that you discovered this by compiling the code, running the software and seeing it fail.&lt;/p&gt;

&lt;p&gt;In this blogpost, I hope to indoctrinate you into believing in the higher powers; automated testing!&lt;/p&gt;

&lt;h1&gt;
  
  
  Don't just write tests
&lt;/h1&gt;

&lt;p&gt;Let me know if you've ever been in this situation before; you've just opened your favorite IDE to work on a large (and old) project, you see the infamous area of the project aptly named "Tests", and you shudder a little bit?&lt;br&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%2Fuploads%2Farticles%2Fzwywxviymhwquw008mft.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwywxviymhwquw008mft.png" alt="Example of folder named "&gt;&lt;/a&gt;&lt;br&gt;
I have, and I can tell you why I shuddered - I knew I was the one delegated the tasks of fixing tests that did not succeed anymore. I was the designated Bughunter (trademark pending), and I knew a bug could take days to resolve, even for my more experienced coworkers who had been working on the project for years.&lt;/p&gt;

&lt;p&gt;You know why? The tests were bad!&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes a test bad?
&lt;/h2&gt;

&lt;p&gt;Just like a bowl of cereal, a lot of things can make a test bad, and it is not always clear why a test has gone bad. However, once you've had a spoonful of it - you know!&lt;/p&gt;

&lt;p&gt;In this short series, I am first going to supply you with a non-exhaustive list of things to check for when suspecting a test of being bad, and then later on in the series, I will discuss how to write good tests.&lt;/p&gt;

&lt;p&gt;I just happened to have a &lt;code&gt;CmsService&lt;/code&gt; lying around which implements a Read/Write access string mapping in a database and that I need to test, so, below I'll use it as an example of how NOT to write a test.&lt;/p&gt;

&lt;p&gt;Try to spot a few things you find obscene in the following .NET code segment.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnitTest1&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Theory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MemberData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestData&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Test1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CmsDbEntry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;boolResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;repoMock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICmsWriteRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dbContextMock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CmsDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CmsRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dbContextMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CmsService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;CreateLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CmsService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repoMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;entriesMock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CmsDbEntry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;entriesMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;ReturnsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;dbContextMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entriesMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadSectionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"some arbitrary constant"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ignoreCase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&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="n"&gt;boolResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;repoMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"some arbitrary constant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Content"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;ReturnsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boolResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;boolResult0&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteSectionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"some arbitrary constant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Content"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boolResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boolResult0&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;public&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[][]&lt;/span&gt; &lt;span class="n"&gt;TestData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;object&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;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here's a few things I like to pay attention to:&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the test readable?
&lt;/h3&gt;

&lt;p&gt;At a quick glance, the test should convey what is being tested. If it fails to convey what is being tested, at best, it becomes harder to update as the target code changes, at worst, it can sabotage development as it misleads the developer.&lt;/p&gt;

&lt;p&gt;If you cannot discern what a test tests, how can you know if it is correct? A test which you cannot tell whether is correct or not because you cannot discern its purpose or meaning adds no value. &lt;strong&gt;You are better off without it&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the test well named?
&lt;/h3&gt;

&lt;p&gt;A well-named test might convey the entire purpose of a test, much like how a well-named method conveys its functionality.&lt;/p&gt;

&lt;p&gt;Formatting test names in a consistent way helps conveying  purpose. Additionally, in conjunction with other tests, well-named tests can actually help you pinpoint exactly where you should look for bugs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I personally use the &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices#naming-your-tests" rel="noopener noreferrer"&gt;{method}_{condition}_{result}&lt;/a&gt;.
```c#
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;public class CmsServiceUnitTests {&lt;br&gt;
  [Fact]&lt;br&gt;
  public async Task ReadSectionAsync_ValidKeyOfExistingEntry_ReturnsEntry() {&lt;br&gt;
    ...&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Other naming conventions also exist - I can recommend the [{Given}\_{When}\_{Then}](https://martinfowler.com/bliki/GivenWhenThen.html) format since user stories can be formulated in this format as well.
```c#


public class CmsServiceUnitTests {
  [Fact]
  public async Task GivenValidKeyEntryPairInDb_WhenFetchingEntryOfTheKey_ThenEntryIsReturned() {
    ...
  }
}


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Does it test more than one thing?
&lt;/h3&gt;

&lt;p&gt;A test that tests more than one thing at a time can be difficult to derive meaning from and becomes less useful with respect to pinpointing where a bug lives in the code.&lt;/p&gt;

&lt;p&gt;One thing does not mean one field or just one value, but rather the resulting state. A rule of thumb: primitive fields and such can be checked together as a way of validating the state of an object, but do make sure to add more informative messages when you have more than one assertion in a test.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With the &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices#naming-your-tests" rel="noopener noreferrer"&gt;{method}_{condition}_{result}&lt;/a&gt; naming convention, this is given:
```c#
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;[Fact]&lt;br&gt;
public async Task ReadSectionAsync_ValidKeyOfExistingEntry_ReturnsEntry() {&lt;br&gt;
  var expected = dbKeyEntryPair.Entry;&lt;br&gt;
  ...&lt;br&gt;
  var actual = await service.ReadSectionAsync(validKey);&lt;br&gt;
  ...&lt;br&gt;
  Assert.Equal(expected, actual);&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
### Is it well arranged?
A well-arranged test does not configure some state, execute some code, configure some more, execute some more, assert something and keep on mixing these things. In the abstract, a test that does one thing, and one thing only, validates that the code under testing from some configured state performs some execution, returns something expected and finds rest in some expected state.
- Microsoft advice adding the three A's as code comments:
```c#


[Fact]
public async Task {TestName}(){
  // Arrange
  {Configuration}
  // Act
  var actual = {subject}.{Method}({args});
  // Assert
  {Assertion(s)}
}


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;There is some disagreement out there in the Great Ether with regards to where one should put shared static configuration, such as the initialization of the test subject(s) and mocks as well as common configuration of the subject(s) and mocks. It is my opinion that initialization can occur in the constructor of the test class and that this will be the same for all tests in a test class. Configuring the subject(s) and mocks thus belongs to the configuration step of each test, as they likely vary.
```c#
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;public class CmsServiceUnitTests {&lt;br&gt;
  public Mock writeRepoMock;&lt;br&gt;
  public Mock readRepoMock;&lt;br&gt;
  public CmsService subject;&lt;/p&gt;

&lt;p&gt;public CmsServiceUnitTests() {&lt;br&gt;
    readRepoMock = new Mock();&lt;br&gt;
    writeRepoMock = new Mock();&lt;br&gt;
    subject = new CmsService(Mock.Of&amp;gt;(), readRepoMock.Object, writeRepoMock.Object)&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;const string validKey = {key of valid format};&lt;br&gt;
  const string entry = {content of entry};&lt;/p&gt;

&lt;p&gt;[Fact]&lt;br&gt;
  public async Task ReadSectionAsync_ValidKeyOfEntry_ReturnsEntry() {&lt;br&gt;
    // Arrange&lt;br&gt;
    readRepoMock.Setup(m =&amp;gt; m.ReadAsync(validKey)).ReturnsAsync(entry);&lt;br&gt;
    var expected = entry;&lt;br&gt;
    // Act&lt;br&gt;
    var actual = subject.ReadSectionAsync(validKey);&lt;br&gt;
    // Assert&lt;br&gt;
    Assert.Equal(expected, actual);&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
### Does logic occur?
A great way to detect whether a test only does one thing or not is to check whether it contains logic, such as a `switch`-, an `if`- or a `for`-statement. These statements make the test a lot less readable seeing as the state of the subject and the input at the time of method calling becomes something computed, rather than something set (which can simply be read).
- I must admit that I sin with respect to this when compressing testcases - sometimes I find it easier to write a lot of testcases using one parameterized test, where some of the arguments inform the testcase to configure in a particular way, instead of splitting up the testcases into multiple separate parameterized tests. However, just because something is easy doesn't mean you should do it!

### Is it a typical test?
Here _typical_ refers to whether or not the test follows some sacred dogma allowing it to be categorized easily into some preconceived type, such as End-to-end integration tests, unit tests, or simply tests that fit in to the overall test strategy.

If the test does not neatly fit into a category, consider refactoring it in such a way that it does. A way to do this could potentially be to clone the test if it resides within a canyon of two or more categories, in order to ensure that each category gets its own version of the test.

### Does it follow Clean Code Principles?
In spite of popular belief, test code is actually also code and should in general receive the same care you provide to any other code. There's great benefits to this, such as readability, reusability and flexibility.
- To demonstrate this, consider a test class containing a large number of tests where the subject has some internal state that may vary. While one state can be considered both an initial and valid, all other states can abstractly be thought of as mutations of this first state. We can use this sort of thinking when coding our tests. In each test following `// arrange`, we use the method `Setup();`. Each test then deviates from this initial state by mutating it, perhaps with a `SetSubjectDependency(s =&amp;gt; s.{dep}, null)` in a test validating how the subject handles internal nullreferences.

### Is the test independent?
Tests which share state and space with other tests are at risk of experiencing bugs themselves because the timing of execution of the tests may affect their correctness. This should be avoided by instantiating dependent objects for each test.
- If you want to increase readability of a test by reducing bloat by moving some variables to the class scope or similar, or perhaps refactoring away some magic constants, make sure they are immutable in the outer scope. If this is not an option, consider not moving it to the outer scope. Otherwise, you are vulnerable to undesirable behavior in your tests.

### Is the test timely (relatively)?
A test that is never run is a bad test as it adds no value. A tests that motivates the developer not to run it, perhaps because it takes a lot of time to set up or it just runs slowly, **is a bad test**.
  - A test is a slow test, seen relatively to how often you'd like to run it. If a test suite is run once a month, because the code it touches is very static, or the suite is overly thorough relatively to everyday development, it is acceptable that it takes longer to run. Test suites that are expected to run on a daily basis, perhaps even hourly basis, should be quick, though! Otherwise, you will end up getting more coffee than is healthy for you, with all those coffee breaks.

### Is the test repeatable?
A test which has stochastic behavior, such as being real-time dependent, using pseudo-randomness generators or threading timing are inherently bad tests because a pass does not guarantee anything (a false positive), and a failure can be hard to reproduce.

Believe me! In my more inexperienced days, I once spent two days trying to reproduce a test failure which turned out to only occur when the clock on the machine had gone past 19:00.

These qualities makes the test unreliable. To fix this, you fix the uncertainties. Mock the problematic dependencies, in relation to which you might possibly need to introduce wrappers, in order to gain control over what they return. In that way, the test result becomes reliable, although the result is less general and more specific - but do not fool yourself! This was already the case, now we just know the specifics.

- This also implies that you should not use a PRG or the clock by yourself to setup or run your tests!
- Sometimes, it cannot be avoided to have uncertainty in your tests, for instance if you are integration testing in conjunction with some third party code. In that case, there are some strategies concerning how to avoid false positives, such as repeating the code x times, but you should be aware that there are not absolutes in the realm of probabilistic.

## What makes a test good?
Having uncovered what makes a test bad, the next logical step is to explore how to identify a good test. Rather than letting this blogpost go on forever, I'll let you ponder upon the matter until the release of the second half of this short series on testing, wherein I'll elaborate further on testing and how to spot good tests - so watch out for part 2! 

# Conclusion
To sum up this blogpost, however, we've now compiled a list of things to check for when you suspect a test of being bad and need to identity what needs to be changed.

In case you already forgot, here's a shorter version:
1. Know what you are testing.
2. Name the test accordingly.
3. Test one thing at a time.
4. Arrange your tests.
5. Make your test readable.
6. Make your test simple.
7. Make your test timely.
8. Make your test independent.
9. Make your test repeatable.

... and if the test is without purpose, get rid of it.

## Remark
If there is anything you wonder about, disagree with or would like added to this blog post - leave a comment!
## References
My personal experience,
https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
https://martinfowler.com/bliki/GivenWhenThen.html
https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29 (Clean Code by Robert C. Martin summary)
https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>testing</category>
    </item>
    <item>
      <title>9 quick tips for keeping development velocity high within a software development team</title>
      <dc:creator>Peter Øvergård Clausen</dc:creator>
      <pubDate>Mon, 21 Feb 2022 12:23:32 +0000</pubDate>
      <link>https://forem.com/itminds/9-quick-tips-for-keeping-development-velocity-high-within-a-software-development-team-38ob</link>
      <guid>https://forem.com/itminds/9-quick-tips-for-keeping-development-velocity-high-within-a-software-development-team-38ob</guid>
      <description>&lt;p&gt;Throughout the 2 years and 7 months I've been a software development consultant at IT Minds, I've been at three different companies. This has provided me with insight into developing software when working in teams, and I would therefore like to share these following 9 tips for keeping development velocity high within a team.&lt;/p&gt;

&lt;p&gt;Consider trying them out in your development team, and let us know in the comments what you think about our tips, and whether you have any tips that you would add to the list. 😊&lt;/p&gt;

&lt;h2&gt;
  
  
  1 - Refactor often to keep software quality high and technical debt low
&lt;/h2&gt;

&lt;p&gt;It is unavoidable to gain technical debt as features grow and requirements change, hence dealing with technical debt before it slows you down is important. A well refactored codebase is more prepared for change and new features. Developers and project managers alike should prioritize refactoring as it keeps the software clean and prepared for change.&lt;/p&gt;

&lt;h2&gt;
  
  
  2 - Rely on external libraries
&lt;/h2&gt;

&lt;p&gt;Rather than developing own libraries from scratch, and having to document and maintain these, one should rather spend a day researching whether there’s an existing external library that solves the problem at hand. Less time will be spent on maintaining the library, less time will be spent on documenting the library, and team members will have a place to read and learn about the library rather than having to ask the person who made it. There is no reason to reinvent the wheel, unless the wheel is your product.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 - Have a shared Wiki for team documentation
&lt;/h2&gt;

&lt;p&gt;When a new team member joins a development team, a lot of time and confusion can be saved if the new team member has access to documentation which allows them to gain knowledge on how to set up the system locally as well as on the choices that have been made thus far. Furthermore, a wiki helps knowledge sharing  within the team, making it easier for team members to help each other out.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 - Plan ahead, at least one sprint in advance
&lt;/h2&gt;

&lt;p&gt;Always knowing where the product is headed, keeps the motor running. Sometimes a feature is implemented faster than expected, and moving on to the next feature can be quick. Likewise, if something takes longer than expected, you can assess the priority and know which other features are postponed.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 - Prioritize what’s essential, and avoid frequent scope changes
&lt;/h2&gt;

&lt;p&gt;Make it clear which feature requests are need-to-have and which are nice-to-have, and always have tasks in the sprint prioritized on what is most important. Seek to break down large tasks into smaller subtasks, and try to estimate how long they’ll take. In this way, the team will know which tasks are heavy and highly prioritized and which tasks should be focused on within the sprint, while the project manager can monitor progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  6 - Divide responsibilities among team members, but also allow team members to help each other across responsibilities
&lt;/h2&gt;

&lt;p&gt;Letting a team member work on other team members' responsibilities speeds up development and knowledge sharing. Instead of two team members having to coordinate tightly, smaller adjustments to the backend could be carried out by a frontend developer and subsequently reviewed by backend developers as a pull request. If the implementation needs adjustment, backend developers can help out. The same goes for backend developers making small changes in the frontend. For smaller software products, it may be the case that everyone can work with everything, but for larger software products it becomes infeasible for one person to know and work on everything. For this reason, my advice is to divide responsibilities, but also to let the team members help each other out across responsibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  7 - Invest in your teammates
&lt;/h2&gt;

&lt;p&gt;You may have a lot on your plate right now, but it’s never a bad idea to help out a teammate and share your knowledge of the codebase and technologies used. You may not get as much work done at first when your teammate starts, but sharing your responsibilities and knowledge will greatly improve development velocity when your teammate is up to speed, and it’ll lift some weight off your shoulders. The faster that can happen, the faster you’ll get the benefit of your teammate. Also, I’m sure your manager understands that you’re spending time onboarding and investing time in your teammate.&lt;/p&gt;

&lt;h2&gt;
  
  
  8 - Automate as much as possible
&lt;/h2&gt;

&lt;p&gt;Build, Test, and Release pipelines makes sure that the software is in a consistent state and that new features can quickly be tried out by the product owner in the DEV environment. Implementing scripts to do mundane common tasks can also save a lot of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  9 - Conduct Engineering meetings
&lt;/h2&gt;

&lt;p&gt;I was at a company as a consultant that conducted a one-hour Engineering meeting every Friday. Developers could submit topics throughout the week to be discussed at the engineering meeting. We pointed out parts of the product that needed refactoring, discussed problems and solutions, and introduced new libraries that could help us tremendously. The meetings also helped with knowledge sharing and improved team members' joy for the project and team spirit. Also, a lot of cross-team-communication could be done efficiently within this hour. I highly recommend these meetings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We have explained the following 9 tips for keeping software velocity high within a team:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Refactor often to keep software quality high and technical debt low&lt;/li&gt;
&lt;li&gt;Rely on external libraries&lt;/li&gt;
&lt;li&gt;Have a shared Wiki for team documentation&lt;/li&gt;
&lt;li&gt;Plan ahead, at least one sprint in advance&lt;/li&gt;
&lt;li&gt;Prioritize what’s essential, and avoid frequent scope changes&lt;/li&gt;
&lt;li&gt;Divide responsibilities among team members, but also allow team members to help each other across responsibilities&lt;/li&gt;
&lt;li&gt;Invest in your teammates&lt;/li&gt;
&lt;li&gt;Automate as much as possible&lt;/li&gt;
&lt;li&gt;Conduct Engineering meetings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let us know in the comments if you have any additional tips for keeping development velocity high within a team - or simply if you have tried out any of our tips within your team, and whether it was a success or not. 😊&lt;/p&gt;

</description>
      <category>agile</category>
      <category>team</category>
      <category>productivity</category>
      <category>management</category>
    </item>
    <item>
      <title>Injecting dependencies into your game development</title>
      <dc:creator>Søren Bach Nielsen</dc:creator>
      <pubDate>Thu, 17 Feb 2022 08:12:33 +0000</pubDate>
      <link>https://forem.com/itminds/injecting-dependencies-into-your-game-development-55fo</link>
      <guid>https://forem.com/itminds/injecting-dependencies-into-your-game-development-55fo</guid>
      <description>&lt;p&gt;Coming from a game development background as a game play programmer and transitioning into more of a traditional software developer path, certain things are the same. You use many of the same languages, programming paradigms and even the same IDE’s. But there are, of course, also plenty of things that are different. These can depend on many factors, such as project size, what type of project you’re developing and so on.&lt;/p&gt;

&lt;p&gt;One of these things is the Dependency Injection (DI) technique and Inversion of Control (IoC) containers.&lt;br&gt;
Through my, for now, somewhat short experience as a traditional software developer, I’ve noticed that this is probably the most prevalent set of design patterns and handy techniques I’ve gotten the most use out of.&lt;/p&gt;

&lt;p&gt;So, naturally, that made me wonder. How come this isn't more widespread in the game development scene?&lt;/p&gt;

&lt;p&gt;Well, it should come as no surprise that there are of course also DI frameworks for popular game engines and frameworks.&lt;br&gt;
In this post we’ll be taking a look at the dependency injection framework called &lt;a href="https://github.com/Mathijs-Bakker/Extenject"&gt;Extenject&lt;/a&gt; for the Unity game engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why even bother with Dependency Injection and IoC containers?
&lt;/h2&gt;

&lt;p&gt;Many of you may already know what DI and IoC containers are, as well as what kind of benefits they can bring to the project, but in case you don't, I’ll briefly give you an overview of the topics and how they can improve your architecture.&lt;/p&gt;

&lt;p&gt;DI is the act of moving the responsibility of fulfilling the class's dependencies and construction of these dependencies to somewhere else in the application.&lt;br&gt;
And this is where the IoC container comes in. Its job is to automatically provide these dependencies where needed which is done by setting up all the correct bindings in the container.&lt;/p&gt;

&lt;p&gt;The biggest benefit Dependency Injection and IoC containers is the loosely coupled architecture you achieve by using it. Classes no longer tightly depend on other class implementations and are not in control of how they obtain that dependency, or of how they should populate that dependency’s dependencies.&lt;/p&gt;

&lt;p&gt;Moreover, the architecture allows you to automatically lean towards the &lt;a href="https://en.wikipedia.org/wiki/Single-responsibility_principle"&gt;Single Responsibility Principle&lt;/a&gt; which ensures that each class only focuses on its own responsibilities rather than worrying about other things, such as achieving the correct dependencies.&lt;/p&gt;

&lt;p&gt;Finally, the architecture helps you create a bigger picture of the dependency of your applications as well as of how they are all wired up in the IoC container. And with that, it also makes it easier to exchange concrete implementations of such dependencies.&lt;/p&gt;

&lt;p&gt;TutorialsTeacher has a great &lt;a href="https://www.tutorialsteacher.com/ioc/dependency-injection"&gt;DI&lt;/a&gt; and &lt;a href="https://www.tutorialsteacher.com/ioc/ioc-container"&gt;IoC&lt;/a&gt; explanation focused on the .NET framework if you feel like reading a more in-depth explanation of this topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The .NET way
&lt;/h2&gt;

&lt;p&gt;So, to give a proper introduction to Extenject, I thought we should start out with a really simple example containing what many of you are probably most familiar with. In this case, I’ll use a simple .NET Core Web API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F-eLBWVY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9yst9d8t7xt587omxna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F-eLBWVY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9yst9d8t7xt587omxna.png" alt="Image description" width="880" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, we see the IoC container in a .NET Core project where we're telling it to hook up the &lt;em&gt;ISomeService&lt;/em&gt; interface as a &lt;em&gt;SomeServiceImplementation&lt;/em&gt; class wherever it’s needed. The SomeServiceImplementation is then injected as a singleton. There are other ways to inject your dependencies, but that is beyond the scope of this post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u2i-MTFB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydswy6yh8op18qyhst52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u2i-MTFB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydswy6yh8op18qyhst52.png" alt="Image description" width="880" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now the great thing is that we can simply inject this ISomeService interface into our classes where needed, and we don’t need to worry about where to get that dependency from or about what type of concrete implementation is needed. The IoC container takes care of all that for us.&lt;/p&gt;

&lt;p&gt;That’s neat! So let’s now take a look at how Extenject does this and how the process is a little bit different with Unity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Extenject way
&lt;/h2&gt;

&lt;p&gt;In order to get started with Extenject, you first have to download the latest package. I recommend downloading it from the &lt;a href="https://github.com/Mathijs-Bakker/Extenject/releases"&gt;official github repository&lt;/a&gt; since the one on the Unity Asset Store isn't updated as frequently.&lt;/p&gt;

&lt;p&gt;In Extenject, you set up your bindings through what is called &lt;em&gt;Installers&lt;/em&gt; which basically constitutes your IoC container. These installers are linked up with a &lt;em&gt;Context&lt;/em&gt;; a context defines the scope of where your bindings are set up. For instance, it's possible to have bindings that are only defined for certain scenes with a &lt;em&gt;Scene Context&lt;/em&gt;, but there’s also a &lt;em&gt;Project Context&lt;/em&gt; which sets up your bindings for the whole lifecycle of your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s3ZqaYIu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87y37m7mx9hb0d392s1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s3ZqaYIu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87y37m7mx9hb0d392s1l.png" alt="Image description" width="880" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are also different types of installers based on the types of bindings you want to set up. In our example, we will be using a &lt;em&gt;Mono Installer&lt;/em&gt; on a Scene Context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JCZyFYNp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fcod5h2gx2uv0z6e0n4l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JCZyFYNp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fcod5h2gx2uv0z6e0n4l.png" alt="Image description" width="880" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can create a Scene Context by right-clicking in your hierarchy and navigating to &lt;em&gt;Zenject -&amp;gt; Scene Context&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Je2tvMXQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqmmoguncvi8v55unui3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Je2tvMXQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqmmoguncvi8v55unui3.png" alt="Image description" width="880" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we take a look at our Installer which basically does the same as our .NET example, i.e. creating the &lt;em&gt;ISomeService&lt;/em&gt; with the concrete class &lt;em&gt;SomeServiceImplementation&lt;/em&gt; as a singleton.&lt;/p&gt;

&lt;p&gt;Extenject has a ton of different ways to set up your bindings, and I recommend you to check out their &lt;a href="https://github.com/Mathijs-Bakker/Extenject#binding"&gt;documentation&lt;/a&gt; for more info on that. They support many Unity specific bindings which will definitely come in handy for your projects.&lt;/p&gt;

&lt;p&gt;Extenject also supports multiple different ways of injecting your dependencies into your classes. From the official documentation, they are the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Constructor injection&lt;/li&gt;
&lt;li&gt;Method injection&lt;/li&gt;
&lt;li&gt;Property injection&lt;/li&gt;
&lt;li&gt;Field injection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those of you familiar with Unity might be wondering “&lt;em&gt;Constructor injection with Monobehaviours? How?&lt;/em&gt;” - and you would be right to think that. &lt;br&gt;
What the documentation is referring to is constructor injection through normal C# classes. &lt;/p&gt;

&lt;p&gt;Since MonoBehaviours can’t really have constructors, we have to use other ways of injecting our dependencies. They can still be injected through either field, property or method injection, where method injection is the preferred way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XrtQ1Sy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65ivsazv9xgiwjk2ddpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XrtQ1Sy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65ivsazv9xgiwjk2ddpc.png" alt="Image description" width="880" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we see a basic example of MonoBehaviour that injects their dependency through a method. My personal preference is to have a method at the top called &lt;em&gt;Construct&lt;/em&gt; and use that with the &lt;em&gt;[Inject]&lt;/em&gt; attribute to simulate a constructor. It also gives a fast and easy overview of which kind of dependencies this object needs. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;DI and IoC containers are great! And if you’re like me, you might have had the thought “&lt;em&gt;I’ll need to start using this in every Unity project from now on!&lt;/em&gt;”. However, I’m not sure that would be the right way to look at it. Different tools, for different problems.&lt;/p&gt;

&lt;p&gt;Extenject is a really good and powerful tool. It comes with a lot more than what I have shown in this post (it even has built-in support for &lt;a href="https://github.com/Mathijs-Bakker/Extenject/blob/master/Documentation/MemoryPools.md"&gt;memory pools&lt;/a&gt;). But, this also means it comes with some overhead, and it can also get a bit hairy to set up all your bindings correctly. &lt;/p&gt;

&lt;p&gt;It also depends on whether you’re working alone or with a team. Some considerations might be whether the entire team understands the ins and outs DI frameworks, and whether you have time/money to invest into onboarding people in the way your architecture for the project is set up. &lt;/p&gt;

&lt;p&gt;All these things need to be considered before you just start throwing Extenject into every one of your projects.&lt;/p&gt;

&lt;p&gt;But I hope this post has shown you yet another tool that you can add to your toolbelt. &lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>unity3d</category>
    </item>
    <item>
      <title>Speeding Up Development with Figma-to-code through Plasmic</title>
      <dc:creator>Sebastian Bove Wagner</dc:creator>
      <pubDate>Fri, 04 Feb 2022 09:34:12 +0000</pubDate>
      <link>https://forem.com/itminds/speeding-up-development-with-figma-to-code-through-plasmic-3o0e</link>
      <guid>https://forem.com/itminds/speeding-up-development-with-figma-to-code-through-plasmic-3o0e</guid>
      <description>&lt;p&gt;Visual web development software and no-code solutions for building static webpages are not exactly new inventions. While no-code has many pros - especially for people or companies with simple requirements and low budgets - it can also be very limiting, and it often doesn't fit well into the development of a complex dynamic site, especially if development has already begun.&lt;/p&gt;

&lt;p&gt;Many of the older no-code solutions are also not very performant nor expensive. &lt;/p&gt;

&lt;p&gt;There are newer no-code tools which are more performant and are able to work more seamlessly with classic web development and hosting (webflow.com for example), but if you're reliant on a lot of custom or legacy code, or want to integrate no-code into your own dev-ops environment, Plasmic might be a solution. And a fast one at that. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Plasmic?
&lt;/h2&gt;

&lt;p&gt;Plasmic is a no-code, collaborative visual builder for the web. &lt;/p&gt;

&lt;p&gt;You can build pages or components and either let Plasmic host them (which is great for quick changes, or for letting  an admin change Landing pages etc.), or generate REACTJS code.&lt;/p&gt;

&lt;p&gt;With the latter approach, Plasmic is free to use and easy to integrate into a pre-existing project. &lt;/p&gt;

&lt;p&gt;On top of that, Plasmic has created and maintains the &lt;a href="https://www.figma.com/community/plugin/845367649027913572/Figma-to-Code-by-Plasmic" rel="noopener noreferrer"&gt;Figma-to-Code&lt;/a&gt; plugin, which can greatly speed up the development of components. &lt;/p&gt;

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

&lt;p&gt;In the following simple example, a "designer" has requested the development of this simple page as an extension to a large, pre-existing project.&lt;br&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%2Fuploads%2Farticles%2F6dec46ejrbpaopwqqsrr.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dec46ejrbpaopwqqsrr.PNG" alt="The page in figma"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page consists of a couple of SVG files, some text and some buttons. &lt;/p&gt;

&lt;p&gt;With the Figma-to-Code plugin, you can copy and paste the entire page into Plasmic.&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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fueoa9c90cq6gkx2y9fiv.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fueoa9c90cq6gkx2y9fiv.PNG" alt="Using the plugin"&gt;&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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5iwopgqxshk2tlh0c5e9.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5iwopgqxshk2tlh0c5e9.PNG" alt="The page in plasmic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More practically, you can copy and paste both of the components into their own Plasmic components.&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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa0rsm91nzdmwjnqyce1.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa0rsm91nzdmwjnqyce1.PNG" alt="Earth component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, it's possible to make variants of each component; these variants can be interaction variants, such as Hover, Press etc. &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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkknewk95udgq94rahdz.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkknewk95udgq94rahdz.PNG" alt="Variants of component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now, to my favorite part. &lt;/p&gt;

&lt;p&gt;By pressing the publish button in the top right, you can connect the project to a github project (or other git projects) and automatically generate the components - ready for use. &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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhe2847o6kh8ccfhkaci.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhe2847o6kh8ccfhkaci.PNG" alt="Publishing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's even possible to trigger other CI-CD pipelines to automate deployment or testing. &lt;/p&gt;

&lt;p&gt;The generated view components will be put in a components/plasmic folder, and in the components folder you will find newly generated "skeleton" components that you can use to add any wanted functionality into.&lt;/p&gt;

&lt;p&gt;Voilá&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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg0i1lc906m7noq39ql3.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg0i1lc906m7noq39ql3.PNG" alt="The list of generated components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to be mindful of
&lt;/h2&gt;

&lt;p&gt;The entire process, from Figma to code, can take minutes, or seconds, if there are no large adjustments needed. &lt;/p&gt;

&lt;p&gt;Many Figma projects created by designers are not necessarily that easy to import. &lt;/p&gt;

&lt;p&gt;Masked components are buggy, and it isn't easy to make good, responsive flexbox designs if the designer doesn't use auto-layout or just manually enters padding per component. &lt;/p&gt;

&lt;p&gt;I also have to mention that Plasmic is not the only visual component creator tool, but the addition of the collaboration tools and deployment make it a powerful tool for fast development of visual components.&lt;/p&gt;

&lt;p&gt;Resources: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.plasmic.app/" rel="noopener noreferrer"&gt;plasmic&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.figma.com/community/plugin/845367649027913572/Figma-to-Code-by-Plasmic" rel="noopener noreferrer"&gt;Figma-to-Code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>figma</category>
      <category>plasmic</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Learn Next.js by creating a static HTML webpage</title>
      <dc:creator>Fridtjof Nystrøm</dc:creator>
      <pubDate>Thu, 13 Jan 2022 09:45:10 +0000</pubDate>
      <link>https://forem.com/itminds/learn-nextjs-by-creating-a-static-html-webpage-4l60</link>
      <guid>https://forem.com/itminds/learn-nextjs-by-creating-a-static-html-webpage-4l60</guid>
      <description>&lt;p&gt;React is great! With React, it's easy to create reusable components. It easily integrates with TypeScript, it can be used for mobile development - and it has a big community.&lt;/p&gt;

&lt;p&gt;In this post, we will create a simple website using the React framework Next.js and export the website as a static HTML webpage ready to be deployed anywhere. &lt;/p&gt;

&lt;h1&gt;
  
  
  👋🏼 Next.js
&lt;/h1&gt;

&lt;p&gt;Next.js is a framework on top of React that enables server-side rendering and static website generation. It comes with a lot of useful features, such as Image Optimization and SEO-friendly routing. We won't go into much detail on these topics, but you may want to check out &lt;a href="https://nextjs.org/docs/getting-started" rel="noopener noreferrer"&gt;Next.js' documentation&lt;/a&gt; if you find these topics interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's create our first Next.js app
&lt;/h2&gt;

&lt;p&gt;Let's head right in and initialize the project using create-next-app. Open your terminal and create a Next environment using the command below:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx create-next-app@latest --typescript&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Type in your project name, and watch the magic happen. To start the application in development mode, enter the project folder and run: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd my-app &amp;amp;&amp;amp; npm run dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should now be able to visit &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; to see the website.&lt;/p&gt;

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

&lt;p&gt;Open the folder belonging to the project, and look at the folders in the root of the project. It should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- /node_modules
- /pages
  - /api
  - index.js
- styles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The pages folder
&lt;/h3&gt;

&lt;p&gt;The pages folder equals to the routes of your webpage. &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; redirects to /pages/index.tsx. To create a new route, create a new file in the pages folder. For example, a /pages/about.tsx file would create the route &lt;a href="http://localhost:3000/about" rel="noopener noreferrer"&gt;http://localhost:3000/about&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  index.tsx
&lt;/h3&gt;

&lt;p&gt;Let's simplify the front page a little bit. Open index.tsx, and replace the file content with the code below:&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextPage&lt;/span&gt; &lt;span class="p"&gt;}&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;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Head&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;next/head&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&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;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&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;../styles/Home.module.css&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;Home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextPage&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="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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&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;Head&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;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&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;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"My own personal static html page"&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;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.ico"&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;Head&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;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&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="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Welcome to index.tsx!
        &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;This is a static HTML website built with React &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Next.js &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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;grid&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;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/people/me"&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;a&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;card&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="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;About me&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&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;Click here to navigate to people/me.&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;a&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;Link&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;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://nextjs.org/learn"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;card&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="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Learn &lt;span class="ni"&gt;&amp;amp;rarr;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&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;Learn more about Next.js!&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;a&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;div&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;main&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;div&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. The page should look something like this:&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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfrg3qn7izlvdtce3ddb.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/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfrg3qn7izlvdtce3ddb.png" alt="Example - index.tsx"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a &lt;code&gt;Link&lt;/code&gt; to &lt;code&gt;people/me&lt;/code&gt;. The &lt;code&gt;Link&lt;/code&gt; component takes care of page routing within the website. Right now, the link redirects to a 404 page. That's because the people/me page is missing from the application. Let's go ahead and create that file.&lt;/p&gt;

&lt;h3&gt;
  
  
  People/me
&lt;/h3&gt;

&lt;p&gt;Paste the following code into &lt;code&gt;pages/people/me.tsx&lt;/code&gt;. Feel free to change anything, for example the name.&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextPage&lt;/span&gt; &lt;span class="p"&gt;}&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;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Head&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;next/head&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&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;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&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;../../styles/Home.module.css&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;Me&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextPage&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="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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&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;Head&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;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;People - Me&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&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;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Page about me"&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;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.ico"&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;Head&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;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&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="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    Hello, my name is Fridtjof! 👋🏼
                &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;
                    Nice to meet you!
                &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="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&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="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
                        Navigate to the frontpage
                    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&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;Link&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;main&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;div&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Me&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter &lt;a href="http://localhost:3000/people/me" rel="noopener noreferrer"&gt;http://localhost:3000/people/me&lt;/a&gt;, and watch your website render. With this two-paged masterpiece finished, it is now time to share your masterpiece with the world. It's time to build!&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the static HTML
&lt;/h2&gt;

&lt;p&gt;Before you can deploy your website as a static website, it must be built and exported. To do so, change the build script in package.json to also export the project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"build": "next build &amp;amp;&amp;amp; next export"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Make sure that you've saved your latest changes, then run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When the script is done, a new folder named &lt;code&gt;out&lt;/code&gt; will appear within the project. To deploy the code, copy the content of the out folder to any web hotel or bucket, just as you would with any static HTML website.&lt;/p&gt;

&lt;p&gt;Congratulations! You now have your very own static website, created with React components that you can deploy anywhere. Note that this post only touches the surface of Next.js. To use Next.js for a simple page like the one we built is totally overkill, but it is a nice introduction. In a future post, we will look further into the advantages of using Next.js.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>nextjs</category>
      <category>react</category>
      <category>html</category>
    </item>
  </channel>
</rss>
