<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adam Gordon Bell</title>
    <description>The latest articles on DEV Community by Adam Gordon Bell (@adamgordonbell).</description>
    <link>https://dev.to/adamgordonbell</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F102039%2F52db35b7-86f7-4b41-ac43-8c4a21089b8f.gif</url>
      <title>DEV Community: Adam Gordon Bell</title>
      <link>https://dev.to/adamgordonbell</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamgordonbell"/>
    <language>en</language>
    <item>
      <title>What's gotten better in programming languages</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Tue, 14 Jun 2022 14:08:36 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/whats-gotten-better-in-programming-languages-50dh</link>
      <guid>https://dev.to/adamgordonbell/whats-gotten-better-in-programming-languages-50dh</guid>
      <description>&lt;p&gt;I have a question, especially for those who’ve been programming for some time or have experience with older languages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What improvements have you seen in programming languages that aren’t related to syntax or semantics of the language?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;I’m thinking about things like the introduction of package managers for pulling in third party dependencies, and the existence of code formatters or linters.&lt;/p&gt;

&lt;p&gt;I guess integrated testing into the tooling / language in rust and golang would count as well. &lt;/p&gt;

&lt;p&gt;( Another way to frame this would be what do you miss when you are programming in c or c++ or some older language that isn’t  the language, but more about the tooling and ecosystem.)&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Full Backend Apps on AWS Lambda?</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Thu, 19 May 2022 12:45:46 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/full-backend-apps-on-aws-lambda-499p</link>
      <guid>https://dev.to/adamgordonbell/full-backend-apps-on-aws-lambda-499p</guid>
      <description>&lt;p&gt;Is anyone hosting a full CRUD type app with routing and persisting as an AWS Lambda? &lt;/p&gt;

&lt;p&gt;And if so, how is that going?&lt;/p&gt;

&lt;p&gt;Since you can run a container as a lambda, and you get built in scaling up and down it seems like it might be a good fit.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Do you use GRPC?</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Mon, 31 Jan 2022 22:11:51 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/do-you-use-grpc-odm</link>
      <guid>https://dev.to/adamgordonbell/do-you-use-grpc-odm</guid>
      <description>&lt;p&gt;I'm writing an article about GRPC and protobufs. Here is the thing I'd like to know:&lt;/p&gt;

&lt;p&gt;What tools to you use for making GRPC requests? &lt;/p&gt;

&lt;p&gt;Like if you just want to test that a service is up and taking requests? I'd like a command line one that is simple to use and install and wondering what people use.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>go</category>
    </item>
    <item>
      <title>Web3 will not replace Contract Law?</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Fri, 28 Jan 2022 22:11:41 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/web3-will-not-replace-contract-law-3deg</link>
      <guid>https://dev.to/adamgordonbell/web3-will-not-replace-contract-law-3deg</guid>
      <description>&lt;p&gt;The law is code that is interpreted by Judges. My wife is a lawyer and tells me this. In this view, amendments or reinterpretations of laws by higher courts are diffs.&lt;/p&gt;

&lt;p&gt;I'm not really up on Web3, but this take is wrong somehow.&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1486698174869909506-976" src="https://platform.twitter.com/embed/Tweet.html?id=1486698174869909506"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1486698174869909506-976');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1486698174869909506&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;And not just in the funny way that many languages run in an interpreter:&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1486700389822910469-326" src="https://platform.twitter.com/embed/Tweet.html?id=1486700389822910469"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1486700389822910469-326');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1486700389822910469&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;But in the fact that laws being interpreted by a person is the point of the legal system. It's why it works.&lt;/p&gt;

&lt;p&gt;The fact that a judge gets to consider the specifics of your particular case and make a decision that takes those into account makes the legal system work.&lt;/p&gt;

&lt;p&gt;In cases where the law is interpreted absolutely, like three-strikes-you're-out laws or mandatory minimums, sentences are usually the places where things turn out badly.&lt;/p&gt;

&lt;p&gt;Explicit coded rules are bad at handling exceptional cases, and the real world is messy. People can enforce the spirit of the law and not just the letter of the law. &lt;/p&gt;

&lt;p&gt;On the other hand, the existing legal system is slow and expensive ( but almost no contracts are actually resolved in court, it's usually only the messy cases with the exceptions that end up in court). &lt;/p&gt;

&lt;p&gt;So, where do coded contracts make sense (Any ethereum people around) ? &lt;/p&gt;

</description>
      <category>web3</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Knowing I'm Dumb Is the First Step</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Thu, 27 Jan 2022 22:09:13 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/knowing-im-dumb-is-the-first-step-26p7</link>
      <guid>https://dev.to/adamgordonbell/knowing-im-dumb-is-the-first-step-26p7</guid>
      <description>&lt;p&gt;The Dunning-Kruger Effect is my favorite.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This effect has been the darling of journalists who want to explain why dumb people don’t know they’re dumb.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's where 'dumb' people don't know that they are 'dumb' and think they are better than more skilled people. &lt;/p&gt;

&lt;p&gt;So maybe it's false?&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1485319874738114564-592" src="https://platform.twitter.com/embed/Tweet.html?id=1485319874738114564"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1485319874738114564-592');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1485319874738114564&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;But I've found it helpful as a model of learning. Learning where you are ignorant is an essential step in understanding.&lt;/p&gt;

&lt;p&gt;When you don't know anything about software development, and you want to learn, it seems like it will either be easy or impossibly hard. It's just a giant skill to acquire. &lt;/p&gt;

&lt;p&gt;But as you learn, you start to recognize all the different gradations of learning. So this one big thing is actually a bunch of skills, and there are levels of each of them.&lt;/p&gt;

&lt;p&gt;And the more you learn about, say, backend development, the more you realize there are other areas you don't understand, like not just frontend, but networking programming, phone development, OS development, and writing firmware. Every little area is fractal with so much knowledge in it.&lt;/p&gt;

&lt;p&gt;Part of learning is just learning how much you don't know, so it makes sense that "dumb people don't know how dumb they are" because knowing how dumb you are is something you have to learn. &lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>discuss</category>
      <category>programming</category>
    </item>
    <item>
      <title>Incident Management Metrics and Key Performance Indicators</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Thu, 27 Jan 2022 20:53:33 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/incident-management-metrics-and-key-performance-indicators-45nf</link>
      <guid>https://dev.to/adamgordonbell/incident-management-metrics-and-key-performance-indicators-45nf</guid>
      <description>&lt;p&gt;In 2008, I got my first job at a software-as-a-service company. We built learning management software and ran it on servers in the small data center connected to our office.&lt;/p&gt;

&lt;p&gt;We released new software onto these production servers monthly and measured quality by counting bugs per release. We also had account managers who kept us informed of how many large clients seemed upset about the last release.  &lt;/p&gt;

&lt;p&gt;Occasionally, when something went wrong, we would do a stability release and spend a month only fixing bugs.  &lt;a href="https://earthly.dev/blog/unit-vs-integration"&gt;Testing&lt;/a&gt; was not a part of our build process but a part of our team: every feature team had quality assurance people who tested each feature before it was released.&lt;/p&gt;

&lt;p&gt;This wasn't that long ago, but cloud software development has matured a lot since this time. Incident management has become standard practice, and many great metrics and Key Performance Indicators (KPIs) exist for measuring release quality. Let's review some of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mean Time Between Failures (MTBF)
&lt;/h2&gt;

&lt;p&gt;When software is being released only once a month, on a fixed timeline, with extensive manual testing, counting the number of bugs might work. But once you start releasing many times per week or per day, this won't work, and another way to measure software quality is required.&lt;/p&gt;

&lt;p&gt;Mean time between failures is a metric from the field of &lt;a href="https://earthly.dev/blog/achieving-repeatability"&gt;reliability&lt;/a&gt; engineering. Calculating it is simple: it is time over the number of failures that occurred during that time. If in the last 30 days you have had two production incidents, then the mean time between failure is 15 days.&lt;/p&gt;
&lt;h3&gt;
  
  
  Calculating MTBF
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incidents in last 30 days&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;#1&lt;/td&gt;
&lt;td&gt;Jan 3rd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;#2&lt;/td&gt;
&lt;td&gt;Jan 25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mean Time Between Failures =&lt;/p&gt;

&lt;p&gt;: 30 days / 2 Incidents = 15 days&lt;/p&gt;

&lt;h2&gt;
  
  
  Mean Time to Recovery (MTTR)
&lt;/h2&gt;

&lt;p&gt;Something funny happens when you start releasing more frequently. You may end up with a higher count of issues in production, but resolving them will happen much faster. If each change is released separately using a continuous delivery model, then recovering gets easier -- often, all that is required is hitting a rollback button.&lt;/p&gt;

&lt;p&gt;If you are measuring MTBF, your software may be getting much better, but your numbers will be getting worse. Enter mean time to recovery. Mean time to recovery is just what it sounds like: you start a timer when the incident begins and stop it when production is healthy again - even a simple rollback counts. Average this number across incidents, and you have MTTR. You now have a metric that captures the health of your incidence response process.&lt;/p&gt;
&lt;h3&gt;
  
  
  Calculating Mean Time to Recovery
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident #1&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reported&lt;/td&gt;
&lt;td&gt;10:00 am.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovered&lt;/td&gt;
&lt;td&gt;12:00 pm.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovery Time&lt;/td&gt;
&lt;td&gt;2 Hours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident #2&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reported&lt;/td&gt;
&lt;td&gt;10:00 am.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovered&lt;/td&gt;
&lt;td&gt;2 days later at 10:00 am.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovery Time&lt;/td&gt;
&lt;td&gt;48 Hours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mean Time To Recovery =&lt;/p&gt;

&lt;p&gt;: 2 hour + 48 hours / 2 failures = 25 hours&lt;/p&gt;

&lt;h2&gt;
  
  
  Mean Time to Resolve (MTTRe)
&lt;/h2&gt;

&lt;p&gt;ℹ️ Acronyms Collision Alert&lt;/p&gt;

&lt;p&gt;Mean Time To Resolve, MTTRe, differs from Mean Time To Recover, MTTR, but some resources use MTTR for both. To avoid confusion, ensure you are using the correct terminology for your metric.&lt;/p&gt;

&lt;p&gt;Rolling back to address an incident is a great idea: it's often the quickest way to get things back in a good place. But there are other types of incidents. Imagine your application deadlocks every once in a while, and you have to restart it to unlock. You may have an excellent mean time to recovery, but you've never actually addressed the root cause. This is what MTTRe measures, not the time to get the service back up and running but to resolve the root cause and ensure the problem never happens again.  &lt;/p&gt;

&lt;p&gt;The never-happens-again part is hard to achieve but vital. If you are responding quickly but never getting to the root cause, you will be living in a stressful world of constant fire fighting. However, if you are resolving the root cause of each incident, then quality will increase over time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Calculating Mean Time to Resolve
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident #3&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reported&lt;/td&gt;
&lt;td&gt;day 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Addressed&lt;/td&gt;
&lt;td&gt;day 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root Cause Analysis&lt;/td&gt;
&lt;td&gt;day 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root Cause Addressed&lt;/td&gt;
&lt;td&gt;day 31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resolve Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30 days&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident #4&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reported&lt;/td&gt;
&lt;td&gt;day 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Addressed&lt;/td&gt;
&lt;td&gt;day 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root Cause Analysis&lt;/td&gt;
&lt;td&gt;day 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root Cause Addressed&lt;/td&gt;
&lt;td&gt;day 11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resolve Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10 days&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mean Time To Resolve =&lt;/p&gt;

&lt;p&gt;: 30 days + 10 days / 2 incidents = 20 days&lt;/p&gt;

&lt;h2&gt;
  
  
  Mean Time to Acknowledge (MTTA)
&lt;/h2&gt;

&lt;p&gt;An essential part of good incident management is an on-call rotation. You need someone around to respond to incidents when they occur. Our previous metrics would be unable to differentiate between an incident that took 3 hours to recover from and one that was recoverable in 5 minutes but took two hours and 55 minutes to be acknowledged.  &lt;/p&gt;

&lt;p&gt;MTTA highlights this difference. It is a metric for measuring the responsiveness of the on-call person to any alerts.&lt;/p&gt;
&lt;h3&gt;
  
  
  ️Calculating Mean Time to Acknowledge
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident #5&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reported&lt;/td&gt;
&lt;td&gt;10 am&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acknowledged&lt;/td&gt;
&lt;td&gt;10: 05 am&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovered&lt;/td&gt;
&lt;td&gt;12:00 pm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Acknowledge Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5 minutes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident #6&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reported&lt;/td&gt;
&lt;td&gt;10 am&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acknowledged&lt;/td&gt;
&lt;td&gt;11: 55 am&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovered&lt;/td&gt;
&lt;td&gt;12:00 pm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Acknowledge Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;115 minutes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mean Time To Acknowledge =&lt;/p&gt;

&lt;p&gt;: 5 minutes + 115 minutes / 2 incidents = 60 minutes&lt;/p&gt;

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

&lt;p&gt;There are many ways to measure the quality of your software as a service product. MTBF, MTTR, MTTRe, and MTTA can each offer a different lens for viewing your software release life cycle. As you improve your Software Development Life Cycle, find ways to collect aggregate metrics like these and choose one or two to target for improvement.&lt;/p&gt;

&lt;p&gt;Invest in improving these metrics, and you'll make up for it in time saved fighting fires. Also, focusing on aggregate metrics can be an effective way to move the discussion from blame about specific incidents to a higher-level debate around changing the process to better support the company's goals.&lt;/p&gt;

&lt;p&gt;If your build pipeline is taking more than 15 minutes and therefore negatively affecting your metrics, then take a look at Earthly's &lt;a href="http://earthly.dev/"&gt;free and open build tool&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Originally published on &lt;a href="https://earthly.dev/blog/incident-management-metrics/"&gt;Earthly's blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>linux</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Property-Based Testing In Go</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Wed, 12 Jan 2022 16:31:08 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/property-based-testing-in-go-48kb</link>
      <guid>https://dev.to/adamgordonbell/property-based-testing-in-go-48kb</guid>
      <description>&lt;p&gt;Have you ever wanted your unit tests written for you? Property based testing is a powerful testing technique that, in a sense, is just that. You describe the properties you’d like to test, and the specific cases are generated for you.&lt;/p&gt;

&lt;p&gt;Property-based testing can be a bit trickier to learn, and not every problem can be well tested in this manner, but it’s a powerful technique that’s well supported by the go std-lib (&lt;code&gt;testing/quick&lt;/code&gt;) and that is under-utilized.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing &lt;code&gt;csvquote&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;csvquote&lt;/code&gt; is a small program that makes it easier to deal with CSV files at the command line. It does this by replacing problematic CSV characters with the controls characters &lt;code&gt;\x1e&lt;/code&gt; and &lt;code&gt;\x1f&lt;/code&gt; and later removing them.&lt;/p&gt;

&lt;p&gt;You can use it 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;csvquote | head | csvquote -u
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want to improve it a bit, but first, I want to add some testing to ensure I don’t break it. The tests will also help me document how it works.&lt;/p&gt;

&lt;p&gt;Here are my initial test cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var tests = []struct {
 in string
 out string
}{
 {`"a","b"`, `"a","b"`}, // Simple
 {"\"a,\",\"b\"", "\"a\x1f\",\"b\""}, //Comma
 {"\"a\n\",\"b\"", "\"a\x1e\",\"b\""}, //New Line
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test that the &lt;code&gt;in&lt;/code&gt; string always results in the &lt;code&gt;out&lt;/code&gt;, I do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func TestSubstitute(t *testing.T) {
 f := substituteNonprintingChars(',', '"', '\n')
 for _, tt := range tests {
  out := string([]byte(apply([]byte(tt.in), f)))
  assert.Equal(t, tt.out, out, "input and output should match")
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;substituteNonprintingChars&lt;/code&gt; returns a function that does the conversion and &lt;code&gt;apply&lt;/code&gt; is a helper for applying that function over a &lt;code&gt;[]byte&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Slightly simplified, it looks 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;func apply(data []byte, f mapper) []byte {
 count := len(data)

 for i := 0; i &amp;lt; count; i++ {
  data[i] = f(data[i])
 }
 return data
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second thing I want to test is that I can reverse this operation. That is, when &lt;code&gt;csvquote&lt;/code&gt; is run with &lt;code&gt;-u&lt;/code&gt;, it should always return the original input.&lt;/p&gt;

&lt;p&gt;I can test this by going in reverse from output to input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func TestRestore(t *testing.T) {
 f := restoreOriginalChars(',', '\n')
 for _, tt := range tests {
  in := string([]byte(apply([]byte(tt.out), f)))
  assert.Equal(t, tt.in, in, "input and output should match")
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;restoreOriginalChars&lt;/code&gt; is the function that restores the string to its original form.&lt;/p&gt;

&lt;p&gt;Both these tests pass (the full source is &lt;a href="https://github.com/adamgordonbell/csvquote/blob/7f4698ad3d3c2d12f063b5d8a8bd304e8307a089/cmd/cvsquote/main_test.go"&gt;on GitHub&lt;/a&gt;). However, it’s still possible there are edge cases that work incorrectly. What I need is a way to generate more test cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Property-Based Testing
&lt;/h2&gt;

&lt;p&gt;One way I could improve my testing is to combine the two tests. That is, rather than testing that the substituted value matches expectations and that the restored value matches expectations, I can simply test that substituting and restoring a string always results in the original string.&lt;/p&gt;

&lt;p&gt;That is for almost all possible values of the string &lt;code&gt;a&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo a | csvquote | csvquote -u == a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my go test, I can state that property 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;func doesIdentityHold(in string) bool {
 substitute := substituteNonprintingChars(',', '"', '\n')
 restore := restoreOriginalChars(',', '\n')
 substituted := apply([]byte(in), substitute)
 restored := string([]byte(apply(substituted, restore)))
 return in == restored
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is, for a string &lt;code&gt;in&lt;/code&gt;, substituting and then restoring it should equal itself. I’m calling this &lt;code&gt;doesIdentityHold&lt;/code&gt; because an identity is any function that returns the original result. &lt;code&gt;csvquote | csvquote -u&lt;/code&gt; should be an identity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Check
&lt;/h3&gt;

&lt;p&gt;Now that we have that function, we can use &lt;code&gt;testing/quick&lt;/code&gt;, the property-based testing library, to test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func TestIdentity1(t *testing.T) {
 if err := quick.Check(doesIdentityHold, nil); err != nil {
  t.Error(err)
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, this will cause &lt;code&gt;testing/quick&lt;/code&gt; to run 100 iterations of my identity test, using random strings as inputs. You can bump that up much higher, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func TestIdentity1(t *testing.T) {
 c := quick.Config{MaxCount: 1000000} // &amp;lt;- changed
  if err := quick.Check(doesIdentityHold, &amp;amp;c); err != nil {
  t.Error(err)
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with that number of tests cases, I run into a problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- FAIL: TestIdentity1 (5.17s)
    main_test.go:43: #13072: failed on input "\x1f\U00037f8b\U000732a1\"
FAIL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failing test is helpfully printed. However, it’s found a problem in my testing method, not with the code under test. You see, &lt;code&gt;csvquote&lt;/code&gt; should always be able to reverse it’s input, &lt;em&gt;except in the case that the input contains the non-printable ASCII control characters&lt;/em&gt; and that is what this test case has found.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\x1f&lt;/code&gt; may not be used much outside of traditional teletype terminals but I asked &lt;code&gt;csvquote&lt;/code&gt; to check across all possible strings, and eventually, it found a failing case.&lt;/p&gt;

&lt;p&gt;Long term, I should adjust &lt;code&gt;csvquote&lt;/code&gt; to exit with an error condition when it receives input it can’t handle, but that is a problem for another day. Instead let’s focus on constraining the input used in the tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a Property Testing Generator
&lt;/h2&gt;

&lt;p&gt;As I’ve shown above, &lt;code&gt;testing/quick&lt;/code&gt; can generate test values for testing on its own, but it’s often valuable to write your own.&lt;/p&gt;

&lt;p&gt;In this case, writing my own CSV file generator will allow me to make a couple of improvements to the test. First of all, I can remove control characters from the test set. But also, I can move to generating strings that are valid CSV files, rather than just random characters. Testing with random characters is easy, but my main concern is that &lt;code&gt;csvquote&lt;/code&gt; can handle valid CSV files, so by narrowing in on that case, I’ll have a better chance of catching real-world issues.&lt;/p&gt;

&lt;p&gt;First I write a function to generate random strings of size &lt;code&gt;size&lt;/code&gt; and from the character set &lt;code&gt;alphabet&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func RandString(r *rand.Rand, size int, alphabet string) string {
 var buffer bytes.Buffer
 for i := 0; i &amp;lt; size; i++ {
  index := r.Intn(len(alphabet))
  buffer.WriteString(string(alphabet[index]))
 }
 return buffer.String()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It outputs what I expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;r := rand.Rand{}
println(RandString(&amp;amp;r, 5, "abc"))
println(RandString(&amp;amp;r, 5, "abc"))

caccb
cabbb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I used that to generate a CSV files with random number or lines and rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func randCSV(r *rand.Rand) string {
 var sb strings.Builder
 lines := r.Intn(20) + 1
 rows := r.Intn(20) + 1
 for i := 0; i &amp;lt; lines; i++ {
  for j := 0; j &amp;lt; rows; j++ {
   if j != 0 {
    sb.WriteString(`,`)
   }
   sb.WriteString(fmt.Sprintf(`"%s"`, randCSVString(r)))
  }
  sb.WriteString("\n")
 }
 return sb.String()
}

func randCSVString(r *rand.Rand) string {
 s := RandString(r, 20, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01233456789,\"")
 return strings.Replace(s, `"`, `""`, -1) // In CSV all double quotes must be doubled up
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what calling &lt;code&gt;randCSVString&lt;/code&gt; will generate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"7IWvdDEGTxlRdJZrc4Ra","LMHcCcN,D2RVRsfbw0IF",",LrZ3B4SvFEfG3FqO01n","""vY4FmzhbyR4iDgYpr6f","sw83uJkitIc8trzaYmEO"
"fGloXshqwC""O9Bd,eM,L","LDRajiwNgZNitCA0QL11","1Q
nqhMhzu7Mlu92qxK""8","jtj33vTlH5VP4UlbKu2e","sDhVlor4RRg4JrKfdR""W"
"EGWYmpm1TVNdB3rfmU80","Mx0RdqqVUuWiOpKY""IXk","ctprb8
7PrVu8rX8iJqTg","wnmagEjUDJrZCGBdmAYH","EJtuq1ZJqz9jf,j6vzh1"
",88Q7qWLewZVK9pE83ut","1kZJipoz2FOWLS96xMjW","5BkVvtZqZk""S2wpOB3rQ","kYN6aGPDmgSLAEI4CJtu","xOH,9y9wDDtwWUPsjgz7"
"mbexX2,Wl4""cuq3VGekP","uMXK5uLDz2ZS3Wv4wynY","KJBLt0RipsJyEqVHqrDx","glDHHs6Ujyg""piJGv595","rIa4KOxe,B""qS7EmO
nTB"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can then hook this up to my test method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func TestIdentity2(t *testing.T) {
 c := quick.Config{MaxCount: 10000,
  Values: func(values []reflect.Value, r *rand.Rand) {
   values[0] = reflect.ValueOf(randCSV(r))
  }}
 if err := quick.Check(doesIdentityHold, &amp;amp;c); err != nil {
  t.Error(err)
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now I can test &lt;code&gt;csvquote&lt;/code&gt; against ten thousand random CSV files in under a second.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; go test
PASS
ok github.com/adamgordonbell/csvquote/cmd/cvsquote 0.635s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ll leave extending this to covering Unicode values and unquoted rows for another day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Property Testing
&lt;/h3&gt;

&lt;p&gt;Property-based testing is a powerful technique for testing your code. As you can see from this real-world but somewhat contrived scenario, it moves the work of testing away from specific test cases and towards validating properties that hold for all values. Here the property under test was that substituting and restoring are inverses of each other (and therefore form an identity).&lt;/p&gt;

&lt;p&gt;For me, the most challenging part of property-based testing is seeing how I can transform a specific test-case into a testable property. It’s not always immediately clear what’s possible to test in this manner and some tests are simply hard to state in this form. But when a problem is well suited for property-based testing, it is a powerful tool!&lt;/p&gt;

&lt;p&gt;( The second most challenging thing about property-based testing is writing generators for your problem domain. Although once written, they make testing in this style feel quite natural. )&lt;/p&gt;

&lt;h2&gt;
  
  
  More Resources
&lt;/h2&gt;

&lt;p&gt;The hardest part of this style of testing is seeing where and how it can apply. The places I’ve found it valuable include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verifying serialization and deserialization code&lt;/li&gt;
&lt;li&gt;Verifying an optimization (by comparing the results to the un-optimized version)&lt;/li&gt;
&lt;li&gt;Anywhere I have more than one representation of some data and need to test the boundaries of those representations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But others have found more ways to use this paradigm. If you want to learn more about property-based testing, then &lt;a href="https://github.com/leanovate/gopter"&gt;&lt;code&gt;gopter&lt;/code&gt;&lt;/a&gt;, &lt;em&gt;the GOlang Property TestER&lt;/em&gt;, is worth taking a look at. &lt;a href="https://github.com/amir"&gt;Amir Saeid&lt;/a&gt;, who’s good at this technique, recommends this &lt;a href="https://leanpub.com/property-based-testing-in-a-screencast-editor"&gt;book&lt;/a&gt; full of examples, and &lt;a href="https://jacobstanley.io/how-to-use-hedgehog-to-test-a-real-world-large-scale-stateful-app/"&gt;this blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any tips or suggested resources for property-based testing, please let me know on Twitter (&lt;a href="https://twitter.com/adamgordonbell/"&gt;@adamgordonbell&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;And if you care about reliable software, take a look at &lt;a href="https://earthly.dev/"&gt;Earthly&lt;/a&gt;. Earthly makes continuous integration less flakey and works with your existing workflow.&lt;/p&gt;

</description>
      <category>go</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Migrating Your Open Source Builds Off Of Travis CI</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Fri, 07 Jan 2022 16:53:01 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/migrating-your-open-source-builds-off-of-travis-ci-24i6</link>
      <guid>https://dev.to/adamgordonbell/migrating-your-open-source-builds-off-of-travis-ci-24i6</guid>
      <description>&lt;p&gt;Starting in early 2021 and continuing to this day, a movement has been underway to migrate open-source projects off of Travis CI. So what happened, and where should you move your project to?&lt;/p&gt;

&lt;p&gt;I bet you can't guess where I 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%2Fearthly.dev%2Fblog%2Fassets%2Fimages%2Fmigrating-from-travis%2Fquote1.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%2Fearthly.dev%2Fblog%2Fassets%2Fimages%2Fmigrating-from-travis%2Fquote1.png" alt="Travis not providing CI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're not familiar with Travis CI, it's a build company that has been powering the continuous integration (CI) of many open source projects since it launched in 2011. It was the first build solution that was free for open source use and that easily integrated into GitHub.&lt;/p&gt;

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

&lt;p&gt;In 2019 Travis was acquired by a private equity group and many engineers were let go.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So apparently Travis CI is being strip-mined immediately after their acquisition by Idera. Sorry, I mean after "joining the Idera family" 🙄 &lt;a href="https://t.co/CE5ERp1RsY" rel="noopener noreferrer"&gt;https://t.co/CE5ERp1RsY&lt;/a&gt; A bunch of talented people are waking up to termination letters. Absolutely shameful. &lt;a href="https://t.co/BbBRPdnswe" rel="noopener noreferrer"&gt;https://t.co/BbBRPdnswe&lt;/a&gt;&lt;/p&gt;— Senior Oops Engineer (&lt;a class="mentioned-user" href="https://dev.to/reinh"&gt;@reinh&lt;/a&gt;) &lt;a href="https://twitter.com/ReinH/status/1098663375985229825?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;February 21, 2019&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, on Nov 2, 2020, Travis CI announced the end of its unlimited support for open-source projects:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For those of you who have been building on public repositories (on travis-ci.com, with no paid subscription), we will upgrade you to our trial (free) plan with a 10K credit allotment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When your credit allotment runs out - we'd love for you to consider which of our plans will meet your needs.&lt;/strong&gt; - &lt;a href="https://blog.travis-ci.com/2020-11-02-travis-ci-new-billing" rel="noopener noreferrer"&gt;Travis CI blog post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reason behind the change is stated to be abuse by crypto-miners:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;However, in recent months we have encountered significant abuse of the intention of this offering (increased activity of cryptocurrency miners, TOR nodes operators etc.).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, many feel the real reason is that the acquirer is aiming for profitability at all costs and supporting the open-source community represents a significant cost.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My previous company was on Travis, and as soon as I saw that Travis was purchased by private equity, I knew the downward spiral had begun and I recommended we move to something else. Not surprised that this is happening a couple of years later...my understanding is that private equity will tend towards slowing/stopping development after acquisition to cut costs/headcount, and then squeeze the remaining value from what's left, so this is in-line with that playbook.  - &lt;a href="https://news.ycombinator.com/item?id=25340486" rel="noopener noreferrer"&gt;rpdillion on hacker news&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why It Matters
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The open source movement runs on the heroic efforts of not enough people doing too much work. They need help. - &lt;a href="https://www.wired.com/author/clive-thompson" rel="noopener noreferrer"&gt;CLIVE THOMPSON&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many open-source projects are still using Travis and open-source maintainers are notoriously overworked.  Time spent migrating builds is time not spent on other things.  Large well-maintained projects will likely quickly transition but for many smaller projects, an abrupt change in a service they depend on is a huge challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Move To
&lt;/h2&gt;

&lt;p&gt;If you maintain an open-source project that uses TravisCI and are hoping to get off it, then assuming you have the time to migrate, there are actually many viable options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option: Run Your Own Builds
&lt;/h3&gt;

&lt;p&gt;You can find some &lt;a href="https://medium.com/google-developers/how-to-run-travisci-locally-on-docker-822fc6b2db2e" rel="noopener noreferrer"&gt;scattered&lt;/a&gt; &lt;a href="https://stackoverflow.com/a/35972902" rel="noopener noreferrer"&gt;instructions&lt;/a&gt; &lt;a href="https://stackoverflow.com/a/35972902" rel="noopener noreferrer"&gt;online&lt;/a&gt; for running Travis builds yourself. There are mixed reports on the stability and feasibility of this approach, but if your adventurous, you could try to set up your own Travis CI build executor on your own hardware.&lt;/p&gt;

&lt;p&gt;A better option, if you want to run the builds on your own hardware is to look at something like &lt;a href="https://buildkite.com/" rel="noopener noreferrer"&gt;Buildkite&lt;/a&gt; or &lt;a href="https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/" rel="noopener noreferrer"&gt;GitLab CI&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option: Circle CI
&lt;/h3&gt;

&lt;p&gt;A better option is &lt;a href="https://dev.to/blog/continuous-integration#circleci"&gt;Circle CI&lt;/a&gt; , a Travis CI competitor which still offers a free plan.  &lt;/p&gt;

&lt;p&gt;Circle CI offers 400,000 build credits per month to any open-source public repository.  This is their free plan and limits concurrency to 1 job at a time. They also have an easy GitHub integration and no application process.  &lt;/p&gt;

&lt;p&gt;They also allow use of the &lt;a href="https://circleci.com/open-source/" rel="noopener noreferrer"&gt;free plan&lt;/a&gt; with private repositories. This makes it a great choice if your project is not actually open-source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Option: GitHub Actions
&lt;/h3&gt;

&lt;p&gt;An even better option is &lt;a href="https://dev.to/blog/continuous-integration#github-actions"&gt;GitHub Actions&lt;/a&gt;, a cloud CI system directly from GitHub.  GitHub is at the center of many open source projects and this makes it a natural choice for CI.  &lt;/p&gt;

&lt;p&gt;GitHub Actions (GHA) is newer than either TravisCI or Circle CI, having launched in late 2018.&lt;/p&gt;

&lt;p&gt;GHA offers very generous build credits, 20 concurrent build jobs per project and no limit on build time used.   If your pipeline can be run in parallel this concurrency can really be a great enabler.  The only limitation I was able to find is that the build may last no longer than 6 hours in total.&lt;/p&gt;

&lt;p&gt;If your project is hosted on GitHub and is open source then the &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/usage-limits-billing-and-administration" rel="noopener noreferrer"&gt;GHA open source plan&lt;/a&gt; seems like the best bet right now.&lt;/p&gt;

&lt;p&gt;But wait, there is an even better solution: being vendor neutral. Before I explain how you become vendor neutral lets review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary of Open Source Plans
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Open Source Offering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Travis CI&lt;/td&gt;
&lt;td&gt;&lt;a href="https://blog.travis-ci.com/2020-11-02-travis-ci-new-billing" rel="noopener noreferrer"&gt;1000 minutes total with application process for more&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Circle CI&lt;/td&gt;
&lt;td&gt;&lt;a href="https://circleci.com/open-source/" rel="noopener noreferrer"&gt;1 concurrent build at a time&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/usage-limits-billing-and-administration" rel="noopener noreferrer"&gt;20 concurrent build jobs per project&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Don't Let This Happen Again
&lt;/h2&gt;

&lt;p&gt;So GitHub has a generous build plan, but moving your CI process is not easy or free.  The more complex your build, the harder porting from one cloud CI to another is going to be.  If you move to GHA and then GHA stops being a viable option in the future then this whole effort will have to be repeated.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Neutral Build Specifications
&lt;/h2&gt;

&lt;p&gt;How can you minimize the effort of moving from build platform to another?&lt;/p&gt;

&lt;p&gt;My suggestion is to keep as much logic as possible out of the proprietary build definition. Instead, define it in an open-source format that you can execute anywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Makefiles and Dockerfiles
&lt;/h3&gt;

&lt;p&gt;One way to build a CI neutral build definition is to use a Makefile and a dockerfile.  The Makefile contains the various steps of your build pipeline and you run it inside a docker container which installs any needed dependencies.  &lt;a href="https://github.com/qmk/qmk_firmware" rel="noopener noreferrer"&gt;QMK&lt;/a&gt; is a popular open-source project that uses this approach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; qmkfm/base_container&lt;/span&gt;

&lt;span class="k"&gt;VOLUME&lt;/span&gt;&lt;span class="s"&gt; /qmk_firmware&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /qmk_firmware&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; make all:default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/qmk/qmk_firmware/blob/master/Dockerfile" rel="noopener noreferrer"&gt;QMK&lt;/a&gt; Docker File for executing the full build&lt;/p&gt;

&lt;h3&gt;
  
  
  Earthly
&lt;/h3&gt;

&lt;p&gt;I am an Earthly contributor and this is the Earthly blog, but in my totally biased opinion, it deserves a mention as an neutral format for defining a build. The Elixir web framework &lt;a href="https://github.com/phoenixframework/phoenix/blob/master/Earthfile" rel="noopener noreferrer"&gt;Phoenix is a great example to take a look at&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Earthly is like a Makefile where each step is containerized and dependencies are explicitly declared.  &lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:1.13-alpine3.11&lt;/span&gt;

build:
 COPY main.go .
 RUN go build main.go
 SAVE ARTIFACT main AS LOCAL main

lint: 
 ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example build steps for a &lt;a href="https://github.com/earthly/earthly/blob/main/examples/go/Earthfile" rel="noopener noreferrer"&gt;go application&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Interesting Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Easier Migration from Travis to GHA
&lt;/h3&gt;

&lt;p&gt;Migrating your build out of Travis will take a little work.  If you aren't interested in a neutral format, &lt;a href="https://github.com/marketplace/actions/run-travis-yml" rel="noopener noreferrer"&gt;this GHA action&lt;/a&gt; might make it easier.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This action setups environment variables specified in the &lt;code&gt;.travis.yml&lt;/code&gt; file and then runs &lt;em&gt;one&lt;/em&gt; of the (potentially) many build jobs within the test build stage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Serverless Builds
&lt;/h3&gt;

&lt;p&gt;Another interesting option if you are feeling adventurous is using AWS lambda as your build executor.  I have no idea how feasible this is, however, &lt;a href="https://github.com/StanfordSNR/gg" rel="noopener noreferrer"&gt;the gg project&lt;/a&gt; from Stanford looks interesting.  It attempts to use AWS lambdas for running builds at the maximum possible parallelism.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Take-Aways
&lt;/h2&gt;

&lt;p&gt;You probably need to move your open-source project's builds off of Travis CI. If you host it on GitHub, GitHub Actions is probably a good choice. There is a risk that the GHA offer will disappear as well.  You can protect yourself from that by defining your build in an open format that is easy to move around.  All build problems can be solved by another layer of abstraction.&lt;/p&gt;

&lt;p&gt;If you are going that route, I think &lt;a href="https://earthly.dev/" rel="noopener noreferrer"&gt;Earthly&lt;/a&gt; is a great option, but as I said, I am biased.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Idiots And Maniacs </title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Wed, 05 Jan 2022 20:12:21 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/idiots-and-maniacs-nmb</link>
      <guid>https://dev.to/adamgordonbell/idiots-and-maniacs-nmb</guid>
      <description>&lt;h2&gt;
  
  
  Observability
&lt;/h2&gt;

&lt;p&gt;If you do software-as-a-service development and you have paying customers, you at some point learn about the need for operational monitoring and observability. Personally, I went through a process something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Service has some basic &lt;a href="//earthly.dev/blog/understanding-docker-logging-and-log-files"&gt;logging&lt;/a&gt; and an uptime alert.&lt;/li&gt;
&lt;li&gt;Service has a health-check endpoint, is deployed in triplicate behind a load balancer.&lt;/li&gt;
&lt;li&gt;Logs are real-time shipped to Splunk / ELK Stack.&lt;/li&gt;
&lt;li&gt;Metrics set up in Datadog / Prometheus with paging.&lt;/li&gt;
&lt;li&gt;Distributed Tracing set up for debugging across services.&lt;/li&gt;
&lt;li&gt;And so on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each step requires more work to set up and has some additional benefits. I moved through each step by necessity as my service handled more requests and became more important to customers.&lt;/p&gt;

&lt;p&gt;If you had shown step-1-me what a simple REST service looks like in step 5, I would have been shocked. The metrics counters and distributed tracing spans, and various operational concerns make the service more complex. I would have thought that whoever wrote the service was obsessed with operational issues, to the detriment of solving the problems at hand. I would have thought the service author was an observability maniac.&lt;/p&gt;

&lt;p&gt;On the other hand, if step-5-me were to get paged because a service written by step-1-me was down, he would not be happy. He would have a hard time figuring out what was wrong, and he'd be pretty sure the service author was an idiot who had never been paged in the middle of the night.&lt;/p&gt;

&lt;p&gt;It reminds me a lot of driving when it first snows here in Peterborough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Driving
&lt;/h2&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fidiots-and-maniacs%2F9580-800-b155e3159.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fidiots-and-maniacs%2F9580-800-b155e3159.webp" alt="Driving in the snow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the winter, in Peterborough, we get snow. It's just a fact of life, and people learn how to drive in snowy conditions. But on the first substantial snowfall of the year, people struggle to remember how to drive.&lt;/p&gt;

&lt;p&gt;Some will drive way below the speed limit and slow down traffic for everyone. They are idiots in the snow. Others get frustrated by the slow drivers and go too fast for the road conditions. Don't they know there is snow on the road?&lt;/p&gt;

&lt;p&gt;Everyone driving slower than me is an idiot, but everyone going faster than me is a maniac.&lt;/p&gt;

&lt;p&gt;So it is with software development. Everyone who takes an idea further than I have is a maniac, and people who haven't taken it as far as me are idiots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;There was a time when I thought all code should have 80% unit test code coverage as a minimum. Anything less was practically unethical, and if you didn't think so, then you hadn't read Clean Code™️ enough times.&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fidiots-and-maniacs%2F9770-500-410f473a4.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fidiots-and-maniacs%2F9770-500-410f473a4.webp" alt="SQLite"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, Richard Hipp – who tests to 100% code coverage at the machine code level, covering every branch by running billions of tests each release&lt;sup id="fnref1"&gt;1&lt;/sup&gt; – is a testing maniac.&lt;/p&gt;

&lt;p&gt;I hope you see where I'm going. This idiot to maniac gradient feels right, but it makes no sense&lt;sup id="fnref2"&gt;2&lt;/sup&gt;. How can I be the only the person driving the right speed? Wherever you find yourself along the spectrum is more a reflection of the context in which you work than anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Examples
&lt;/h2&gt;

&lt;p&gt;Here are some made-up and exaggerated examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Typescript Developer:&lt;/strong&gt; JavaScript developers are &lt;strong&gt;idiots&lt;/strong&gt;. Don't they know how many bugs the type system could have caught for them.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typescript Developer:&lt;/strong&gt; Elm developers are obsessed with types. They are &lt;strong&gt;maniacs&lt;/strong&gt; about using types to catch things at compile time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go Developer:&lt;/strong&gt; The JVM is such a heavyweight runtime and uses so much memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go Developer:&lt;/strong&gt; Rust is so complex! Who wants to manage memory manually. Use a GC, you &lt;strong&gt;maniacs&lt;/strong&gt;!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin Developer:&lt;/strong&gt; Java is so verbose and ugly. Welcome to 2021. Kotlin has a lot of sugar and type improvements that make writing correct code simpler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin Developer:&lt;/strong&gt; Scala is for &lt;strong&gt;maniacs&lt;/strong&gt;. There is so much syntactic sugar and type stuff that it's not worth learning about.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not trying to pick on any of these languages - Typescript, Go, and Kotlin are exceptionally well suited to their niche. But contexts vary&lt;sup id="fnref3"&gt;3&lt;/sup&gt;, and it takes mental effort to see that people making other trade-offs sometimes have good reasons for it.&lt;/p&gt;

&lt;p&gt;That doesn't mean that other people are never wrong, though. People choose the wrong tool for the job all the time. For example, if I were doing SQLite's level of testing for a low-reliability, low-traffic, state-less web service when a couple of integration tests would do, then that'd be a mistake, but you'd have to know the context to make that call.&lt;/p&gt;

&lt;p&gt;What do you think? What are you a maniac about? &lt;/p&gt;

&lt;p&gt;I'm a maniac about builds. This article was originally published on &lt;a href="//earthly.dev"&gt;earthly.dev&lt;/a&gt; where we make pretty cool build stuff.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Richard is the creator of SQLite. See my &lt;a href="https://corecursive.com/066-sqlite-with-richard-hipp/#billions-of-tests" rel="noopener noreferrer"&gt;interview&lt;/a&gt; with him for a discussion of his testing approach. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;strong&gt;Article Update:&lt;/strong&gt; Apparently I've unintentionally stolen this idiot to maniac spectrum idea from &lt;a href="https://www.youtube.com/watch?v=XWPCE2tTLZQ" rel="noopener noreferrer"&gt;George Carlin&lt;/a&gt;. Thanks to tjones21xx on Reddit for pointing this out. All credit for this idea goes to Carlin. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Personal and team familiarity can also be part of the context. PHP may not seem to be the best choice for building a command-line tool, but if it's a small tool used by a team of PHP developers, then it might be the best choice. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>career</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Earthly Stickers And Hacktoberfest</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Wed, 29 Sep 2021 16:13:03 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/earthly-stickers-and-hacktoberfest-1096</link>
      <guid>https://dev.to/adamgordonbell/earthly-stickers-and-hacktoberfest-1096</guid>
      <description>&lt;p&gt;Earthly is excited about Hacktoberfest and we are looking for contributors.&lt;/p&gt;

&lt;p&gt;Last year we saw lots of great contributions from the community and sent out a bunch of Earthly stickers to participants.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Hacktoberfest?
&lt;/h2&gt;

&lt;p&gt;The hosting provider DigitalOcean is once again organizing the &lt;a href="https://hacktoberfest.digitalocean.com/" rel="noopener noreferrer"&gt;Hacktoberfest&lt;/a&gt; to celebrate open source. Hacktoberfest invites you to submit pull requests to open-source projects. People who submit four or more pull requests, get a free t-shirt from DigitalOcean.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Earthly?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://earthly.dev/" rel="noopener noreferrer"&gt;Earthly&lt;/a&gt; is the command-line build tool you didn't know you needed. It combines the isolation of containers with great usability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sticker Rewards
&lt;/h2&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1329394070393925635-24" src="https://platform.twitter.com/embed/Tweet.html?id=1329394070393925635"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1329394070393925635-24');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1329394070393925635&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;In addition to this official event, Earthly would also like to kick in some rewards for people who contribute.&lt;/p&gt;

&lt;p&gt;Earthly is a free and open build tool.  It lets you build everything using containers.&lt;/p&gt;

&lt;p&gt;The first 40 participants who submit at least one pull request that is accepted &lt;a href="https://github.com/earthly/earthly" rel="noopener noreferrer"&gt;Earthly's GitHub Repository&lt;/a&gt; between October 1, 2021 and November 1, 2021, will get an earthly sticker.  We have labeled many issues &lt;a href="https://github.com/earthly/earthly/issues?q=is%3Aissue+is%3Aopen+label%3Ahacktoberfest" rel="noopener noreferrer"&gt;&lt;code&gt;hacktoberfest&lt;/code&gt;&lt;/a&gt; to get you started.&lt;/p&gt;

&lt;p&gt;You can also qualify for the sticker by adding a working Earthfile file to another open-source project, including your own personal project.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do I Claim my Earthly Stickers?
&lt;/h2&gt;

&lt;p&gt;Once your PR has been accepted please fill out this &lt;a href="https://forms.gle/DVkrWCu4yD2wMPmE8" rel="noopener noreferrer"&gt;form&lt;/a&gt;. All PRs that are contributing value will get an Earthly sticker!&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions / Help ?
&lt;/h2&gt;

&lt;p&gt;If you have any questions, or if you need any assistance, &lt;a href="https://earthly.dev/slack" rel="noopener noreferrer"&gt;join our slack channel&lt;/a&gt;. We are here to help!&lt;/p&gt;

&lt;p&gt;Questions about a specific issue or PR are best left on that issue or PR.&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>contributorswanted</category>
    </item>
    <item>
      <title>Markdown Linting </title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Thu, 19 Aug 2021 15:22:34 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/markdown-linting-5a3</link>
      <guid>https://dev.to/adamgordonbell/markdown-linting-5a3</guid>
      <description>&lt;p&gt;Many linting, code formatting, and static analysis tools exist for code. You can use &lt;code&gt;eslint&lt;/code&gt;, &lt;code&gt;gofmt&lt;/code&gt;, or many other static analysis tools, combined with a great continuous integration process, and ensure that your code stays in good shape. But what about markdown files and documentation? How do you ensure you aren't committing spelling and grammar mistakes? How do you ensure your files are valid markdown and that the language you are using is clear and correct? You can do this and more with a documentation linter.&lt;/p&gt;

&lt;p&gt;Many tools exist for finding problems in text files. You can use this list as a starting point for finding the markdown and prose linting tools that best fit your needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docs as Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The movement behind testing and linting prose is known as &lt;a href="https://www.writethedocs.org/guide/docs-as-code/" rel="noopener noreferrer"&gt;Docs as Code&lt;/a&gt;, and the &lt;a href="https://www.writethedocs.org/" rel="noopener noreferrer"&gt;Writing The Docs&lt;/a&gt; website is a great place to learn more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criteria
&lt;/h2&gt;

&lt;p&gt;For Ease of skimming, I'll rate each tool based on this criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Formatting:&lt;/strong&gt; The ability to find errors in the formatting of text files (markdown, txt, asciidoc).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spelling:&lt;/strong&gt; The ability to find spelling mistakes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grammar:&lt;/strong&gt; The ability to detect grammar errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarity:&lt;/strong&gt; The ability to suggest changes that can improve writing clarity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, I will rate tools based on their feature set:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remediation:&lt;/strong&gt; The ability to fix errors without manual intervention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customization:&lt;/strong&gt; How well the tool can be customized to fit your use case. If you can't exclude a rule or disable a warning, CI usage may be challenging. The most robust tools support custom rules and documentation style guides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrated Developer Environment (IDE) support:&lt;/strong&gt; Ability to use in common code editors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous Integration (CI) / Command Line Interface (CLI) Usage:&lt;/strong&gt; Ability to be used at the command line and in a continuous integration environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/DavidAnson/markdownlint" rel="noopener noreferrer"&gt;Markdown Lint&lt;/a&gt;
&lt;/h2&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fmarkdownlint4-800-99dd9fb9b.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fmarkdownlint4-800-99dd9fb9b.webp" alt="markdown lint GitHub Readme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;markdownlint&lt;/code&gt; is a node.js markdown linter that is easy to install and easy to customize. It is based on an earlier Ruby tool, also called &lt;a href="https://github.com/markdownlint/markdownlint" rel="noopener noreferrer"&gt;markdownlint&lt;/a&gt;. Both are great, but the Node.js tool is easy to install and easy to customize.  &lt;/p&gt;

&lt;p&gt;You can disable specific rules inline ( &lt;code&gt;&amp;lt;!-- markdownlint-disable-file MD001 --&amp;gt;&lt;/code&gt; ) and set up a per-project config in a &lt;code&gt;.markdownlintrc&lt;/code&gt; file. It also supports writing custom rules in JavaScript and can remediate many problems itself with the &lt;code&gt;fix&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;markdownlint --fix "./_posts/*.md"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It doesn't handle spelling, grammar, or sentence structure, but it can't be beaten for dealing with markdown structure and it has a great online &lt;a href="https://dlaa.me/markdownlint/" rel="noopener noreferrer"&gt;demo site&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Formatting: 5&lt;/li&gt;
&lt;li&gt;Spelling: 0&lt;/li&gt;
&lt;li&gt;Grammar: 0&lt;/li&gt;
&lt;li&gt;Clarity: 0&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ease of Use: 5&lt;/li&gt;
&lt;li&gt;Remediation: 5&lt;/li&gt;
&lt;li&gt;Customization: 5&lt;/li&gt;
&lt;li&gt;IDE support: 5&lt;/li&gt;
&lt;li&gt;CI / CLI Support: 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.npmjs.com/package/markdown-spellcheck" rel="noopener noreferrer"&gt;mdspell&lt;/a&gt;
&lt;/h2&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fmdspell1-800-4bbda79e3.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fmdspell1-800-4bbda79e3.webp" alt="mdspell readme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mdspell&lt;/code&gt; is a tool specifically for spelling checking markdown documents. Install it 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;npm i markdown-spellcheck -g    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run it on markdown files in an interactive mode that builds up a custom dictionary of exceptions. You can then use that list later in a continuous integration process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mdspell -n -a --en-us  ./blog/_posts/2021-02-11-mitmproxy.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downsides of &lt;code&gt;mdspell&lt;/code&gt; are that the dictionary will likely complain about lots of words that are quite common. It may take some time to build up a list of exceptions. As a shortcut, you might be able to find some more &lt;code&gt;.spelling&lt;/code&gt; files on GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Formatting: 0&lt;/li&gt;
&lt;li&gt;Spelling: 5&lt;/li&gt;
&lt;li&gt;Grammar: 0&lt;/li&gt;
&lt;li&gt;Clarity: 0&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ease of Use: 5&lt;/li&gt;
&lt;li&gt;Remediation: 5&lt;/li&gt;
&lt;li&gt;Customization: 5&lt;/li&gt;
&lt;li&gt;IDE support: 5&lt;/li&gt;
&lt;li&gt;CI / CLI Support: 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://alexjs.com/" rel="noopener noreferrer"&gt;alex&lt;/a&gt;
&lt;/h2&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Falex-800-446c930fd.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Falex-800-446c930fd.webp" alt="alex.js readme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alex&lt;/code&gt; does one thing: catches insensitive and inconsiderate writing. It supports markdown files, and works via command-line, and has various IDE integrations. The specificity of &lt;code&gt;alex&lt;/code&gt; is its strength. For my rubric, I am scoring it under clarity as catching insensitive writing certainly improves clarity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Formatting: 0&lt;/li&gt;
&lt;li&gt;Spelling: 0&lt;/li&gt;
&lt;li&gt;Grammar: 0&lt;/li&gt;
&lt;li&gt;Clarity: 3&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ease of Use: 5&lt;/li&gt;
&lt;li&gt;Remediation: 5&lt;/li&gt;
&lt;li&gt;Customization: 5&lt;/li&gt;
&lt;li&gt;IDE support: 5&lt;/li&gt;
&lt;li&gt;CI / CLI Support: 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/btford/write-good" rel="noopener noreferrer"&gt;&lt;code&gt;write-good&lt;/code&gt;&lt;/a&gt;
&lt;/h2&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fwrite-good-800-b8501ff39.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fwrite-good-800-b8501ff39.webp" alt="write good website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;write-good&lt;/code&gt; is designed for "developers who can't write good and wanna learn to do other stuff good too." The tool's focus is on improving the clarity of writing (and helping developers write well).&lt;/p&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g write-good
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ write-good ./blog/_posts/2021-02-11-mitmproxy.md
here are several ways to accomplish this.
                         ^^^^^^^^^^
"accomplish" is wordy or unneeded on line 305 at column 26
-------------
e-ca-certificates` is an excellent proof of concept, but if you want to run a do
                         ^^^^^^^^^
"excellent" is a weasel word on line 367 at column 84
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;write-good&lt;/code&gt; has many exciting suggestions. It will highlight passive voice, cliches, weak adverbs, and much more. Unfortunately, it's not easy to exclude items or configure rules. It might be helpful as a writing suggestion tool, but this lack of configurability means you will have difficulty using it in a continuous integration process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Formatting: 0&lt;/li&gt;
&lt;li&gt;Spelling: 0&lt;/li&gt;
&lt;li&gt;Grammar: 0&lt;/li&gt;
&lt;li&gt;Clarity: 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ease of Use: 5&lt;/li&gt;
&lt;li&gt;Remediation: 0&lt;/li&gt;
&lt;li&gt;Customization: 1&lt;/li&gt;
&lt;li&gt;IDE support: 2&lt;/li&gt;
&lt;li&gt;CI / CLI Support: 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://textlint.github.io/" rel="noopener noreferrer"&gt;textlint&lt;/a&gt;
&lt;/h2&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Ftextlint-800-6eb4f859e.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Ftextlint-800-6eb4f859e.webp" alt="text lint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;textlint&lt;/code&gt; is a pluggable linting tool that supports markdown, plain text, and HTML. The plug-in architecture means that it can offer the features of some of the previous items by wrapping them up as a plug-in. It has a &lt;a href="https://github.com/textlint/textlint/wiki/Collection-of-textlint-rule#rules-english" rel="noopener noreferrer"&gt;plug-in&lt;/a&gt; for &lt;code&gt;alex&lt;/code&gt;, &lt;code&gt;write-good&lt;/code&gt;, and for many spell checkers and grammar checkers. The downside of this flexibility is that it is a bit harder to set up and configure: you have to install each plug-in separately.&lt;/p&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install textlint --global
# install each plugin
$ npm install --global textlint-rule-no-todo
....

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

&lt;/div&gt;



&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;textlint "docs/**"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;textlint&lt;/code&gt; is configurable via an &lt;code&gt;textlintrc&lt;/code&gt; and has inline exclude rules ( &lt;code&gt;&amp;lt;!-- textlint-disable ruleA,ruleB --&amp;gt;&lt;/code&gt; ) -- which may make it a possible way to use &lt;code&gt;write-good&lt;/code&gt; or other tools that lack this functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Formatting: 0&lt;/li&gt;
&lt;li&gt;Spelling: 3&lt;/li&gt;
&lt;li&gt;Grammar: 3&lt;/li&gt;
&lt;li&gt;Clarity: 4&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ease of Use: 1&lt;/li&gt;
&lt;li&gt;Remediation: 3&lt;/li&gt;
&lt;li&gt;Customization: 4&lt;/li&gt;
&lt;li&gt;IDE support: 5&lt;/li&gt;
&lt;li&gt;CI / CLI Support: 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="http://proselint.com/" rel="noopener noreferrer"&gt;proselint&lt;/a&gt;
&lt;/h2&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fproselint-800-b152ebcc0.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fproselint-800-b152ebcc0.webp" alt="prose lint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;proselint&lt;/code&gt; goes deep on writing clarity improvements in the same way the &lt;code&gt;alex&lt;/code&gt; goes deep on inclusive writing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;proselint&lt;/code&gt; places the world's greatest writers and editors by your side, where they whisper suggestions on how to improve your prose. You'll be guided by advice inspired by Bryan Garner, David Foster Wallace, Chuck Palahniuk, Steve Pinker, Mary Norris, Mark Twain, Elmore Leonard, George Orwell, Matthew Butterick, William Strunk, E. B. White, Philip Corbett, Ernest Gowers, and the editorial staff of the world's finest literary magazines and newspapers, among others. Our goal is to aggregate knowledge about best practices in writing and to make that knowledge immediately accessible to all authors in the form of a linter for prose.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of the writing advice included is great:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "The very first thing you'll see at the top of every (well-written) bash script " | proselint
&amp;lt;stdin&amp;gt;:1:5: weasel_words.very Substitute 'damn' every time you're inclined to write 'very'; your editor will delete it and the writing will be just as it should be.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "Thankfully, not all the advice I received was bad. " | proselint
&amp;lt;stdin&amp;gt;:1:2: skunked_terms.misc 'Thankfully,' is a bit of a skunked term — impossible to use without issue. Find some other way to say it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "it is worth noting that both for CI and CD, the operating principles and coding philosophy are equally as important as the technical aspect of the implementation." | proselint
&amp;lt;stdin&amp;gt;:1:96: after_the_deadline.redundancy Redundancy. Use 'as' instead of 'equally as'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one is awesome considering the context of the &lt;a href="https://earthly.dev/blog/thought-leaders/" rel="noopener noreferrer"&gt;original article&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "thought leaders" | proselint
&amp;lt;stdin&amp;gt;:1:2: cliches.garner 'thought leaders' is cliché.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; echo "One elephant in the room with ngrok is" | proselint
&amp;lt;stdin&amp;gt;:1:5: corporate_speak.misc Minimize your use of corporate catchphrases like this one.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Learning from all the best writers is a very lofty objective, and &lt;code&gt;proselint&lt;/code&gt; has accumulated some valuable rules, but it falls short of its goal of collecting all the worlds writing advice in a parsable form. Ignoring and excluding rules are also not fully supported.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Formatting: 0&lt;/li&gt;
&lt;li&gt;Spelling: 0&lt;/li&gt;
&lt;li&gt;Grammar: 0&lt;/li&gt;
&lt;li&gt;Clarity: 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ease of Use: 5&lt;/li&gt;
&lt;li&gt;Remediation: 0&lt;/li&gt;
&lt;li&gt;Customization: 0&lt;/li&gt;
&lt;li&gt;IDE support: 5&lt;/li&gt;
&lt;li&gt;CI / CLI Support: 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/errata-ai/vale" rel="noopener noreferrer"&gt;Vale&lt;/a&gt;
&lt;/h2&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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fvale-800-337217691.webp" 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%2Fearthly.dev%2Fblog%2Fgenerated%2Fassets%2Fimages%2Fmarkdown-lint%2Fvale-800-337217691.webp" alt="Vale homepage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vale, created by Joseph Kato, supports spelling, grammar, and clarity checks. It is extendable using a YAML rule format and is designed around the idea of a style guide -- a specific house style that you put together and vale enforces. It has an implementation of most &lt;code&gt;proselint&lt;/code&gt; as a style guide, most of &lt;code&gt;write-good&lt;/code&gt;, as well as the &lt;a href="https://docs.microsoft.com/en-us/style-guide/welcome/" rel="noopener noreferrer"&gt;Microsoft Writing Style Guide&lt;/a&gt; and the Google&lt;br&gt;
&lt;a href="https://developers.google.com/style/" rel="noopener noreferrer"&gt;developer documentation style guide&lt;/a&gt;. Vale is targeted directly at the Docs-as-Code community and documentation teams, who take the writing style of documents very seriously.&lt;/p&gt;

&lt;p&gt;Vale is fast and configurable but not necessarily easy to get started with. Initially, I couldn't get it to find any problems until I realized that it needs a config file to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MinAlertLevel = suggestion

[*]
BasedOnStyles = Vale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
.vale.ini





&lt;p&gt;Additionally, to use it effectively, you will need to copy an existing style-guide into your repository. Separating the styles from the tool is Vale's biggest strength. It's also could be a weakness as the rules you build up are specific to your repository. It is easy to write and customize rules but hard to share them back as they need to live in your source code repository.&lt;/p&gt;

&lt;p&gt;Besides the official Vale style guides &lt;a href="https://buildkite.com/blog/linting-the-buildkite-docs" rel="noopener noreferrer"&gt;Buildkite&lt;/a&gt;, &lt;a href="https://github.com/linode/docs/tree/develop/ci/vale/styles" rel="noopener noreferrer"&gt;Linode&lt;/a&gt;, and &lt;a href="https://github.com/testthedocs/vale-styles" rel="noopener noreferrer"&gt;Write The Docs&lt;/a&gt; have rules online that you can copy into your repo or use as inspiration for your own rules.&lt;/p&gt;

&lt;p&gt;If you are taking linting documentation seriously and can take the time to set up a style that works for you, then Vale is the way to go. The rules of most other tools can be implemented inside value, and many already are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Formatting: 2&lt;/li&gt;
&lt;li&gt;Spelling: 5&lt;/li&gt;
&lt;li&gt;Grammar: 5&lt;/li&gt;
&lt;li&gt;Clarity: 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ease of Use: 1&lt;/li&gt;
&lt;li&gt;Remediation: 0&lt;/li&gt;
&lt;li&gt;Customization: 5&lt;/li&gt;
&lt;li&gt;IDE support: 5&lt;/li&gt;
&lt;li&gt;CI / CLI Support: 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vale Styles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/topics/vale-linter-style" rel="noopener noreferrer"&gt;Official Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/search?q=topic%3Avale-linter-style+org%3Atestthedocs+fork%3Atrue" rel="noopener noreferrer"&gt;Write The Docs Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/testthedocs/Openly" rel="noopener noreferrer"&gt;Grammarly Clone in Vale&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Many tools exist for testing and linting English prose. You can start as simply as just spelling checking your readme before you commit it or go as complex as a full style guide running on every change to your software documentation.&lt;/p&gt;

&lt;p&gt;If you are willing to invest the time, then &lt;code&gt;Vale&lt;/code&gt;, with its flexible rules, is the clear leader. Combining &lt;code&gt;Vale&lt;/code&gt; with &lt;code&gt;markdownlint&lt;/code&gt; and running both in a continuous integration build should ensure that documents are spelling correctly, grammatically correct, and written in a properly formatted and exclusive way.&lt;/p&gt;

&lt;p&gt;If you're looking for a more accessible place to start or don't need the grammar and clarity suggestions, then &lt;code&gt;mdspell&lt;/code&gt; and &lt;code&gt;markdownlint&lt;/code&gt; make a great combination.&lt;/p&gt;

&lt;p&gt;Once you have decided on what tools will work best for you, make sure you find a way to automate their usage. This blog uses &lt;code&gt;Vale&lt;/code&gt; and &lt;code&gt;markdownlint&lt;/code&gt; inside an &lt;a href="https://earthly.dev/" rel="noopener noreferrer"&gt;Earthfile&lt;/a&gt; that is run every commit. This helps us prevent mistakes from getting into the blog.&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>writing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Python Concatenate Lists</title>
      <dc:creator>Adam Gordon Bell</dc:creator>
      <pubDate>Mon, 16 Aug 2021 19:35:56 +0000</pubDate>
      <link>https://dev.to/adamgordonbell/python-concatenate-lists-ocf</link>
      <guid>https://dev.to/adamgordonbell/python-concatenate-lists-ocf</guid>
      <description>&lt;h2&gt;
  
  
  Concatenate Two Lists in Python
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; You have two lists and you'd like to join them into a new list.&lt;br&gt;
Solution:&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;Python&lt;/span&gt; &lt;span class="mf"&gt;3.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'four'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'five'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;📢 TLDR: Use &lt;code&gt;+&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In almost all simple situations, &lt;strong&gt;using &lt;code&gt;list1 + list2&lt;/code&gt; is the way you want to concatenate lists&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The edge cases below are better in some situations, but &lt;code&gt;+&lt;/code&gt; is generally the best choice. All options covered work in Python 2.3, Python 2.7, and all versions of Python 3&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combine Lists in Place in Python
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; You have a huge list, and you want to add a smaller list on the end while minimizing memory usage.&lt;/p&gt;

&lt;p&gt;In this case, it may be best to append to the existing list, reusing it instead of recreating a new list.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;longlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;shortlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with any optimization, you should verify that this reduces memory thrash in your specific case and stick to the simple idiomatic &lt;code&gt;x + y&lt;/code&gt; otherwise.&lt;/p&gt;

&lt;p&gt;Let's use the &lt;a href="https://docs.python.org/3/library/timeit.html"&gt;&lt;code&gt;timeit&lt;/code&gt;&lt;/a&gt; module to check some performance numbers.&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;# Performance Check
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;x = ["one","two","three"] * 1000 
y = ["four","five","six"]
"""&lt;/span&gt;
&lt;span class="c1"&gt;# x + y with large x
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'x + y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;3.6260274310000113&lt;/span&gt;
&lt;span class="c1"&gt;# x.extend(y) with large x
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'x.extend(y)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.06857255800002804&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, where x is 3000 elements, extend is around 50x faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❗ Concatenating Lists With Large Elements is Fine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the elements in your list are huge (million character strings), but the list size is less than a thousand elements, the previous solution &lt;code&gt;x + y&lt;/code&gt; will work just fine. This is because Python stores references to the values in the list, not the values themselves. Thus, the element size makes no difference to the runtime complexity.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"two"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"five"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#This is fine
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#Performance Testing (extend is slower for large elements)
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;x = ["one" * 1000, "two" * 1000, "three" * 1000]
y = ["four" * 1000, "five" * 1000]
"""&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'x + y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.05397573999994165&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'x.extend(y)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.06511967799997365&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, &lt;code&gt;extend&lt;/code&gt; does not have an advantage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid Chain From &lt;code&gt;itertools&lt;/code&gt; For Two Lists
&lt;/h3&gt;

&lt;p&gt;It is possible to use &lt;code&gt;chain&lt;/code&gt; from &lt;code&gt;itertools&lt;/code&gt; to create an iterable of two lists.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;longlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;shortlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shortlist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'one'&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="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&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 check the performance of using chain:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;from itertools import chain
x = ["one","two","three"] * 1000 
y = ["four","five","six"]
"""&lt;/span&gt;
&lt;span class="c1"&gt;# x + y with large x
# x.extend(y) with large x
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'x.extend(y)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.06857255800002804&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'list(chain(x, y))'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;16.810488051999982&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;chain&lt;/code&gt; with two lists is slower in all cases tested, and &lt;code&gt;x + y&lt;/code&gt; is easier to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining N Lists in Python
&lt;/h2&gt;

&lt;p&gt;If you need to add three or even ten lists together and the lists are statically known, then &lt;code&gt;+&lt;/code&gt; for concatenate works great.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;three&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;three&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Flatten a List of Lists in Python
&lt;/h2&gt;

&lt;p&gt;However, if the number of lists is dynamic and unknown until runtime, &lt;code&gt;chain&lt;/code&gt; from &lt;code&gt;itertools&lt;/code&gt; becomes a great option. Chain takes a list of lists and flattens it into a single list.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;],[]]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;
&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'four'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'five'&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_iterable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'three'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'four'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'five'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;chain&lt;/code&gt; can take anything iterable, making it an excellent choice for combining lists, dictionaries, and other iterable structures.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance of Flattening a List of Lists
&lt;/h3&gt;

&lt;p&gt;Performance doesn't always matter, but readability always does, and the chain method is a straightforward way to combine lists of lists. That said, let's put readability aside for a moment and try to find the fastest way to flatten lists.&lt;/p&gt;

&lt;p&gt;One option is iterating ourselves:&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;nestedlist&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;l&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;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nestedlist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check its performance vs chain:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;from itertools import chain
l = [["one","two", "three"],["four","five"],[]] * 99
"""&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Add Nested Lists using chain.from_iterable
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'list(chain.from_iterable(l))'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;1.0384087909997106&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;### Add using our own iteration
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;result = []
for nestedlist in l:
    result.extend(nestedlist)  
"""&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;1.8619721710001613&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This shows that &lt;code&gt;chain.from_iterable&lt;/code&gt; is faster than extend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flattening and Merging Lists With One Big List
&lt;/h2&gt;

&lt;p&gt;What about adding a list of lists to an existing and large list? We saw that using extend can be faster with two lists when one is significantly longer than the other so let's test the performance of &lt;code&gt;extend&lt;/code&gt; with N lists.&lt;/p&gt;

&lt;p&gt;First, we use our standard &lt;code&gt;chain.from_iterable&lt;/code&gt;.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Method 1 - chain.from_iterable 
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;longlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nestedlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;longlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;],[]]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_iterable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nestedlist&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then test its performance:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;from itertools import chain
longlist = ["one","two", "three"] * 1000;
combinedlist = [longlist, ["one","two", "three"],["four","five"],[]]
"""&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'list(chain.from_iterable(combinedlist))'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;1.8676087710009597&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's try concatenating by adding everything onto the long list:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Method 2 - extend
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;longlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;nestedlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"three"&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="s"&gt;"four"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"five"&lt;/span&gt;&lt;span class="p"&gt;],[]]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nestedlist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;     &lt;span class="n"&gt;longlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Performance Test:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;from itertools import chain
longlist = ["one","two", "three"] * 1000;
nestedlist = [["one","two", "three"],["four","five"],[]]
"""&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;for item in nestedlist:
   longlist.extend(item)
   """&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="mf"&gt;0.02403609199973289&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There we go, &lt;code&gt;extend&lt;/code&gt; is much faster when flattening lists or concatenating many lists with one long list. If you encounter this, using &lt;code&gt;extend&lt;/code&gt; to add the smaller lists to the long list can decrease the work that has to be done and increase performance.&lt;/p&gt;

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

&lt;p&gt;These are the main variants of combining lists in python. Use this table to guide you in the future.&lt;/p&gt;

&lt;p&gt;Also, if you are looking for a nice way to standardize the processes around your python projects -- running tests, installing dependencies, and linting code -- take a look at Earthly for &lt;a href="https://earthly.dev/"&gt;Repeatable Builds&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Performance Optimization&lt;sup id="fnref2"&gt;2&lt;/sup&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2 lists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x + y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 large list, 1 small list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x.extend(y)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Known number of N lists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x + y + z&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown number of N lists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list(chain.from_iterable(l))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List of Lists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list(chain.from_iterable(l))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 large list, many small lists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;for l1 in l: x.extend(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;I did all the performance testing using Python 3.9.5 on MacOS BigSur. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;If you don't have a performance bottleneck, clarity trumps performance, and you should ignore the performance suggestions. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
