<?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: Liz Acosta</title>
    <description>The latest articles on DEV Community by Liz Acosta (@lizzzzz).</description>
    <link>https://dev.to/lizzzzz</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%2F520299%2F38de40f4-43b2-4a0d-a7cc-2fa6494ac4b3.png</url>
      <title>DEV Community: Liz Acosta</title>
      <link>https://dev.to/lizzzzz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lizzzzz"/>
    <language>en</language>
    <item>
      <title>The Vonage Dev Discussion: Builder pattern</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Tue, 07 Apr 2026 16:48:06 +0000</pubDate>
      <link>https://dev.to/vonagedev/the-vonage-dev-discussion-builder-pattern-1f45</link>
      <guid>https://dev.to/vonagedev/the-vonage-dev-discussion-builder-pattern-1f45</guid>
      <description>&lt;p&gt;Have you ever heard of the builder pattern? 🧰🏘️&lt;/p&gt;

&lt;p&gt;In software development, the builder pattern is a &lt;a href="https://vonage.dev/4c8VdpU" rel="noopener noreferrer"&gt;creational design pattern&lt;/a&gt; that lets you construct complex objects step by step. It is especially helpful when you have complex classes of objects that can be extended in many different ways.&lt;/p&gt;

&lt;p&gt;In this video, our developer advocates Guillaume Faas and Jeremy Foster discuss the builder pattern and how it is implemented in the &lt;a href="https://github.com/vonage/vonage-dotnet-sdk" rel="noopener noreferrer"&gt;Vonage .NET SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/h31xOHKJ7D8"&gt;
  &lt;/iframe&gt;
&lt;br&gt;
The builder pattern &lt;a href="https://dev.to/tr00d/why-i-use-and-abuse-the-builder-pattern-3lbm"&gt;sparks debate in the dev community&lt;/a&gt;. Some swear by it, others see it as over-engineering. We want to know: &lt;strong&gt;What's your take?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you use the builder pattern?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you use it in your own work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How would you explain it to someone?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5gw4srnoxebb0ulget7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5gw4srnoxebb0ulget7.png" alt="A graphic of the Vonage mascot with the text: Do you use the builder pattern? How do you use it in your own work? How would you explain it to someone?" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Share your experience and knowledge of the builder pattern in the comments below! &lt;/p&gt;

&lt;p&gt;⚒️👇&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>architecture</category>
      <category>discuss</category>
    </item>
    <item>
      <title>The Vonage Dev Discussion: Open source</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Wed, 25 Mar 2026 16:59:12 +0000</pubDate>
      <link>https://dev.to/vonagedev/the-vonage-dev-discussion-open-source-ipb</link>
      <guid>https://dev.to/vonagedev/the-vonage-dev-discussion-open-source-ipb</guid>
      <description>&lt;p&gt;At &lt;a href="https://vonage.dev/41y34Zf" rel="noopener noreferrer"&gt;Women+ in Open Source Day&lt;/a&gt;, I got to try my hand at making a contribution to &lt;a href="https://vonage.dev/4uOwPmf" rel="noopener noreferrer"&gt;Apache Spark&lt;/a&gt;. While I didn’t manage to clone and build the project within the allotted time, I did get to speak with one of the project’s key contributors, &lt;a href="https://vonage.dev/4rQF26G" rel="noopener noreferrer"&gt;Holden Karau&lt;/a&gt;, and was inspired by her encouragement for all sorts of people to get involved.&lt;/p&gt;

&lt;p&gt;Contributing to open source can be intimidating! And in the age of AI, contributors from diverse perspectives are even more important.&lt;/p&gt;

&lt;p&gt;So for our second ever Dev Discussion, we want to know: &lt;strong&gt;What is your advice for getting started contributing to open source projects?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9i3f701wd102z9xapcdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9i3f701wd102z9xapcdx.png" alt="A graphic of a robot with a speech bubble that reads, " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Share your advice in the comments below along with links to some of your favorite open source projects! 🖥️ 🎉👇&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>opensource</category>
      <category>womenintech</category>
    </item>
    <item>
      <title>The Vonage Dev Discussion: Music</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Tue, 17 Feb 2026 20:50:55 +0000</pubDate>
      <link>https://dev.to/vonagedev/the-vonage-dev-discussion-4h52</link>
      <guid>https://dev.to/vonagedev/the-vonage-dev-discussion-4h52</guid>
      <description>&lt;p&gt;Hey developers! We want to hear from you for a series we’re calling the &lt;strong&gt;Dev Discussion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What is the Dev Discussion? We want it to be a space where we can take a break and talk about the human side of software development.&lt;/p&gt;

&lt;p&gt;Our first Dev Discussion topic is about &lt;strong&gt;music&lt;/strong&gt; 🎶&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmfl6w1ljwabvanxnoas.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmfl6w1ljwabvanxnoas.jpg" alt="A stylized illustration of a robot with the text: " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Speaking of music and tech, you’ve probably heard &lt;a href="https://www.qobuz.com/ie-en/album/opus-no-1-tim-carleton-darrick-deel/vxcde5txl6b9a" rel="noopener noreferrer"&gt;this track&lt;/a&gt; while waiting on hold ☎️🎵&lt;/p&gt;

&lt;p&gt;Did you know it was composed in 1989 by Darrick Deel and Tim Carleton, a &lt;a href="https://www.thisamericanlife.org/516/stuck-in-the-middle-2014/act-one-0" rel="noopener noreferrer"&gt;self described&lt;/a&gt;, “Yanni-loving computer nerd” who was just “messing around with a drum machine and a synthesizer” in his parents' garage in California?&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://vonage.dev/46NixYz" rel="noopener noreferrer"&gt;make it your hold music too&lt;/a&gt;! &lt;/p&gt;

&lt;p&gt;Besides cool drum machines and chill synthesizers, we want to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is currently playing in your headphones?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does what you listen to change according to what you are working on?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you ever work in complete silence?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Feel free to drop those playlists in the comments below 🎶 👇&lt;/p&gt;

</description>
      <category>music</category>
      <category>discuss</category>
      <category>api</category>
    </item>
    <item>
      <title>Don’t Make Assumptions About Assertions: Even with AI you still have to write your unit tests</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Sun, 26 Oct 2025 23:40:26 +0000</pubDate>
      <link>https://dev.to/lizzzzz/dont-make-assumptions-about-assertions-even-with-ai-you-still-have-to-write-your-unit-tests-4053</link>
      <guid>https://dev.to/lizzzzz/dont-make-assumptions-about-assertions-even-with-ai-you-still-have-to-write-your-unit-tests-4053</guid>
      <description>&lt;p&gt;This blog post talks about why making assumptions about assertions makes not one “ass,” but two – which is especially true in this new age of AI. You’re not going to like this, but guess what? You still have to write your unit tests. (Sorry!)&lt;/p&gt;

&lt;p&gt;But that’s not necessarily a bad thing! If this blog post doesn’t convince you of that, then I at least hope to invite you to reconsider your feelings about unit testing by taking a closer look at the most important component of the AAA test pattern: the assertion.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to get the most out of this blog post
&lt;/h2&gt;

&lt;p&gt;This blog post is designed to accommodate many different learning styles so you can choose your own adventure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://utm.guru/ujh5X" rel="noopener noreferrer"&gt;&lt;strong&gt;Jump straight to the code&lt;/strong&gt;&lt;/a&gt;: Use the README to get it up and running locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The key to human productivity or a “disconcerting trend”?&lt;/strong&gt;: A tale of two reports and their findings about AI, developer productivity, and code churn. (And why &lt;em&gt;human generated&lt;/em&gt; tests are important.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A quick review of unit tests&lt;/strong&gt;: In case you need it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing unit tests doesn’t have to be horrible&lt;/strong&gt;: How we can use the assert methods included with Python’s unittest framework to make writing tests less awful.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using more precise assert methods to write better unit tests&lt;/strong&gt;: A walk-through of some code in which we’ll explore the effects of using different kinds of assert methods so you can experience that “&lt;em&gt;Aha!&lt;/em&gt;” moment first-hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resources and references&lt;/strong&gt;: Links to more information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="ai-generated-code"&gt;AI generated code: The key to human productivity or a “disconcerting trend”?&lt;/h2&gt;

&lt;p&gt;In June 2022, &lt;strong&gt;GitHub Copilot&lt;/strong&gt; &lt;a href="https://utm.guru/ujh57" rel="noopener noreferrer"&gt;went GA&lt;/a&gt;. A year later, in June 2023, a GitHub blog post proclaimed that “&lt;a href="https://utm.guru/ujh56" rel="noopener noreferrer"&gt;AI developer productivity benefits could &lt;strong&gt;boost global GDP by over $1.5 trillion&lt;/strong&gt;&lt;/a&gt;,” citing a February 2023 research paper about an experiment in which a group of programmers with “&lt;a href="https://utm.guru/ujh55" rel="noopener noreferrer"&gt;access to GitHub Copilot was able to complete the task [of implementing an HTTP server in JavaScript as quickly as possible] 55.8% faster&lt;/a&gt;” than a control group without help from AI.&lt;/p&gt;

&lt;p&gt;It is worth noting that of the paper’s four authors, three of them are associated with GitHub or its parent company Microsoft.&lt;/p&gt;

&lt;p&gt;In response to this “promise to increase human productivity,” &lt;strong&gt;GitClear&lt;/strong&gt;, a software engineering intelligence platform, asked the question, “&lt;strong&gt;How does this profusion of LLM generated code affect quality and maintainability?&lt;/strong&gt;” To answer that question, GitClear collected 153 million changed lines of code authored between January 2020 and December 2023, and evaluated the data for differences in code quality. At the time, it was the largest known database of highly structured code change data used for this purpose, and included repos owned by Google, Microsoft, Meta, and enterprise C-Corps.&lt;/p&gt;

&lt;p&gt;What GitClear found was “&lt;a href="https://utm.guru/ujh54" rel="noopener noreferrer"&gt;disconcerting trends for maintainability&lt;/a&gt;.” The authors of the report drew this conclusion from two factors they observed notable changes in following the general availability of GitHub Copilot:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code churn&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“Code churn” refers to the percentage of lines that are reverted or updated less than two weeks after being authored. In other words, this is code that was probably authored in one sprint and then reverted or updated in the next sprint because the changes were either incomplete or erroneous. The report noted that code churn increased around the same time as the release of GitHub Copilot and projected that it would continue to do so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Added and copy-pasted code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This refers to code that is newly authored instead of code that is “updated,” “deleted,” or “moved.” The report goes on to explain that an increase in adding new code instead of refactoring existing code “resembles an itinerant contributor, prone to violate the DRY-ness of the repos visited.” (DRY is an acronym for the “Don’t Repeat Yourself” &lt;a href="https://utm.guru/ujh51" rel="noopener noreferrer"&gt;tenet of software engineering&lt;/a&gt;.) And who might that “itinerant contributor” be? Yup – AI.&lt;/p&gt;

&lt;p&gt;(No, AI didn’t write that. I’ve always been a prolific em-dash user so AI probably stole its usage &lt;em&gt;from&lt;/em&gt; &lt;em&gt;me&lt;/em&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  So what can we, as developers, take away from this?
&lt;/h3&gt;

&lt;p&gt;While both the GitHub and the GitClear reports don’t try to hide their bias or content marketing intentions, we can still glean some useful insights from them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re probably going to encounter AI generated code – whether you’re the one adding it or you’re reading/reviewing AI generated code that someone else added.&lt;/li&gt;
&lt;li&gt;I’m sorry, but &lt;strong&gt;you still have to write your unit tests&lt;/strong&gt;. Now more so than ever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;… but that’s not a bad thing. (Stay with me here.)&lt;/p&gt;

&lt;h2 id="unit-test-review"&gt;A quick review of unit tests&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is unit testing?
&lt;/h3&gt;

&lt;p&gt;Unit testing is the process of &lt;strong&gt;evaluating and verifying&lt;/strong&gt; that the &lt;strong&gt;smallest functional units of code&lt;/strong&gt; do what they are supposed to do &lt;strong&gt;individually and independently&lt;/strong&gt;. So if we have a web application that allows a user to view collections of Pokemon according to ability, color, or type, we might have a Pokemon object with methods that perform tasks such as calling a Pokemon API endpoint, processing whatever response we get back from that endpoint, and then transforming that processed response into something we can display in a browser for the user. Our unit tests would act on each method individually and independently to verify that each method performs as expected.&lt;/p&gt;

&lt;p&gt;The idea is that if each “ingredient” of a whole application is correct, then we can assume the end result will turn out the way we want. We can assume that if we have the right kind of tomato sauce, crust, and toppings, our pizza will be edible &lt;em&gt;and&lt;/em&gt; delicious.&lt;/p&gt;

&lt;p&gt;Unit tests are just one kind of software testing. There are lots of different types of tests that try to answer different types of questions such as, “Do all the different parts of this system actually work together?” and, “What happens if I throw in this totally wild edge case – will my system survive?”&lt;/p&gt;

&lt;p&gt;(This is where I trot out my software testing alignment chart because it’s probably one of the most clever things I’ve ever created and people seem to really like it!)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq2v4r68ccej73s7c03u.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq2v4r68ccej73s7c03u.webp" alt="A Dungeons and Dragons style alignment chart that lists different kinds of software testing and where they fall on the spectrum of good to evil and lawful to chaotic." width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The benefits of unit tests
&lt;/h3&gt;

&lt;p&gt;The benefits of unit tests include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preventing bugs&lt;/li&gt;
&lt;li&gt;Accelerating development&lt;/li&gt;
&lt;li&gt;Saving money&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the most &lt;strong&gt;compelling&lt;/strong&gt; &lt;strong&gt;benefit&lt;/strong&gt; of unit tests is they help us become &lt;strong&gt;better engineers&lt;/strong&gt;. Unit tests force us to ask ourselves, “What actually &lt;em&gt;is&lt;/em&gt; the expected behavior of this method?” In the best case scenario, unit tests reveal code smells or redundancy or unnecessary complexity that motivate us to &lt;strong&gt;refactor&lt;/strong&gt; the code under test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkktkhu6u6e33pnbbngfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkktkhu6u6e33pnbbngfu.png" alt="A LinkedIn post that says: I do not understand the average developer's revulsion toward refactoring. Refactoring has been my favorite part of being a software engineer because You learn about software architecture though re-structuring, You learn about new libraries to solve problems you are facing, You learn best practices studying how others solved your problem, You learn how to document and make your code readable, You learn to take pride in your work. I am obsessed with refactoring because when you're refactoring, you're literally learning how to write better code." width="800" height="770"&gt;&lt;/a&gt;&lt;/p&gt;
A &lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7384647063281651712" rel="noopener noreferrer"&gt;LinkedIn post&lt;/a&gt; in praise of refactoring and why.



&lt;h3&gt;
  
  
  Unit tests and AI
&lt;/h3&gt;

&lt;p&gt;“But Liz!” you say, “Writing unit tests is &lt;em&gt;so tedious and boring!&lt;/em&gt; Won’t I be more productive if I get AI to write them?”&lt;/p&gt;

&lt;p&gt;Maybe.&lt;/p&gt;

&lt;p&gt;After all, if these LLMs are trained on millions of lines of code and all of the internet, isn’t using AI to write unit tests kind of exactly like copying and pasting a solution from Stack Overflow? That time-honored tradition of software engineering?&lt;/p&gt;

&lt;p&gt;If someone else has already figured it out, why not reuse their solution? Is that not in alignment with the DRY principle?&lt;/p&gt;

&lt;p&gt;What could go wrong?&lt;/p&gt;

&lt;p&gt;To answer that, here’s an excerpt from the GitHub paper on AI powered productivity mentioned above:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We included a test suite in the repository, comprising twelve checks for submission correctness. If a submission passes, all twelve tests we counted are successfully completed. Participants could see the tests but were unable to alter them.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to ensure that both the AI enabled and control groups of programmers tasked with spinning up a server did so correctly, the &lt;strong&gt;&lt;em&gt;tests were written first&lt;/em&gt;&lt;/strong&gt;. Whether the code was human or AI generated, it was &lt;strong&gt;&lt;em&gt;verified with tests provided by the researchers&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And anyway, would you really trust an LLM trained on the tests most developers write?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flw0qrpjzmolnxuwp7auk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flw0qrpjzmolnxuwp7auk.png" alt="A LinkedIn post that says: A cruel irony of coding agents is that everyone who blew off automated testing for the past 20 years is now telling the AI to do TDD all the time. But because LLMs were trained on decades of their shitty tests, the agents are also terrible at testing." width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;
A &lt;a href="https://www.linkedin.com/posts/searls_a-cruel-irony-of-coding-agents-is-that-everyone-activity-7383097357816602624-cLY5" rel="noopener noreferrer"&gt;LinkedIn post&lt;/a&gt; describing why LLMs are bad at writing tests.



&lt;h2 id="not-horrible"&gt;Writing unit tests doesn’t have to be horrible&lt;/h2&gt;

&lt;p&gt;Personally, I would love to one day achieve the disciplined zen of &lt;strong&gt;&lt;a href="https://utm.guru/ujh5Y" rel="noopener noreferrer"&gt;test driven development&lt;/a&gt;&lt;/strong&gt;, but jumping right into the application code is just so much more seductive. It’s like eating dessert first, and while eating dessert first isn’t necessarily “bad” (we’re adults who can make our own decisions), it’s probably not great for us nutritionally in the long run. So how can we write unit tests in a way that is efficient and optimized? Unit tests that are &lt;strong&gt;modular&lt;/strong&gt; and &lt;strong&gt;maintainable&lt;/strong&gt; and leverage all of the tools in our toolkit?&lt;/p&gt;

&lt;h3&gt;
  
  
  The AAAs of testing
&lt;/h3&gt;

&lt;p&gt;Typically, a test follows this pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Arrange&lt;/strong&gt;: Set up the test environment. This can include fixtures, mocks, or context managers – whatever is needed to execute the code under test. When it comes to unit tests, the test environments for each test should be isolated from each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Act&lt;/strong&gt;: Execute the code under test. If it’s an end-to-end test, this might mean kicking off a workflow that includes multiple services and dependencies. In a unit test, however, this should be a single method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assert&lt;/strong&gt;: Verify the results. Compare the expected result with the test results – did the code do what you want it to do? This is the most important part of the test and in unit tests, it is (usually) best practice to have one precise assertion per test. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keeping this pattern in mind can help make it easier to write unit tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t make assumptions about assertions
&lt;/h3&gt;

&lt;p&gt;When you make assumptions about assertions, you end up with not &lt;em&gt;one&lt;/em&gt; “ass,” but &lt;em&gt;two&lt;/em&gt;. Just because you have 100% test coverage and everything is passing, it doesn’t mean your tests are actually &lt;em&gt;meaningful&lt;/em&gt; or – and here’s the “galaxy brain” revelation for you – &lt;em&gt;maintainable&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In Python’s untittest specifically, assert methods come included with the &lt;code&gt;TestCase&lt;/code&gt; class. &lt;a href="https://utm.guru/ujh52" rel="noopener noreferrer"&gt;These methods check for and report failures&lt;/a&gt;. You are probably familiar with the tried and true &lt;code&gt;assertEqual&lt;/code&gt; method, in which one argument is compared with another, and if the two do not match, result in a test failure … but did you know that there are &lt;em&gt;so many more specific and precise assertions&lt;/em&gt; available to you? All out of the box?&lt;/p&gt;

&lt;p&gt;Take a look at these!&lt;/p&gt;

&lt;p&gt;Most common assert methods:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;Method&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;Checks that ...&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a == b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a != b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertTrue" rel="noopener noreferrer"&gt;&lt;span&gt;assertTrue(x)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;bool(x) is True&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertFalse" rel="noopener noreferrer"&gt;&lt;span&gt;assertFalse(x)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;bool(x) is False&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertIs" rel="noopener noreferrer"&gt;&lt;span&gt;assertIs(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a is b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertIsNot" rel="noopener noreferrer"&gt;&lt;span&gt;assertIsNot(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a is not b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertIsNone" rel="noopener noreferrer"&gt;&lt;span&gt;assertIsNone(x)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;x is None&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertIsNotNone" rel="noopener noreferrer"&gt;&lt;span&gt;assertIsNotNone(x)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;x is not None&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertIn" rel="noopener noreferrer"&gt;&lt;span&gt;assertIn(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a in b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotIn" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotIn(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a not in b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertIsInstance" rel="noopener noreferrer"&gt;&lt;span&gt;assertIsInstance(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;isinstance(a, b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotIsInstance" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotIsInstance(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;not isinstance(a, b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertIsSubclass" rel="noopener noreferrer"&gt;&lt;span&gt;assertIsSubclass(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;issubclass(a, b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotIsSubclass" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotIsSubclass(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;not issubclass(a, b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Assert methods that check the production of exceptions, warnings, and log messages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Checks that ...&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertRaises" rel="noopener noreferrer"&gt;&lt;span&gt;assertRaises(exc, fun, *args, **kwds)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;fun(*args, **kwds) raises &lt;/span&gt;&lt;em&gt;&lt;span&gt;exc&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertRaisesRegex" rel="noopener noreferrer"&gt;&lt;span&gt;assertRaisesRegex(exc, r, fun, *args, **kwds)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;fun(*args, **kwds) raises &lt;/span&gt;&lt;em&gt;&lt;span&gt;exc&lt;/span&gt;&lt;/em&gt;&lt;span&gt; and the message matches regex &lt;/span&gt;&lt;em&gt;&lt;span&gt;r&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertWarns" rel="noopener noreferrer"&gt;&lt;span&gt;assertWarns(warn, fun, *args, **kwds)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;fun(*args, **kwds) raises &lt;/span&gt;&lt;em&gt;&lt;span&gt;warn&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertWarnsRegex" rel="noopener noreferrer"&gt;&lt;span&gt;assertWarnsRegex(warn, r, fun, *args, **kwds)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;fun(*args, **kwds) raises &lt;/span&gt;&lt;em&gt;&lt;span&gt;warn&lt;/span&gt;&lt;/em&gt;&lt;span&gt; and the message matches regex &lt;/span&gt;&lt;em&gt;&lt;span&gt;r&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertLogs" rel="noopener noreferrer"&gt;&lt;span&gt;assertLogs(logger, level)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;The with block logs on &lt;/span&gt;&lt;em&gt;&lt;span&gt;logger&lt;/span&gt;&lt;/em&gt;&lt;span&gt; with minimum &lt;/span&gt;&lt;em&gt;&lt;span&gt;level&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNoLogs" rel="noopener noreferrer"&gt;&lt;span&gt;assertNoLogs(logger, level)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;The with block does not log on &lt;/span&gt;&lt;em&gt;&lt;span&gt;logger&lt;/span&gt;&lt;/em&gt;&lt;span&gt; with minimum &lt;/span&gt;&lt;em&gt;&lt;span&gt;level&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertRaises" rel="noopener noreferrer"&gt;&lt;span&gt;assertRaises(exc, fun, *args, **kwds)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;fun(*args, **kwds) raises &lt;/span&gt;&lt;em&gt;&lt;span&gt;exc&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Even more specific checks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Checks that ...&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertAlmostEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertAlmostEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;round(a-b, 7) == 0&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotAlmostEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotAlmostEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;round(a-b, 7) != 0&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertGreater" rel="noopener noreferrer"&gt;&lt;span&gt;assertGreater(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a &amp;gt; b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertGreaterEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertGreaterEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a &amp;gt;= b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertLess" rel="noopener noreferrer"&gt;&lt;span&gt;assertLess(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a &amp;lt; b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertLessEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertLessEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a &amp;lt;= b&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertRegex" rel="noopener noreferrer"&gt;&lt;span&gt;assertRegex(s, r)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;r.search(s)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotRegex" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotRegex(s, r)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;not r.search(s)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertCountEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertCountEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;em&gt;&lt;span&gt;a&lt;/span&gt;&lt;/em&gt;&lt;span&gt; and &lt;/span&gt;&lt;em&gt;&lt;span&gt;b&lt;/span&gt;&lt;/em&gt;&lt;span&gt; have the same elements in the same number, regardless of their order&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertStartsWith" rel="noopener noreferrer"&gt;&lt;span&gt;assertStartsWith(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a.startswith(b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotStartsWith" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotStartsWith(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;not a.startswith(b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertEndsWith" rel="noopener noreferrer"&gt;&lt;span&gt;assertEndsWith(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;a.endswith(b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotEndsWith" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotEndsWith(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;not a.endswith(b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertHasAttr" rel="noopener noreferrer"&gt;&lt;span&gt;assertHasAttr(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;hastattr(a, b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertNotHasAttr" rel="noopener noreferrer"&gt;&lt;span&gt;assertNotHasAttr(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;not hastattr(a, b)&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Type specific assertEqual methods:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Compares ...&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertMultiLineEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertMultiLineEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;strings&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertSequenceEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertSequenceEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;sequences&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertListEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertListEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;lists&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertTupleEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertTupleEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;tuples&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertSetEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertSetEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;sets or frozensets&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertDictEqual" rel="noopener noreferrer"&gt;&lt;span&gt;assertDictEqual(a, b)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;span&gt;dicts&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Using a more precise assert method can help refine your unit tests and make the work of writing them more efficient and optimized.&lt;/p&gt;

&lt;h2 id="using-assert-methods"&gt;Getting your hands dirty: Using more precise assert methods to write better unit tests&lt;/h2&gt;

&lt;p&gt;What I appreciate most about developers as an audience is the emphasis on &lt;em&gt;showing&lt;/em&gt; rather than &lt;em&gt;telling&lt;/em&gt; because personally, I need to see something before I believe it, too. It’s even better when I get to run the code myself and arrive at that “Aha!” moment on my own. Hands-on is the best way to learn.&lt;/p&gt;

&lt;h3&gt;
  
  
  An artisanal, handcrafted, slow coded Pokemon Flask app
&lt;/h3&gt;

&lt;p&gt;You’ve heard of “no code,” right? Well, get ready for “slow code.”&lt;/p&gt;

&lt;p&gt;I wanted to see if I could use &lt;strong&gt;Cursor&lt;/strong&gt;, an AI-powered code editor, to write my unit tests, but I needed some code to test first. I decided to code – by hand – a very simple Pokedex Flask app. Sure, I could have prompted Cursor to do it for me, but that seemed to defeat the purpose of the experiment. Nor does it really simulate a real world use case since most professional developers are probably working with existing pre-AI code, and, more than that, I &lt;em&gt;wanted&lt;/em&gt; to write some Python. Isn’t that why I do this? Because it’s enjoyable?&lt;/p&gt;

&lt;p&gt;Yeah, it’s “slow code” – and it’s important. Programming is a muscle, and if you don’t exercise it regularly, it atrophies. I understand that the craft of code is often not as important as the profit it produces, but at what cost? I could have prompted an LLM to generate this blog post, but I didn’t, because I &lt;em&gt;like writing&lt;/em&gt;. Every blog post I write myself makes me a better writer; every line of code I write makes me a better programmer. It’s that hands-on learning thing.&lt;/p&gt;

&lt;p&gt;So I wrote my app by hand, using a forked minimal Flask template to avoid the boilerplate code. I ended up with a web app that uses &lt;a href="https://utm.guru/ujh7I" rel="noopener noreferrer"&gt;an API endpoint&lt;/a&gt; to view collections of Pokemon according to ability, color, or type. I muddled through the limited JavaScript the app implements and used a Python-wrapped Bootstrap library for the styling. It’s not very complicated so using Cursor to write the unit tests should be a simple task – right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw95gum6u685odb247p6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw95gum6u685odb247p6v.png" alt="A screenshot from the handcrafted Flask app showing all the pins Pokemon" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;
All the pink Pokemon all in one place.



&lt;h3&gt;
  
  
  A look at the AI generated unit tests
&lt;/h3&gt;

&lt;p&gt;My prompt was simple: &lt;code&gt;generate unit tests for pokemon.py using unittest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a look at what we ended up with. Feel free to pull down the code &lt;a href="https://utm.guru/ujh5X" rel="noopener noreferrer"&gt;here&lt;/a&gt; and check it out.&lt;/p&gt;

&lt;p&gt;To start things off, let’s see if the tests pass and what kind of coverage they provide.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;----------------------------------------------------------------------&lt;/span&gt;

Ran 12 tests &lt;span class="k"&gt;in &lt;/span&gt;0.008s

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Name              Stmts   Miss  Cover

&lt;span class="nt"&gt;-------------------------------------&lt;/span&gt;

pokemon.py           29      0   100%

test_pokemon.py     147      1    99%

&lt;span class="nt"&gt;-------------------------------------&lt;/span&gt;

TOTAL               176      1    99%

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

&lt;/div&gt;



&lt;p&gt;Passing tests and 100% coverage. We’re off to a promising start … or are we? Not all coverage is created equally, so it’s worth investigating the tests themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Funky fixtures&lt;/strong&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Set up test fixtures before each test method.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mock_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mock_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Clean up after each test method.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We begin benign enough with some test fixtures in which a mock response is created for every test method in the class. Things start to get a little “smelly” when we examine the teardown method, which is just a &lt;code&gt;pass&lt;/code&gt;. In this particular case, the mock object would already be inaccessible beyond the test function it is created within, so while the teardown ensures it is truly gone, it’s a little excessive, and renders the whole fixture moot. Test fixtures can be very useful, especially when creating isolated, independent test environments, but in this scenario, it doesn’t seem to be adding to the meaningfulness of the tests.&lt;/p&gt;

&lt;p&gt;Furthermore, mock responses are created in each of the test functions, making the fixture even more redundant. (Read more about &lt;a href="https://utm.guru/ujh50" rel="noopener noreferrer"&gt;test fixtures&lt;/a&gt; and &lt;a href="https://utm.guru/ujh5Z" rel="noopener noreferrer"&gt;mocks&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;So already we find ourselves having to refactor AI generated code.&lt;/p&gt;

&lt;p&gt;Now let’s take a look at the first test and its assertions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More maintainable, human friendly tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like their source code, tests will need to be updated as a system evolves. The more “finding and replacing” you need to conduct, the more brittle and unreliable your tests are. Using variables instead of “magic values” can reduce the number of instances that require updating. In this example, we’ve replaced the magic test values and expected values with a variable. Our test is now more modular and easier to maintain.&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;# Act: Execute the code under test
# Test the function
&lt;/span&gt;
&lt;span class="n"&gt;test_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;test_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Assert: Check the results
&lt;/span&gt;
&lt;span class="c1"&gt;# Add a message for the assert methods
&lt;/span&gt;&lt;span class="n"&gt;assert_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;For test values: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;test_input&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; \
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the function produced: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;test_result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;TestCase&lt;/code&gt; assert methods also take a message argument: &lt;code&gt;assertEqual(arg1, arg2, msg=None)&lt;/code&gt; The value provided for &lt;code&gt;msg&lt;/code&gt; outputs when a test fails. This can give us more information about a test failure, which makes it easier to fix or debug.&lt;/p&gt;

&lt;p&gt;Let’s add a test that will fail:&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;# Add a test failure to demonstrate ouput
&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIsNone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;something&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without a message, this is what our test failure looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;AssertionError: &lt;span class="s1"&gt;'something'&lt;/span&gt; is not None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a message, and utilizing the variables we created, even our failures become helpful:&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;# Add a test failure to demonstrate ouput
&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIsNone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;something&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;assert_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;AssertionError: &lt;span class="s1"&gt;'something'&lt;/span&gt; is not None : For &lt;span class="nb"&gt;test &lt;/span&gt;values: &lt;span class="nb"&gt;type &lt;/span&gt;the &lt;span class="k"&gt;function &lt;/span&gt;produced: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'fire'&lt;/span&gt;, &lt;span class="s1"&gt;'water'&lt;/span&gt;, &lt;span class="s1"&gt;'grass'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Too many assertions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are competing philosophies on the number of assertions that a test should contain. Some people will tell you that a unit test should have only one assertion and others might tell you that more than one is okay. When writing tests, it’s important to remember that the assert methods provided by &lt;code&gt;TestCase&lt;/code&gt; “check for and report failures.” Imagine if all of these assertions result in failures you have to then fix or debug. Do these failures actually tell us anything about the code under 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="c1"&gt;# Assertions
&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIsInstance&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="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&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="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we look at the code under test, a list of strings is returned. That’s it, that’s all that happens. While this code is not code you’d want to push to production, it is the code we are testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;attributes_list&lt;/span&gt; &lt;span class="o"&gt;=&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;attributes_list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do we really need to assert on the specific contents of the list? Especially if this particular function doesn’t do anything with those contents? We probably want to reduce the number of assertions in this test. We really only need to test whether or not the code produces a list.&lt;/p&gt;

&lt;p&gt;We can do that with &lt;code&gt;assertEqual(test_result, expected_result, msg=test_message&lt;/code&gt; or we can eliminate yet another assertion (the &lt;code&gt;assertIsInstance&lt;/code&gt;) with &lt;code&gt;assertListEqual&lt;/code&gt; which will not only compare the lists, but also verify the list type.&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertListEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;assert_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t believe me? Let’s change &lt;code&gt;expected_result&lt;/code&gt; to a string and see what happens when we use &lt;code&gt;assertListEqual&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="c1"&gt;# Change `expected_result` to a string
&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertListEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;assert_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nb"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Second&lt;/span&gt; &lt;span class="n"&gt;sequence&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test fails. Now we’ve verified not only the test result itself, but the test result type as well.&lt;/p&gt;

&lt;p&gt;Can we eliminate another assertion? Let’s see!&lt;/p&gt;

&lt;p&gt;Let’s say we want to also make sure we don’t end up with an empty list even though we might not know the exact number of list elements we will end up with. This is where we can use &lt;code&gt;assertGreaterThan&lt;/code&gt; and create a variable &lt;code&gt;list_minimum = 0&lt;/code&gt; for the minimum value we can accept – which, in this case, is zero.&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertGreater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_result&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;list_minimum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;assert_message&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;No comment please&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is just a nit, but the AI generated tests included this comment:&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&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="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# Order matters for this function
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing in the code suggests that, so it’s just a random comment. In response, I added my own useless comment: &lt;code&gt;# No, it doesn't&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(I don’t cover the rest of the tests here, but if you &lt;a href="https://utm.guru/ujh5X" rel="noopener noreferrer"&gt;check out the code&lt;/a&gt;, I’ve commented on the parts of the tests that I would have to refactor if I wanted to make this code production ready.) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before and after&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Comparing the test before and after, our new test is a lot more succinct, meaningful, and maintainable. Now, no matter how the source code evolves, we can rely on this test to tell us if we’ve introduced any breaking changes.&lt;/p&gt;

&lt;p&gt;Before:&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="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pokemon.requests.get&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_attributes_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_get&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test get_attributes function with successful API response.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Mock the response
&lt;/span&gt;    &lt;span class="n"&gt;mock_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;mock_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
                                                 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;               
                                                 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                                                 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;
    &lt;span class="n"&gt;mock_get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_response&lt;/span&gt;

    &lt;span class="c1"&gt;# Test the function
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Assertions
&lt;/span&gt;    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIsInstance&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="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&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="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# Order matters for this function
&lt;/span&gt;    &lt;span class="n"&gt;mock_get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert_called_once_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&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="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pokemon.requests.get&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_attributes_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_get&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test get_attributes function with successful API response.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Mock the response
&lt;/span&gt;    &lt;span class="n"&gt;mock_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;mock_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
                                                 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;               
                                                 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                                                 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;
    &lt;span class="n"&gt;mock_get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_response&lt;/span&gt;

    &lt;span class="c1"&gt;# Test the function
&lt;/span&gt;    &lt;span class="n"&gt;test_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;test_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Assert
&lt;/span&gt;    &lt;span class="n"&gt;assert_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;For test values: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;test_input&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; \
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the function produced: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;test_result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;expected_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fire&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;water&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;list_minimum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertGreater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_result&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;list_minimum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;assert_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertListEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;assert_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mock_get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert_called_once_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In conclusion: Unit tests are the human in the loop
&lt;/h2&gt;

&lt;p&gt;Whether your code is meticulously typed out character by character, copied and pasted from Stack Overflow, or generated by an LLM, unit tests are the quickest way to verify it operates as expected. Moreover, when we start with unit tests that are written with as much care and intention as the source code itself, we lay the foundation for efficiency and optimization which makes writing the next set of unit tests much less laborious and tedious. Solid unit tests are an investment in future productivity. While AI can “hallucinate,” it has no imagination or empathy, so it cannot write tests for the humans who will eventually be stuck deciphering test failures.&lt;/p&gt;

&lt;p&gt;What do you think? Do you think AI will get better at writing unit tests? Do you feel inspired to try out other assert methods in your testing?&lt;/p&gt;

&lt;h2 id="resources-references"&gt;Resources and references&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://utm.guru/ujh5W" rel="noopener noreferrer"&gt;Python Testing – Unit Tests, Pytest, and Best Practices - DEV Community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://utm.guru/ujh5V" rel="noopener noreferrer"&gt;The Arrange, Act, and Assert (AAA) Pattern in Unit Test Automation - Semaphore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://utm.guru/ujh5U" rel="noopener noreferrer"&gt;Python assertEqual(): Test If Two Values are Equal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://utm.guru/ujh5T" rel="noopener noreferrer"&gt;Stop requiring only one assertion per unit test: Multiple assertions are fine - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://utm.guru/ujh5S" rel="noopener noreferrer"&gt;Mastering Unit Tests in Python: A Comprehensive Guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❤️ If you found this blog post helpful, please consider &lt;a href="https://utm.guru/uhSYX" rel="noopener noreferrer"&gt;buying me a coffee&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>testing</category>
    </item>
    <item>
      <title>A New Era of Code Quality: Beyond bugs and into legal license compliance and risk management</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Wed, 25 Jun 2025 18:16:46 +0000</pubDate>
      <link>https://dev.to/lizzzzz/a-new-era-of-code-quality-beyond-bugs-and-into-legal-license-compliance-and-risk-management-5g8n</link>
      <guid>https://dev.to/lizzzzz/a-new-era-of-code-quality-beyond-bugs-and-into-legal-license-compliance-and-risk-management-5g8n</guid>
      <description>&lt;p&gt;In the interconnected world of software, few applications are conjured into existence entirely from scratch. Developers consistently draw upon a vast ecosystem of open-source libraries, frameworks, and external components, all designed to expedite coding and reuse established solutions. It’s simply efficient: Why should anyone invent a solution when a perfectly good one already exists?&lt;/p&gt;

&lt;p&gt;However, while incredibly efficient, reliance on third-party components comes with its own baggage: Licensing. It may seem like a minor detail, but failure to understand or comply with the terms of even one license — among the dozens or hundreds typically found in a modern application — can lead to severe legal disputes, the forced open-sourcing of proprietary code, and substantial financial penalties. This makes license compliance a remarkably complex and unglamorous undertaking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 This article is designed to accommodate different learning and reading styles, so feel free to jump ahead:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;License and litigation and why it’s important to pay attention&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The intersection of dependency management, SBOMs, and software licenses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How license management contributes to code quality&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More resources and references&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="80e1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  License and litigation and why it’s important to pay attention
&lt;/h2&gt;

&lt;p&gt;Modern software projects often have a deep “dependency tree,” where a direct dependency itself relies on other libraries (transitive dependencies). It’s easy to overlook the licenses of these indirect dependencies, leading to hidden compliance risks. Unfortunately, “I didn’t know” is not an excuse that holds up in court.&lt;/p&gt;

&lt;p&gt;Not even the biggest corporations are immune to the consequences of license mismanagement. License violations have cost companies &lt;a href="https://natlawreview.com/article/100-million-reasons-open-source-compliance" rel="noopener noreferrer"&gt;millions of dollars&lt;/a&gt;, forced code &lt;a href="https://en.wikipedia.org/wiki/Free_Software_Foundation,_Inc._v._Cisco_Systems,_Inc." rel="noopener noreferrer"&gt;disclosure&lt;/a&gt; or refactoring, injunctions, &lt;a href="https://softwarefreedom.org/news/2008/mar/17/busybox-verizon/" rel="noopener noreferrer"&gt;settlements&lt;/a&gt;, and precious time and resources that could have been spent otherwise.&lt;/p&gt;

&lt;p&gt;&lt;a id="9ede"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The intersection of dependency management, SBOMs, and software licenses
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is an SBOM?
&lt;/h3&gt;

&lt;p&gt;An &lt;a href="https://www.sonarsource.com/learn/software-bill-of-materials/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=devto-devrel-sca" rel="noopener noreferrer"&gt;SBOM&lt;/a&gt; acts as a comprehensive, machine-readable inventory of all components within a software package. Much like an ingredients list on a food product, &lt;strong&gt;a Software Bills of Materials (SBOM) details every piece of open-source and proprietary software, libraries, and modules that make up a given application, along with their versions and origins&lt;/strong&gt;. And just as a food label might list “enriched flour” and then detail its components like niacin and thiamine, an SBOM also breaks down your dependencies’ dependencies, giving you a complete picture of every ingredient. For developers, maintaining an up-to-date SBOM is crucial for visibility into their software supply chain, enabling them to track and respond to security advisories more effectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2478%2F0%2Au1V4oJ5keiAkPEfR" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2478%2F0%2Au1V4oJ5keiAkPEfR" alt="An example of an SBOM in JSON according to the [CycloneDX](https://cyclonedx.org/capabilities/sbom/) format." width="1239" height="1247"&gt;&lt;/a&gt;&lt;/p&gt;
An example of an SBOM in JSON according to the &lt;a href="https://cyclonedx.org/capabilities/sbom/" rel="noopener noreferrer"&gt;CycloneDX&lt;/a&gt; format.



&lt;h3&gt;
  
  
  What is dependency license management?
&lt;/h3&gt;

&lt;p&gt;As developers, we are usually pretty aware of the importance of checking our “list of ingredients” (albeit often against our will while detangling dependency conflicts) and even if we don’t always immediately address CVEs, we at least know we should probably cut a card for the task. Another aspect of dependencies that we may know less about is license management.&lt;/p&gt;

&lt;p&gt;At its core, &lt;strong&gt;a software license is a legal agreement that defines the terms under which you can use, modify, and distribute a piece of software&lt;/strong&gt;. Think of it like a contract between the creator (licensor) and the user (licensee). Most open-source software comes with specific licenses that dictate how it can be used, modified, and distributed. Failing to comply with these licenses can lead to legal complications, intellectual property disputes, and financial penalties. An SBOM can serve as a foundational tool for license compliance, providing a clear record of the licenses associated with each component. This becomes particularly important in environments where certain license types (e.g., strong copyleft licenses like GPL) may have significant implications for the broader software product.&lt;/p&gt;

&lt;p&gt;The intersection of SBOMs, &lt;a href="https://utm.guru/uiBEc" rel="noopener noreferrer"&gt;CVEs&lt;/a&gt;, and licensing forms the backbone of effective dependency management. In an era where AI hallucinations and the accidental inclusion of malicious or problematic packages are growing concerns, the practice of diligent processes for generating and analyzing SBOMs become indispensable to the production and deployment of quality code.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the different kinds of software licenses?
&lt;/h3&gt;

&lt;p&gt;These licenses are the legal frameworks that define how you can use, modify, and distribute software, whether you’re building a new application, contributing to an open-source project, or simply using a program on your computer. Understanding the distinctions between these licenses is crucial for avoiding legal pitfalls, ensuring compliance, and making informed decisions about your software projects. Licenses can be broken down into the following &lt;a href="https://docs.sonarsource.com/sonarqube-server/latest/advanced-security/managing-license-profiles-and-policies/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=devto-devrel-sca" rel="noopener noreferrer"&gt;broad categories&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standard permissive:&lt;/strong&gt; The most commonly used permissive licenses. They grant broad permissions to use and modify with very minimal obligations (primarily attribution) and have all the essential elements of permissive open source licenses. Examples include &lt;a href="https://tlo.mit.edu/understand-ip/exploring-mit-open-source-license-comprehensive-guide" rel="noopener noreferrer"&gt;MIT&lt;/a&gt; and &lt;a href="https://www.apache.org/licenses/LICENSE-2.0" rel="noopener noreferrer"&gt;Apache&lt;/a&gt; software licenses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Non-standard permissive:&lt;/strong&gt; Permissive licenses that lack one or more essential elements of modern permissive open source licenses, or impose complex or confusing requirements. Examples include &lt;a href="https://dev.perl.org/licenses/artistic.html" rel="noopener noreferrer"&gt;Artistic 1.0&lt;/a&gt; and the &lt;a href="https://www.wtfpl.net/" rel="noopener noreferrer"&gt;WTFPL&lt;/a&gt; software licenses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Weak copyleft:&lt;/strong&gt; Weak copyleft licenses require sharing your changes and additions to the licensed software when you give copies to others. Examples include &lt;a href="https://www.gnu.org/licenses/lgpl-3.0.en.html" rel="noopener noreferrer"&gt;GNU LGPL&lt;/a&gt; and the &lt;a href="https://www.mozilla.org/en-US/MPL/" rel="noopener noreferrer"&gt;Mozilla Public License&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strong copyleft:&lt;/strong&gt; In addition to the requirements of the weak copyleft licenses, strong copyleft licenses require you to share larger programs that you build with the licensed software when you give copies to others. Examples include the GNU GPL license.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Network copyleft:&lt;/strong&gt; In addition to the requirements of strong copyleft licenses, network copyleft licenses require you to share larger programs that you build with the licensed software not just when you give copies to others, but also when you run the software for others to use over the Internet or another network. Examples include the &lt;a href="https://www.gnu.org/licenses/agpl-3.0.en.html" rel="noopener noreferrer"&gt;GNU AGPL&lt;/a&gt; (Which is different from the GNU LGPL!) and the &lt;a href="https://www.mongodb.com/legal/licensing/server-side-public-license" rel="noopener noreferrer"&gt;Server-Side-Public License&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maximal copyleft:&lt;/strong&gt; Maximal copyleft licenses answer the question “When does the license require you to share?” differently than other licenses. Maximal copyleft licenses require you to share software you make with others, and to license that software alike when you do. Examples include the &lt;a href="https://paritylicense.com/" rel="noopener noreferrer"&gt;Parity&lt;/a&gt; and &lt;a href="https://opensource.org/license/RPL-1.5" rel="noopener noreferrer"&gt;Reciprocal&lt;/a&gt; software licenses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Uncategorized:&lt;/strong&gt; Nonstandard licenses that do not fit into the above categories.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2552%2F0%2A4MxT-2eRbFruHwmD" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2552%2F0%2A4MxT-2eRbFruHwmD" alt="With SonarQube Software Composition Analysis, each dependency includes information for all the dependencies it relies on so you can see exactly which licenses are being introduced into your project." width="1276" height="543"&gt;&lt;/a&gt;&lt;/p&gt;
With SonarQube Software Composition Analysis, each dependency includes information for all the dependencies it relies on so you can see exactly which licenses are being introduced into your project.



&lt;p&gt;&lt;a id="29cc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How license management contributes to code quality
&lt;/h2&gt;

&lt;p&gt;At a fundamental level, high-quality code embodies these key traits: it’s reliable, secure, and &lt;em&gt;maintainable&lt;/em&gt;. Software licensing may not be the first thing that comes to mind when considering code maintainability, but think of it this way: Much like tech debt, leaving license management for later can result in “legal debt” that almost inevitably comes to collect. Dependency trees are already thorny when it comes to keeping versions in line, just imagine having to remove dependencies due to license violations. Or worse — the possibility of being forced to recall a product (and all the effort that went into it). Decommissioned code is not maintainable code.&lt;/p&gt;

&lt;p&gt;Moreover, because you can’t oversee the licensing decisions or compliance procedures governing your dependencies’ code, you need an alternative approach to ensure &lt;em&gt;their&lt;/em&gt; potential license problems don’t jeopardize &lt;em&gt;your&lt;/em&gt; own software’s legal standing. Nowadays, a rigorous and intelligent code quality discipline absolutely must encompass legal considerations like dependency license management.&lt;/p&gt;

&lt;p&gt;SonarQube’s &lt;a href="https://www.sonarsource.com/blog/sonarqube-advanced-security-now-available/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=devto-devrel-sca" rel="noopener noreferrer"&gt;Software Composition Analysis&lt;/a&gt; (SCA) goes beyond just listing your project’s dependencies. It also provides a comprehensive overview of associated &lt;strong&gt;license risks&lt;/strong&gt;, complete with holistic scoring and actionable remediation steps. This allows you to prioritize and address license compliance issues according to your organization’s specific needs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Want to learn more about SonarQube Software Composition Analysis? Check out a tutorial &lt;a href="https://utm.guru/uiBD3" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a id="d69d"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More resources and references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Read more about coding best practices &lt;a href="https://utm.guru/uiona" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read more about best practices for dependency risk management in particular &lt;a href="https://docs.sonarsource.com/sonarqube-server/latest/advanced-security/best-practices-for-managing-dependency-risks/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=devto-devrel-sca" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For a breakdown of other top security flaws and how to address them, check out &lt;a href="https://medium.com/@vilojona/top-security-flaws-hiding-in-your-code-right-now-and-how-to-fix-them-342d411f9b99" rel="noopener noreferrer"&gt;Jonathan Vila’s article&lt;/a&gt; on the topic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Want to learn more about SBOMs? &lt;a href="https://medium.com/@tahirbalarabe2/why-every-modern-software-needs-a-software-bill-of-materials-sbom-for-security-04a4fec312c8" rel="noopener noreferrer"&gt;This article&lt;/a&gt; provides a comprehensive overview.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>codequality</category>
      <category>sbom</category>
      <category>sonar</category>
      <category>dependencymanagement</category>
    </item>
    <item>
      <title>A New Era of Code Quality: Beyond bugs to supply chain security and dependency health</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Wed, 25 Jun 2025 17:57:08 +0000</pubDate>
      <link>https://dev.to/lizzzzz/a-new-era-of-code-qualitybeyond-bugs-to-supply-chain-security-and-dependency-health-3p40</link>
      <guid>https://dev.to/lizzzzz/a-new-era-of-code-qualitybeyond-bugs-to-supply-chain-security-and-dependency-health-3p40</guid>
      <description>&lt;p&gt;In today’s interconnected software landscape, most applications don’t just spring up from thin air. Developers are constantly pulling in countless open-source libraries, frameworks, and other third-party components to speed things up and reuse existing solutions. And why not? If a specialized solution already exists for a particular problem, why reinvent the wheel? However, while efficient, this reliance opens up a massive new vulnerability: the software supply chain.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 This article is designed to accommodate different learning and reading styles, so feel free to jump ahead:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Weak links: Anatomy of a supply chain attack and the new threat AI poses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The intersection of dependency management, SBOMs and CVEs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A stick or a snake: A framework for evaluating risk&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How security contributes to code quality&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More resources and references&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="d0f8"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Weak links: Anatomy of a supply chain attack
&lt;/h2&gt;

&lt;p&gt;In the context of cybersecurity, a &lt;strong&gt;supply chain attack refers to a cyberattack that targets an organization by infiltrating less secure elements in its extended network of partners, suppliers, or components, rather than directly attacking the primary target itself&lt;/strong&gt;. Simply put, it’s like a hacker getting to you by attacking someone you do business with.&lt;/p&gt;

&lt;p&gt;The core idea is to compromise a trusted third party or a component that the target organization relies on. By doing so, the attackers can then gain unauthorized access or introduce malicious code into the target’s systems, products, or services without directly breaching their main defenses.&lt;/p&gt;

&lt;p&gt;Think of it like this: If you want to break into a heavily guarded fortress (the main target), instead of attacking the front gates, you find a weakness in the company that supplies their food or builds their walls, and use that weakness to get inside.&lt;/p&gt;

&lt;p&gt;These attacks are particularly dangerous because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;They leverage trust:&lt;/strong&gt; The malicious element comes from a source that the target organization or its customers inherently rely on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;They have a wide blast radius:&lt;/strong&gt; Compromising one component or supplier can lead to a ripple effect. That means that even if your organization wasn’t the primary target of such an attack, you can still be compromised.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;They are often difficult to detect:&lt;/strong&gt; The malicious activity might be hidden within legitimate software updates, hardware, or services, making it challenging for standard security measures to identify.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common vectors for supply chain attacks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Software updates:&lt;/strong&gt; Injecting malware into legitimate software updates, often by compromising the software vendor’s systems or exploiting vulnerabilities in the update delivery mechanism.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open-source components:&lt;/strong&gt; Introducing malicious code into widely used open-source libraries that developers then incorporate into their applications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware tampering:&lt;/strong&gt; Modifying hardware components during manufacturing or shipping.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compromised build tools or environments:&lt;/strong&gt; Attacking the tools or infrastructure used to build software.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Third-party service providers:&lt;/strong&gt; Gaining access through a less secure vendor that has legitimate access to the target’s systems (e.g., an IT service provider).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve seen this play out in countless high-stakes scenarios, where everyone from savvy cybercriminals to &lt;a href="https://www.upguard.com/blog/how-did-kaseya-get-hacked" rel="noopener noreferrer"&gt;highly resourced, persistent threat actors&lt;/a&gt; has &lt;a href="https://www.ivanti.com/blog/software-supply-chain-attack-risk" rel="noopener noreferrer"&gt;leveraged these dependency pathways&lt;/a&gt;. These weren’t about traditional application hacks. They were about malicious packages quietly slipped into public repositories, compromised build pipelines, or trusted updates carrying hidden payloads. And it’s not always malicious! Sometimes a supply chain vulnerability is just a &lt;a href="https://tuxcare.com/blog/navigating-the-java-supply-chain-vulnerability-the-log4j-incident/" rel="noopener noreferrer"&gt;really, really big unintentional complication&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As these supply chain attacks and vulnerabilities illustrate, maintaining a clean Software Bill of Materials (SBOM) is critical to code security. An SBOM is essentially a detailed “ingredient list” for your software, identifying all its components, including open-source libraries and third-party code. This transparency helps you understand exactly what’s in your software, which is crucial because bad actors know how challenging it can be to assess, verify, and remediate these hidden supply chain vulnerabilities. In modern software, it’s a constant tightrope act of prioritization with weights that shift as you cross the wire.&lt;/p&gt;

&lt;p&gt;But don’t worry — it gets more complex!&lt;/p&gt;

&lt;h3&gt;
  
  
  First came “vibe coding” …
&lt;/h3&gt;

&lt;p&gt;“Vibe coding” is an emerging approach to software development, heavily reliant on artificial intelligence, particularly large language models (LLMs). First mentioned by AI researcher Andrej Karpathy in early 2025, “vibe coding” describes a process where developers primarily use natural language prompts — speaking or typing in plain language — to instruct AI tools to generate, refine, and debug code. The core idea allows users to focus on describing what they want the software to do (the “vibe” or intent), letting the AI handle much of the how (the actual code implementation), often without the developer needing to fully understand the generated code. This method aims to make software creation more accessible and potentially faster, especially for simpler projects or prototypes.&lt;/p&gt;

&lt;p&gt;It has been met with both a great deal of skepticism and support. Love it or hate it, &lt;a href="https://utm.guru/uiuBE" rel="noopener noreferrer"&gt;“vibe coding” is changing the way we approach the software development lifecycle&lt;/a&gt;, and with these new tools come new challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Now there is “slopsquatting”
&lt;/h3&gt;

&lt;p&gt;Coined by Python Software Foundation Developer-in-Residence Seth Larson and popularized by &lt;a href="https://mastodon.social/@andrewnez/114302875075999244" rel="noopener noreferrer"&gt;a Mastodon post&lt;/a&gt; from Ecosyste.ms creator Andrew Nesbitt, “slopsquatting” — more formally known as “package hallucination” — is a type of cybersquatting. It is the “&lt;a href="https://en.wikipedia.org/wiki/Slopsquatting" rel="noopener noreferrer"&gt;practice of registering a non-existent software package name that a LLM may hallucinate in its output&lt;/a&gt;.” At best, developers may attempt to install an AI-suggested package only to run into an annoying error; at worst, these false packages are exploited.&lt;/p&gt;

&lt;p&gt;For example, if an AI suggests using a non-existent package like &lt;code&gt;super_fast_lib&lt;/code&gt; and a malicious actor then registers that name on a public repository, a developer blindly installing it could inadvertently introduce malicious code. This is where a robust Software Bill of Materials (SBOM) becomes crucial: An SBOM would ideally reveal the presence of such an unfamiliar or newly added dependency, alerting security teams to a potential slopsquatting attempt and a vulnerability in their supply chain.&lt;/p&gt;

&lt;p&gt;Either way, slopsquatting is yet another threat to software supply chain integrity, and along with all the other risks of vibe coding, further emphasize the need for human oversight in AI-generated code.&lt;/p&gt;

&lt;p&gt;Whether it’s a package that doesn’t actually exist or a package that has been deliberately exploited, software dependencies can be a point of weakness in any system. Secure code is an important aspect of the reliability and integrity of your production systems, regardless of whether the code comes from a third-party, AI, or in-house development. Implementing best practices at every step of development helps ensure that code is not only readable and maintainable, but secure as well.&lt;/p&gt;

&lt;p&gt;&lt;a id="0f61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The intersection of dependency management, SBOMs and CVEs
&lt;/h2&gt;

&lt;p&gt;As highlighted by some of the most notable supply chain attacks and the emerging threat of “slopsquatting,” the increasing reliance on third-party software components underscores the critical importance of robust dependency management. This discipline isn’t just about integrating external code, it’s about understanding and mitigating the inherent risks associated with it. Central to this understanding are Software Bills of Materials (SBOMs) and Common Vulnerabilities and Exposures (CVEs).&lt;/p&gt;

&lt;h3&gt;
  
  
  What is an SBOM?
&lt;/h3&gt;

&lt;p&gt;An &lt;a href="https://www.sonarsource.com/learn/software-bill-of-materials/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=devto-devrel-sca" rel="noopener noreferrer"&gt;SBOM&lt;/a&gt; acts as a comprehensive, machine-readable inventory of all components within a software package. Much like an ingredients list on a food product, &lt;strong&gt;a Software Bills of Materials (SBOM) details every piece of open-source and proprietary software, libraries, and modules that make up a given application, along with their versions and origins&lt;/strong&gt;. And just as a food label might list “enriched flour” and then detail its components like niacin and thiamine, an SBOM also breaks down your dependencies’ dependencies, giving you a complete picture of every ingredient. For developers, maintaining an up-to-date SBOM is crucial for visibility into their software supply chain, enabling them to track and respond to security advisories more effectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2478%2F0%2ARy7QsiyrRTaQZWve" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2478%2F0%2ARy7QsiyrRTaQZWve" alt="An example of an SBOM in JSON according to the [CycloneDX](https://cyclonedx.org/capabilities/sbom/) format." width="1239" height="1247"&gt;&lt;/a&gt;&lt;/p&gt;
An example of an SBOM in JSON according to the &lt;a href="https://cyclonedx.org/capabilities/sbom/" rel="noopener noreferrer"&gt;CycloneDX&lt;/a&gt; format.



&lt;h3&gt;
  
  
  What is a CVE?
&lt;/h3&gt;

&lt;p&gt;Closely related to SBOMs are &lt;a href="https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures" rel="noopener noreferrer"&gt;Common Vulnerabilities and Exposures (CVEs)&lt;/a&gt;. &lt;strong&gt;A CVE is a publicly available identifier for a known cybersecurity vulnerability.&lt;/strong&gt; When a vulnerability is discovered in a specific software component (often a library or framework), it is assigned a CVE ID. By cross-referencing components listed in an SBOM with known CVEs, organizations can rapidly identify and prioritize patching efforts for vulnerable dependencies. This proactive approach helps to prevent exploitation by malicious actors who might otherwise leverage unpatched weaknesses. The “SunSpot” malware’s ability to operate with elevated permissions and deploy backdoors underscores the severe consequences of unaddressed vulnerabilities, whether they stem from sophisticated supply chain attacks, more common software flaws, or AI hallucinations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9axa3nqvixekko61dmf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9axa3nqvixekko61dmf.png" alt="An example of a [CVE record](https://www.cve.org/CVERecord?id=CVE-2025-27789)." width="629" height="658"&gt;&lt;/a&gt;&lt;/p&gt;
An example of a &lt;a href="https://www.cve.org/CVERecord?id=CVE-2025-27789" rel="noopener noreferrer"&gt;CVE record&lt;/a&gt;.



&lt;p&gt;&lt;a id="1806"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A stick or a snake: Not every CVE is a threat to your supply chain
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://www.cve.org/About/Metrics" rel="noopener noreferrer"&gt;CVE.org&lt;/a&gt;, 40,077 CVEs were published in 2024, which is a significant jump from 2023’s 28,961 published CVEs. Responding to each and every CVE in a supply chain is neither feasible nor practical and with FIRST (the Forum of Incident Response and Security Teams) predicting an &lt;a href="https://www.first.org/blog/20250607-Vulnerability-Forecast-for-2025" rel="noopener noreferrer"&gt;increase of CVEs in 2025&lt;/a&gt;, you need to draw on other information to determine best next steps when alerted to a CVE for one of your dependencies.&lt;/p&gt;

&lt;p&gt;Imagine you are walking down a road where snakes are known to slither and you see a stick in the distance. You might mistake it for something more dangerous than just an errant piece of wood. If you’ve had the misfortune of a snake bite, you might be biased toward assuming the stick is more than just a stick. Conversely, if you’ve never encountered a snake despite reports of their presence, you might walk too close past what you &lt;em&gt;think&lt;/em&gt; is a stick and make your last mistake ever.&lt;/p&gt;

&lt;p&gt;Not all dependency vulnerabilities and exposures pose the same risk. How a CVE may affect a system is &lt;em&gt;dependent&lt;/em&gt; (See what I did there?) on a few different factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Common Vulnerability Scoring System (CVSS)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Known Exploited Vulnerabilities (KEV) catalog&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Exploit Prediction Scoring System (EPSS)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Anu6iZj2y8tf1RVfe" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Anu6iZj2y8tf1RVfe" alt="With SonarQube Software Composition Analysis, each dependency risk is evaluated holistically. As you can see here, while the CVSS score is high, the EPSS score is closer to medium with no known exploits. This information can help you determine if this CVE is worth addressing and how." width="450" height="359"&gt;&lt;/a&gt;&lt;/p&gt;
With SonarQube Software Composition Analysis, each dependency risk is evaluated holistically. As you can see here, while the CVSS score is high, the EPSS score is closer to medium with no known exploits. This information can help you determine if this CVE is worth addressing and how.



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Combining all these facets of a CVE helps provide a more holistic approach to vulnerability remediation. In order to understand how CVSS, KEV, and EPSS come together, it’s important to understand them independently.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the CVSS?
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.first.org/cvss/v4-0/user-guide" rel="noopener noreferrer"&gt;Common Vulnerability Scoring System (CVSS)&lt;/a&gt; &lt;strong&gt;is a free and open industry standard for assessing the severity of computer system security vulnerabilities&lt;/strong&gt;. It provides a numerical score (ranging from 0.0 to 10.0) that reflects the characteristics and severity of a vulnerability. Commissioned by the National Infrastructure Advisory Council (NIAC) and now under the custodianship of the Forum of Incident Response and Security Teams (FIRST), the CVSS was introduced in 2005. Its mission is to standardize vulnerability assessments to empower organizations to evaluate and respond to risks efficiently and effectively.&lt;/p&gt;

&lt;p&gt;A CVSS score is determined by three metric groups:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Base:&lt;/strong&gt; These represent the intrinsic characteristics of a vulnerability and are constant over time and across user environments. They consider factors like attack vector, attack complexity, required privileges, user interaction, scope, and impact on confidentiality, integrity, and availability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Temporal:&lt;/strong&gt; These reflect the evolving characteristics of a vulnerability over time. They account for factors such as the maturity of the exploit code, the availability of a fix, and the level of confidence in the existence of the vulnerability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Environmental:&lt;/strong&gt; These metrics allow for custom adjustments to the score based on the specific context of an organization’s environment. For example, a vulnerability might be more critical in a system handling sensitive government data than in a less critical internal tool.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To summarize, CVEs provide a unique identifier for a publicly known security flaw while the CVSS provides the numerical severity rating that helps organizations assess the risk associated with a CVE within their specific context. CVSS scores serve as a vital tool for making informed decisions about which dependencies to use, which to patch first, and how to allocate limited security resources most effectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Ag3SfLeFGzVNR6Oix" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Ag3SfLeFGzVNR6Oix" alt="An example of a [CVSS score](https://www.cve.org/CVERecord?id=CVE-2025-27789)." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
An example of a &lt;a href="https://www.cve.org/CVERecord?id=CVE-2025-27789" rel="noopener noreferrer"&gt;CVSS score&lt;/a&gt;.



&lt;h3&gt;
  
  
  What is a KEV?
&lt;/h3&gt;

&lt;p&gt;While the CVSS provides a crucial framework for assessing the technical severity of a vulnerability, it doesn’t inherently tell you if that vulnerability is actively being used by attackers “in the wild.” This is where the concept of a Known Exploited Vulnerabilities (KEV) catalog becomes useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A KEV catalog is a curated list of vulnerabilities that are known to have been actively exploited by threat actors&lt;/strong&gt;. Unlike a comprehensive database of all discovered vulnerabilities (which can number in the hundreds of thousands), a KEV catalog focuses on the subset of vulnerabilities that pose an immediate and proven threat because they have already been leveraged in real-world attacks.&lt;/p&gt;

&lt;p&gt;The most prominent and widely referenced KEV catalog is maintained by the &lt;a href="https://www.cisa.gov/known-exploited-vulnerabilities-catalog" rel="noopener noreferrer"&gt;U.S. Cybersecurity and Infrastructure Security Agency (CISA)&lt;/a&gt;. The CISA’s KEV contributes critically to cybersecurity by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prioritizing remediation:&lt;/strong&gt; If a dependency in your software has a CVE that appears on the KEV list, it signifies an urgent need for patching or mitigation, regardless of its CVSS score. (Not surprisingly KEVs often have high CVSS scores.) The fact that a vulnerability has been exploited means it’s not a theoretical risk, but a clear and present danger.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shifting from reactive to proactive defense:&lt;/strong&gt; By focusing on vulnerabilities that attackers are already exploiting, organizations can more effectively cut through the noise of countless theoretical vulnerabilities and target efforts where they will have the most impact.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Providing actionable intelligence:&lt;/strong&gt; The KEV catalog is not just a list; it provides actionable intelligence to defenders. It serves as a clear signal from a trusted authority that these specific vulnerabilities are being weaponized and require immediate attention.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For federal agencies and — increasingly — for private sector organizations, vulnerabilities listed in the CISA KEV catalog are considered extremely high-priority. Without this particular KEV catalog, organizations may be forced to cross reference several different sources in order to &lt;em&gt;not waste&lt;/em&gt; precious resources chasing after every single risk in an increasingly insecure cyber landscape.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the EPSS model?
&lt;/h3&gt;

&lt;p&gt;In the ongoing battle to secure complex software supply chains, simply knowing about vulnerabilities (CVEs) and their technical severity (CVSS), or even if they’ve been exploited (KEV), isn’t always enough to make the most informed decisions. Security teams need to understand the likelihood that a newly disclosed vulnerability will be exploited in the near future. This is precisely the gap that the &lt;a href="https://www.first.org/epss/model" rel="noopener noreferrer"&gt;Exploit Prediction Scoring System (EPSS)&lt;/a&gt; model aims to fill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The EPSS is an open, data-driven, and predictive scoring system that estimates the probability of a software vulnerability being exploited in the wild within the next 30 days&lt;/strong&gt;. Developed by FIRST — the same organization behind CVSS — EPSS provides a crucial layer of intelligence that complements existing vulnerability management tools.&lt;/p&gt;

&lt;p&gt;Unlike the CVSS, which scores the severity of a vulnerability’s potential impact, EPSS focuses exclusively on the &lt;em&gt;probability&lt;/em&gt; that it will be actively exploited. A vulnerability might have a high CVSS score, but if no one is &lt;em&gt;actually&lt;/em&gt; exploiting it, its immediate risk profile might be lower than a medium-CVSS vulnerability that has a high EPSS score.&lt;/p&gt;

&lt;p&gt;The EPSS estimate is updated regularly based on the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The existence of publicly available exploit code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Discussions of the vulnerability on social media and dark web forums.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The age of the vulnerability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The prevalence of the affected software.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Its relationship to other exploited vulnerabilities. This dynamic nature means EPSS scores can change rapidly as new information emerges, reflecting the evolving threat landscape.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AZe7kzBfa9s8lQsf2" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AZe7kzBfa9s8lQsf2" alt="A diagram comparing the outcomes of [CVSS scores vs. EPSS scores](https://www.first.org/epss/model)." width="1600" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;
A diagram comparing the outcomes of &lt;a href="https://www.first.org/epss/model" rel="noopener noreferrer"&gt;CVSS scores vs. EPSS scores&lt;/a&gt;.



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Combined with CVEs, a CVSS score, and data from the CISA KEV catalog, the EPSS estimate plays a vital role in an environment where the speed and sophistication of attacks — as seen in supply chain compromises — demand smarter and more efficient resource allocation to secure ever-growing and complex software dependencies.&lt;/p&gt;

&lt;p&gt;In other words, security threats contain different facets of vulnerability that together determine the risk they pose and the urgency with which they should be addressed. Once you have decided that the stick down the road is indeed a snake, your next moves are based on your proximity to the snake, whether or not the snake is poisonous, whether or not an antidote exists, and whether or not the snake is an aggressive and deadly rattle snake or a common garter snake that will mostly likely just slither away upon your approach. In moments where quick decisions can be a matter of life or death — or a matter of a quick patch or a security notice to customers — these inputs help form a framework of threat evaluation that can help streamline next steps.&lt;/p&gt;

&lt;p&gt;&lt;a id="fc14"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How security contributes to code quality
&lt;/h2&gt;

&lt;p&gt;At a fundamental level, high quality code exhibits the following traits: it is maintainable, it is reliable, and it is &lt;em&gt;secure&lt;/em&gt;. With software’s increasing power and complexity, &lt;a href="https://www.linuxfoundation.org/blog/blog/a-summary-of-census-ii-open-source-software-application-libraries-the-world-depends-on" rel="noopener noreferrer"&gt;relying on third party components is inevitable&lt;/a&gt;. And that’s not a bad thing — in fact, avoiding redundancy is a &lt;a href="https://utm.guru/uiona" rel="noopener noreferrer"&gt;good coding best practice&lt;/a&gt;. However, since you don’t get to review the pull requests that are merged into the code of your dependencies, you need some other method of ensuring that their potential lapses in code quality don’t affect the quality of &lt;em&gt;your&lt;/em&gt; code. These days, a robust and intelligent code quality discipline needs to include security considerations such as dependency vulnerability management.&lt;/p&gt;

&lt;p&gt;A tool like &lt;a href="https://www.sonarsource.com/blog/sonarqube-advanced-security-now-available/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=devto-devrel-sca" rel="noopener noreferrer"&gt;SonarQube Advanced Security&lt;/a&gt; and its Software Composition Analysis (SCA) enables you to not only view all the dependencies within your project, but provides you with a list of dependency risks with holistic scoring and actionable fixes so you can prioritize next steps according to the specific needs of your organization.&lt;/p&gt;

&lt;p&gt;In collaboration with &lt;a href="https://www.sonarsource.com/company/press-releases/sonar-to-acquire-tidelift/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=devto-devrel-sca" rel="noopener noreferrer"&gt;Tidelift and its roster of open source maintainers&lt;/a&gt;, SonarQube SCA goes beyond public databases of vulnerabilities and provides insights straight from the source. This adds a layer of specialized intelligence that can help reduce the noise often associated with dependency scanning. While the Exploit Prediction Scoring System (EPSS) offers a probability of exploitation, building on the critical “Known Exploited Vulnerabilities” (KEV) catalog, SonarQube SCA further enhances this by integrating real-world maintainer insights, providing an even more refined and actionable understanding of risk. (Because we all know how often we &lt;em&gt;actually&lt;/em&gt; look at our Dependabot alerts …)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Want to learn more about SonarQube Software Composition Analysis? Check out a tutorial &lt;a href="https://utm.guru/uiBD3" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a id="be6f"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More resources and references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Read more about coding best practices &lt;a href="https://utm.guru/uiona" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read more about best practices for dependency risk management in particular &lt;a href="https://docs.sonarsource.com/sonarqube-server/latest/advanced-security/best-practices-for-managing-dependency-risks/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=devto-devrel-sca" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For a breakdown of other top security flaws and how to address them, check out &lt;a href="https://medium.com/@vilojona/top-security-flaws-hiding-in-your-code-right-now-and-how-to-fix-them-342d411f9b99" rel="noopener noreferrer"&gt;Jonathan Vila’s article&lt;/a&gt; on the topic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Want to learn more about SBOMs? &lt;a href="https://medium.com/@tahirbalarabe2/why-every-modern-software-needs-a-software-bill-of-materials-sbom-for-security-04a4fec312c8" rel="noopener noreferrer"&gt;This article&lt;/a&gt; provides a comprehensive overview.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>codequality</category>
      <category>sbom</category>
      <category>dependencymanagement</category>
    </item>
    <item>
      <title>Passing the Vibe Check: Navigating the changing development landscape</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Fri, 16 May 2025 16:50:04 +0000</pubDate>
      <link>https://dev.to/lizzzzz/passing-the-vibe-check-navigating-the-changing-development-landscape-939</link>
      <guid>https://dev.to/lizzzzz/passing-the-vibe-check-navigating-the-changing-development-landscape-939</guid>
      <description>&lt;p&gt;In February 2025, OpenAI researcher &lt;a href="http://archive.today/2025.03.19-071425/https://x.com/karpathy/status/1886192184808149383" rel="noopener noreferrer"&gt;Andrej Karpathy coined the term “vibe coding” on social media&lt;/a&gt;. In his post, he described his process for developing a small project — without ever writing any code of his own.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Aa22RbcKI8lA3NcpO" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Aa22RbcKI8lA3NcpO" alt="The inception of the term “vibe coding.”" width="596" height="563"&gt;&lt;/a&gt;&lt;/p&gt;
The inception of the term “vibe coding.



&lt;p&gt;&lt;br&gt;&lt;br&gt;
To build his “amusing throwaway weekend project,” Karpathy didn’t even need to touch a keyboard. He used superwhisper, an AI-powered voice to text transcriptor, to dictate application requirements to Cursor Composer. Cursor Composer, the AI-powered multi-file code editor and application generator, then translated the dictated instructions into a complete web app. Karpathy adjusted the app with the “dumbest” prompts like “decrease the padding on the sidebar by half” and accepted the changes without reviewing the diffs. When Karpathy encountered an issue that Cursor couldn’t fix, the OpenAI co-founder would just “ask for random changes” until the bug went away.&lt;/p&gt;

&lt;p&gt;“It’s not really coding,” he admitted, “I just see stuff, say stuff, run stuff, and copy paste stuff, and it &lt;em&gt;mostly&lt;/em&gt; works.” (Emphasis mine.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AWOmR4LKCblybAutY" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AWOmR4LKCblybAutY" alt="Look ma — no typing! A diagram of Karpathy’s vibe coding stack." width="548" height="369"&gt;&lt;/a&gt;&lt;/p&gt;
Look ma — no typing! A diagram of Karpathy’s vibe coding stack.



&lt;p&gt;&lt;br&gt;&lt;br&gt;
But the vibes were anything but good for Leonel Acevedo, CEO of EnrichLead. In March 2025, &lt;a href="http://archive.today/2025.03.17-203347/https://x.com/leojr94_/status/1900767509621674109" rel="noopener noreferrer"&gt;he posted on social media&lt;/a&gt; about the SaaS application he’d built with “zero hand written code,” concluding that everyone else could “continue to whine about it [AI] or start building.” This use of AI contrasts with Karpathy’s “throwaway” project as Acevedo was touting a production-ready SaaS application and implying a different level of robustness and real-world applicability.&lt;/p&gt;

&lt;p&gt;Just a few days later, Acevedo posted that EnrichLead was under attack with &lt;a href="http://archive.today/2025.03.17-203415/https://x.com/leojr94_/status/1901560276488511759" rel="noopener noreferrer"&gt;“random things happening”&lt;/a&gt; such as maxed-out API keys, bypassed subscriptions, and corrupted databases. Because Acevedo isn’t technical, it took him longer than he anticipated to debug the code. Eventually he discovered that his vibed code exposed important API keys, making his app vulnerable to attack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AQhYGAZ6-VdIaiPPU" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AQhYGAZ6-VdIaiPPU" alt="The worst kind of live debugging ever." width="535" height="475"&gt;&lt;/a&gt;&lt;/p&gt;
The worst kind of live debugging ever.



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Accidentally pushing a hardcoded secret can be seen as a rite of passage for developers. This mistake is so common that GitHub has automatic alerting, and remediation for unintentionally publicized keys and tokens; at Sonar, we shifted that check further left by highlighting potential vulnerabilities &lt;em&gt;&lt;a href="https://www.sonarsource.com/products/sonarlint/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;right in your IDE&lt;/a&gt;&lt;/em&gt;. With the advent of “vibe coding,” incidents like the one EnrichLead experienced are likely to become more common. While it’s too soon to say if vibe coding will persist, one thing we know for sure is that AI-generated code is here and it’s here to stay.&lt;/p&gt;

&lt;p&gt;And it’s just another iteration of the continuously evolving software development lifecycle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 This article is designed to accommodate different learning and reading styles, so feel free to jump ahead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What is vibe coding?: A definition of vibe coding along with the associated tools, benefits, and disadvantages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To vibe or not to vibe?: Surviving AI as a developer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More than just vibes: How AI can help us become better developers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Try it yourself: Tools and resources for surviving vibe coding&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="1d33"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is vibe coding?
&lt;/h2&gt;

&lt;p&gt;Vibe coding — or if you prefer a less cringey name, “AI coding” — is “&lt;a href="https://en.wikipedia.org/wiki/Vibe_coding" rel="noopener noreferrer"&gt;an AI-dependent programming technique where a person describes a problem in a few sentences as a prompt to a large language model (LLM) tuned for coding.&lt;/a&gt;” Vibe coding relies exclusively on LLMs to interpret requirements and generate entire applications accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  What tools are used for vibe coding?
&lt;/h3&gt;

&lt;p&gt;The most popular vibe coding tools at the moment are &lt;strong&gt;Cursor&lt;/strong&gt; and &lt;strong&gt;Windsurf&lt;/strong&gt;. Both IDEs use Claude 3.5 Sonnet under the AI hood, but with different features and user experiences.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxcakohgth5e4yi3ptvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxcakohgth5e4yi3ptvi.png" alt="Cursor and Windsurf use the same LLM under the hood, but each IDE has its own vibes." width="777" height="280"&gt;&lt;/a&gt;&lt;/p&gt;
Cursor and Windsurf use the same LLM under the hood, but each IDE has its own vibes.



&lt;h3&gt;
  
  
  What are the benefits of vibe coding?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lower cost of entry:&lt;/strong&gt; No coding knowledge, no problem! If you’ve got a good, well thought-out idea and can write a decent prompt, the code itself is no longer an obstacle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Higher development velocity:&lt;/strong&gt; When you can let the IDE handle all of the code, prototyping and iteration cycles become quicker and easier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;More room for complex and innovative problem solving:&lt;/strong&gt; With the burden of writing boilerplate, class setups, basic CRUD operations, and other tedious tasks lifted, developers are free to focus on more high-level logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A bridge for knowledge gaps:&lt;/strong&gt; Even the most experienced developer faces the challenges of learning new technology — AI coding can facilitate quicker onboarding by supplying the fundamentals.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddi48o39tzchdq72ibij.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddi48o39tzchdq72ibij.gif" alt="Prompting Cursor to generate a Pokédex Flask app …" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;
Prompting Cursor to generate a Pokédex Flask app …



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafy6ondgum9k8p4ns93z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafy6ondgum9k8p4ns93z.gif" alt="… and it worked!" width="510" height="287"&gt;&lt;/a&gt;&lt;/p&gt;
… and it worked!



&lt;h3&gt;
  
  
  What are the disadvantages of vibe coding?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of consistency and predictability:&lt;/strong&gt; It is important to remember that the output of LLMs is not deterministic. This can lead to a codebase with varying styles, structures, and approaches, rendering the code harder to understand, maintain, and debug predictably.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An increased vector of bugs, errors, and security vulnerabilities:&lt;/strong&gt; Because AI doesn’t actually “know” anything, a prompt may generate code with issues that a more experienced developer would know to avoid.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Challenges in long-term maintainability and scalability:&lt;/strong&gt; Without any context, an LLM may produce code that is inconsistent and with no consideration for future optimization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Less developer productivity:&lt;/strong&gt; AI-generated code may not be well documented, increasing developer toil while trying to refactor, debug, or extend code. Moreover, trying to refine a prompt for a more desired outcome could take more time than just writing the code manually.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdllw451quj8ako7w9827.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdllw451quj8ako7w9827.png" alt="The problem with vibes: You probably know better than to include all your styling in your HTML template file … but AI doesn’t!" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;
The problem with vibes: You probably know better than to include all your styling in your HTML template file … but AI doesn’t!



&lt;p&gt;&lt;a id="9b75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  To vibe or not to vibe? Surviving AI as a software developer
&lt;/h2&gt;

&lt;p&gt;The software development lifecycle (SDLC) refers to a structured process for designing and building software. It is a rapidly evolving landscape wherein each iteration the role of the developer changes and adapts to address the latest challenges in software engineering.&lt;/p&gt;

&lt;p&gt;In the earliest “code-and-fix” era, the developer was a lone coder, directly addressing issues as they arose with little formal process. The advent of the Waterfall model transformed the developer into a specialist within a linear process, focusing on specific stages like coding or testing after requirements were defined. Iterative and incremental models then required the developer to become more adaptable, working in smaller cycles and integrating feedback more frequently.&lt;/p&gt;

&lt;p&gt;The rise of object-oriented methodologies shifted the developer towards architect and component builder, emphasizing modularity and reusability. The Agile revolution demanded the developer become a collaborative team member, engaging in frequent communication, adapting to changing requirements, and participating in all stages of the sprint. Finally, the Lean and DevOps era positions the developer as an integrated part of the entire delivery pipeline, involved in automation, deployment, and operations, with a broader responsibility for efficiency and reliability.&lt;/p&gt;

&lt;p&gt;AI coding signals a new horizon for SDLC, and with that new day comes new opportunities for developers — as long as we’re prepared for them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2600%2F0%2A5ELnc_9y96BfZgKW" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2600%2F0%2A5ELnc_9y96BfZgKW" alt="The only const is = “change”: The evolution of the software development lifecycle." width="1300" height="294"&gt;&lt;/a&gt;&lt;/p&gt;
The only const is = “change”: The evolution of the software development lifecycle.



&lt;h3&gt;
  
  
  The evolution of the developer
&lt;/h3&gt;

&lt;p&gt;By understanding the benefits and disadvantages of AI, you will be better equipped to continue to grow as a developer. After all — you are a developer. You’re smart and learning new technologies is what you do best.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Familiarize yourself with AI coding tools for developers.&lt;/strong&gt; Whether you call it “vibe coding” or “AI coding,” check out the different tools people are using. There’s nothing quite like hands-on experience to learn something new.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vibe, but verify.&lt;/strong&gt; Get into a good practice of double-checking the AI’s work. With AI doing most of the heavy lifting, your job is more about diligently overseeing and reviewing the code rather than manually composing it. Employing a code quality tool like &lt;a href="https://www.sonarsource.com/products/sonarqube?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;SonarQube&lt;/a&gt; could have prevented EnrichLead’s secret exposure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embrace the opportunity to think more systematically.&lt;/strong&gt; With AI handling all the nitty-gritty, you can focus more on solving more complex problems with more innovative solutions. You are more free to be an architect without having to be an expert on each component.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Learn how to refine your prompts.&lt;/strong&gt; LLMs like Claude respond better to clear, concise prompts that explicitly outline what is being requested and what the expected output is. Thinking more strategically about what your requirements are, what kind of design pattern you want to follow, how you are going to test your code, and how you are going to deploy and maintain it will help inform and improve your prompting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 How could you formulate a prompt to avoid exposing secrets? What requirements or parameters would you include?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="ece1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More than just vibes: How AI can help us become better developers
&lt;/h2&gt;

&lt;p&gt;Waterfall, Agile, ad-hoc, DevOps, vibe coding — no matter what SDLC or tools we are using, as developers, we are concerned with efficiency, optimization, reliability, and maintainability. We want to make software that is useful &lt;em&gt;and&lt;/em&gt; we don’t want to get paged in the middle of the night for an incident. AI can be a valuable tool that helps remove some of the more tedious tasks associated with development and free us to think more creatively; the potential disadvantages of AI mean we have the opportunity to be more deliberate and intentional in our work.&lt;/p&gt;

&lt;p&gt;While we can’t predict exactly &lt;em&gt;how&lt;/em&gt; AI will affect the software development lifecycle, we do know that no matter what direction it takes, &lt;a href="https://www.sonarsource.com/blog/ai-code-assurance-sonar/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;code quality and security&lt;/a&gt; will always be central to meaningful, robust, and reliable software.&lt;/p&gt;

&lt;p&gt;Just ask the guy at EnrichLead.&lt;/p&gt;

&lt;p&gt;&lt;a id="464b"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself: Tools and resources for surviving vibe coding
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For all you Java developers out there, Sonar developer advocate Jonathan Vila has posts on Java and code quality &lt;a href="https://medium.com/@vilojona/code-reviews-with-ai-a-developer-guide-06761b73ef54?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Interested in learning more about how Sonar can help you vibe better? Check out &lt;a href="https://www.sonarsource.com/learn/integrating-quality-gates-ci-cd-pipeline/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Still feeling a little AI anxiety? &lt;a href="https://uxdesign.cc/cracking-the-code-of-vibe-coding-124b9288e551" rel="noopener noreferrer"&gt;This article&lt;/a&gt; by UX Collective’s Pete Sena might help alleviate that.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Whether we like it or not, AI is (probably) here to stay, so now’s a good time to start developing some &lt;a href="https://thenewstack.io/seven-habits-of-highly-effective-ai-coding/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;good AI coding habits&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No matter what, &lt;a href="https://utm.guru/uiona" rel="noopener noreferrer"&gt;best practices&lt;/a&gt; will always be in style.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>vibecoding</category>
      <category>bestpractices</category>
      <category>cursor</category>
    </item>
    <item>
      <title>Code That Won’t Cost You: Coding best practices for any language to increase code quality and reduce developer toil</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Tue, 13 May 2025 20:21:02 +0000</pubDate>
      <link>https://dev.to/lizzzzz/code-that-wont-cost-you-coding-best-practices-for-any-language-to-increase-code-quality-and-5461</link>
      <guid>https://dev.to/lizzzzz/code-that-wont-cost-you-coding-best-practices-for-any-language-to-increase-code-quality-and-5461</guid>
      <description>&lt;p&gt;Bad code is not only hard to read, it can cause expensive incidents. According to the &lt;a href="https://www.businesswire.com/news/home/20220608005265/en/Uptime-Institutes-2022-Outage-Analysis-Finds-Downtime-Costs-and-Consequences-Worsening-as-Industry-Efforts-to-Curb-Outage-Frequency-Fall-Short" rel="noopener noreferrer"&gt;Uptime Institute’s 2022 Outage Analysis&lt;/a&gt;, “nearly 40% of organizations have suffered a major outage caused by human error over the past three years” with over 60% of all failures resulting in at least $100,000 in total losses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With more and more &lt;a href="https://thenewstack.io/ai-code-generation-6-faqs-for-developers/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;AI-generated code creeping into software&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;the potential for bad code increases.&lt;/strong&gt; While LLMs are just the latest innovation altering the software development lifecycle, regardless of whatever disruption comes next, code will always benefit from consistently applied best practices.&lt;/p&gt;

&lt;p&gt;In this article, we’ll talk about tech debt, good versus bad code, best practices to follow, and other ways you can fortify your software. We’ll also discuss practical strategies for implementing these best practices in your development workflow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 This article is designed to accommodate different learning and reading styles, so feel free to jump ahead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When tech debt comes to collect: Understanding the cost of neglecting code quality&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The good, the bad, and the ugly code: What “good code” is and why it is important&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suggested best practices for code quality in any language: The primary focus of this article, this section suggests best practices for avoiding bad code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From theory to practice: Best practices for implementing coding standards effectively&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Key takeaways: Building a foundation for sustainable software development&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Try it yourself: Tools and resources for enforcing code quality&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="c09e"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When tech debt comes to collect: Understanding the cost of neglecting code quality
&lt;/h2&gt;

&lt;p&gt;“Oh, we’ll add that to the backlog and when we have a chance, we’ll get around to it.”&lt;/p&gt;

&lt;p&gt;Say these words two more times and you’ll invoke a curse known as “technical debt.” For those of you new enough in your engineering careers to be blissfully unaware of this curse, technical debt (often shortened to “tech debt”) refers to the extra work required &lt;em&gt;in the future&lt;/em&gt; to correct the outcomes of faster or more expedient solutions, including architectural designs and development shortcuts, &lt;em&gt;taken in the present&lt;/em&gt;. Like financial debt, tech debt incurs interest the longer it is put off. And like financial debt, tech debt can be expensive.&lt;/p&gt;

&lt;p&gt;In January 2023, tech debt came to collect when &lt;strong&gt;more than 1,300 flights in the United States had to be cancelled and 10,000 more delayed&lt;/strong&gt;. Why? &lt;a href="https://www.faa.gov/newsroom/faa-notam-statement]" rel="noopener noreferrer"&gt;Because files accidentally deleted while trying to update a database&lt;/a&gt; caused the Notice to Air Missions system (NOTAM) — which alerts pilots to potential hazards along their routes — &lt;a href="https://www.cnbc.com/2023/01/11/faa-orders-airlines-to-pause-departures-until-9-am-et-after-system-outage.html" rel="noopener noreferrer"&gt;to fail&lt;/a&gt;. It wasn’t the first time something like this had happened; &lt;a href="https://www.nytimes.com/2022/12/31/opinion/southwest-airlines-computers.html" rel="noopener noreferrer"&gt;they had been kicking the tech debt can down the road for years&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In essence, tech debt is the result of unaddressed technical decisions, &lt;strong&gt;including poorly written code,&lt;/strong&gt; that have remained unchanged and propagated throughout a software system, leading to future complications. When these brittle systems finally collapse, it costs more to fix them than it would have cost to avoid the debt altogether. Moreover, on average, &lt;strong&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3194164.3194178" rel="noopener noreferrer"&gt;developers waste 23% of their development time on tech debt&lt;/a&gt;&lt;/strong&gt;, and the cognitive drain can lead to dissatisfaction, burnout, and &lt;em&gt;even more debt&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The solution seems simple: &lt;strong&gt;Don’t push bad code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, the daily pressures of deadlines, the complexities of existing systems, and the need for quick fixes often make this a significant challenge. Consistently writing clear, maintainable code requires discipline, awareness, and the implementation of sound development practices.&lt;/p&gt;

&lt;p&gt;&lt;a id="d4de"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The good, the bad, and the ugly code: What “good code” is and why it is important
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is considered “bad code”?
&lt;/h3&gt;

&lt;p&gt;“Bad code” is a lot of things: Hardcoded secrets, overly complex functions, missing unit tests, unnecessary repetition, unclear documentation, etc. Go look at some of your earliest code — it’s probably not as good as the code you write now. At a high level, while some bad code takes longer than it should to understand, other forms of bad code might be simple to grasp but still exhibit issues like inefficiency, lack of maintainability, or poor design. &lt;strong&gt;Code that is harder to understand is more susceptible to bugs and vulnerabilities because its complexity makes it difficult for developers to correctly interpret its logic&lt;/strong&gt;, leading to errors during modification, debugging, or when integrating with other parts of the system.&lt;/p&gt;

&lt;p&gt;Similar to poorly written prose, if you find yourself having to stop and re-read a block of code more than a few times, it’s probably code that should be refactored. Difficulty in code comprehension isn’t just a minor inconvenience; bad code significantly complicates debugging — &lt;strong&gt;especially in the chaos of a 3 AM critical outage incident&lt;/strong&gt;. That’s when things get really ugly.&lt;/p&gt;

&lt;h3&gt;
  
  
  So what is considered “good code”?
&lt;/h3&gt;

&lt;p&gt;While one aspect of bad code can be its difficulty to read, defining it solely by this characteristic is an oversimplification. Good code, therefore, involves more than just being “easy to read”; it also encompasses factors like maintainability, efficiency, clarity of intent, and adherence to best practices. So what specific attributes contribute to this readability? Just as standards for good writing vary across genres, the characteristics of good code can differ based on programming languages, system architectures, and frameworks.&lt;/p&gt;

&lt;p&gt;However, at a fundamental level, high quality code exhibits several key traits: &lt;strong&gt;&lt;a href="https://www.sonarsource.com/learn/code-quality/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;it is maintainable, reliable, and secure&lt;/a&gt;&lt;/strong&gt;. Now that we have a sense of what makes code easy to understand, change, operate, and debug, what are some best coding practices we can keep in mind to help guide our software development efforts?&lt;/p&gt;

&lt;p&gt;&lt;a id="d439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Suggested best practices for code quality in any language
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Never hardcode secrets
&lt;/h3&gt;

&lt;p&gt;Hardcoding sensitive information or arbitrary values directly into your code is a significant source of technical debt and security risks. In October 2022, &lt;a href="https://blog.gitguardian.com/toyota-accidently-exposed-a-secret-key-publicly-on-github-for-five-years/" rel="noopener noreferrer"&gt;Toyota revealed that a hardcoded secret had been exposed in a public repo&lt;/a&gt; for nearly five years. The issue was remedied, but it would be naive to think that no breach of data had already occurred.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of a hardcoded secret:&lt;/strong&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="n"&gt;REPORTING_API_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A_HARDCODED_TOKEN_1234&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;report_quality_gate_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;    
        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;REPORTING_API_TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gate_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8081/api/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Status reported successfully.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error reporting status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, a request is made to the &lt;code&gt;http://localhost:8081/api/status&lt;/code&gt; endpoint. The request contains headers with authorization — a very familiar API pattern. Unfortunately, the token has been hardcoded, leaving this code vulnerable to a security breach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved version without a hardcoded secret:&lt;/strong&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;report_quality_gate_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reports the status to an external service.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="c1"&gt;# API token for reporting retrieved from environment.
&lt;/span&gt;        &lt;span class="n"&gt;api_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPORTING_API_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Warning: REPORTING_API_TOKEN environment        
                  variable not set (reporting skipped).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gate_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8081/api/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Ensure successful request.
&lt;/span&gt;            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
                 Status &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gate_status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
                 reported successfully.
                 &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error reporting status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the improved version, the value for &lt;code&gt;REPORTING_API_TOKEN&lt;/code&gt; is retrieved from the operating system environment and never exposed in the code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Click &lt;a href="https://rules.sonarsource.com/python/?search=secrets?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;here&lt;/a&gt; to learn more about handling secrets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Reduce redundancy by embracing the DRY principle
&lt;/h3&gt;

&lt;p&gt;One of the core purposes of programming is to automate repetitive tasks, but that’s not the only reason to reduce redundancy. The more often a task is replicated throughout a system, the greater the vector of vulnerabilities. Redundancy is also harder to maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of redundancy:&lt;/strong&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QualityGateCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error_threshold&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; 
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Combined Threshold Check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; 

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed: Exceeds error threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Passed with errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Passed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;final_status&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;final_status&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;current_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; 
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current_status&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;describe_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
            &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
                          Checking for errors above 
                          &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
                          and warnings above
                          &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.
                          &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above creates a class called &lt;code&gt;QualityGateCheck&lt;/code&gt; initialized with thresholds for errors and warnings, a status, and what kind of check it is. The default status is &lt;code&gt;Pending&lt;/code&gt; and the default check type is &lt;code&gt;Combined Threshold Check&lt;/code&gt;. The class also contains three different functions: one to run the check, one to get the status of the check and one to describe the check.&lt;/p&gt;

&lt;p&gt;The redundancy occurs in a few different places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In &lt;code&gt;run_check&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt; is again set to &lt;code&gt;Pending&lt;/code&gt; — something that is handled already in the instantiation of the class.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the same function, two unnecessary intermediate variables are created and returned: &lt;code&gt;output and&lt;/code&gt; &lt;code&gt;final_status&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As the function is currently written, the last &lt;code&gt;return final_status&lt;/code&gt; will never be reached.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Similar unnecessary intermediate variables occur in the functions &lt;code&gt;get_status&lt;/code&gt; and &lt;code&gt;describe_check&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Improved version with reduced redundancy:&lt;/strong&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QualityGateCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error_threshold&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;  
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Code Error Threshold Check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# Returns status without intermediate variable.
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;describe_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# Returns description without intermediate variable.
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
                   Checking for errors above &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
                   and warnings above &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.
                   &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# Unnecessary status setting and
&lt;/span&gt;            &lt;span class="c1"&gt;# intermediate variables removed.
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check failed: Exceeds error threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check passed with errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check passed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="c1"&gt;# Unreachable return removed.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 How does this improved version further reduce redundancy in the conditional logic?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can extend this concept to deeply nested functions, application configurations, integrated systems, and any other opportunity to &lt;strong&gt;DRY — Don’t Repeat Yourself&lt;/strong&gt;. Strive to identify and eliminate duplication wherever possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define, divide, and decouple responsibilities for maintainability
&lt;/h3&gt;

&lt;p&gt;Speaking of deeply nested functions, the Single Responsibility Principle (SRP) advocates for &lt;strong&gt;reducing complexity where possible by narrowing down classes and modules to a single task&lt;/strong&gt;. We can expand on this principle to include how we define, divide, and decouple services and applications and architecture, ultimately mitigating technical debt.&lt;/p&gt;

&lt;p&gt;In April 2022, &lt;a href="https://www.atlassian.com/blog/atlassian-engineering/post-incident-review-april-2022-outage" rel="noopener noreferrer"&gt;Atlassian experienced a 14 day outage&lt;/a&gt; while trying to delete data related to a deprecated legacy standalone application. In the incident, the API used to perform deletions of accepted IDs related to two entirely different entities, resulting in the removal of instances of the inactive app &lt;em&gt;and&lt;/em&gt; active cloud sites using the application. Customers lost access to their Atlassian products (very likely including their backlogs of tech debt).&lt;/p&gt;

&lt;p&gt;Decoupling the data deletion process for the legacy application from the active cloud sites could have prevented this significant disruption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of a function with multiple responsibilities:&lt;/strong&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main_quality_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;error_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="n"&gt;warning_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="mi"&gt;5&lt;/span&gt;
        &lt;span class="n"&gt;gate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;QualityGateCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No code errors provided&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid code errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check failed: Exceeds error threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check passed with errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check passed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;final_status&lt;/span&gt;

            &lt;span class="n"&gt;api_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A_HARDCODED_TOKEN_1234&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Warning: REPORTING_API_TOKEN
                     environment variable not 
                     set (reporting skipped).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                       &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe_check&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8081/api/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
                         Status &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
                         reported successfully.
                         &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
                         &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error reporting status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;final_status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is &lt;em&gt;a lot&lt;/em&gt; going on in the code above. Not only is the code validating whether or not &lt;code&gt;errors&lt;/code&gt; is an integer, it is also determining the status of the quality gate check as well as posting the resulting gate check status to an endpoint. In other words, there are many different tasks happening in this function — it is overcomplicated, which makes it not only harder to read, but harder to test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved version with separated responsibilities:&lt;/strong&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="c1"&gt;# Handles just the operations pertaining to evaluating the code errors
&lt;/span&gt;    &lt;span class="c1"&gt;# against thresholds.
&lt;/span&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QualityGateCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error_threshold&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Code Error Threshold Check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;describe_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
                   Checking for errors above &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; and warnings
                   above &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.
                   &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check failed: Exceeds error threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check passed with errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check passed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Handles just the operations pertaining to validating the value
&lt;/span&gt;    &lt;span class="c1"&gt;# of code errors and can be used elsewhere in the code.
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;code_errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No code errors provided&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid code errors: Expected an integer.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="c1"&gt;# Handles just the operations pertaining to reporting the gate check status
&lt;/span&gt;    &lt;span class="c1"&gt;# and can be used elsewhere in the code.
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;report_quality_gate_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_description&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;api_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPORTING_API_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Warning: REPORTING_API_TOKEN environment variable not set
                 (reporting skipped).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe_check&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8081/api/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
                 Status &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gate_status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; reported successfully.
                 &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error reporting status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Ties everything together.
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main_quality_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;error_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="n"&gt;warning_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
        &lt;span class="n"&gt;gate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;QualityGateCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;validation_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;validation_result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;validation_result&lt;/span&gt;

        &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;final_status&lt;/span&gt;

        &lt;span class="nf"&gt;report_quality_gate_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                   &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                   &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check_description&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the improved version, the tasks of validating the value for &lt;code&gt;errors&lt;/code&gt;, processing the errors, and then reporting the errors status have been broken into separate functions that are called in &lt;code&gt;main_quality_check&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By dividing responsibilities, we define each task and decouple them from each other, resulting in code that is easier to read, easier to test, easier to maintain, and easier to extend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write effective code comments and documentation
&lt;/h3&gt;

&lt;p&gt;Meaningful comments enhance code readability, but excessive or poorly written ones hinder it. The issue isn’t the length of necessary documentation, especially for public APIs and SDKs which often require multi-line explanations. Rather, a red flag for potential refactoring is when a small block of core logic needs significantly more comment lines to explain &lt;em&gt;what it does&lt;/em&gt; than the code itself. In such instances, refactoring using best practices to create clearer, more self-explanatory code is preferable to relying on verbose comments to decipher complexity. Well-structured and clearly named code often minimizes the need for extensive explanatory comments on its basic operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of verbose comments:&lt;/strong&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="c1"&gt;# The main function to orchestrate the quality check process.
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main_quality_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;error_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="n"&gt;warning_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
        &lt;span class="c1"&gt;# Create an instance of the QualityGateCheck class with the configured
&lt;/span&gt;        &lt;span class="c1"&gt;# thresholds.
&lt;/span&gt;        &lt;span class="n"&gt;gate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;QualityGateCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Validate the input 'errors' using the validate_code_errors function.
&lt;/span&gt;        &lt;span class="n"&gt;validation_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# If the validation returns an error message (not None),
&lt;/span&gt;        &lt;span class="c1"&gt;# return that message and stop further processing.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;validation_result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;validation_result&lt;/span&gt;

        &lt;span class="c1"&gt;# If the input is valid, process the 'errors' using the
&lt;/span&gt;        &lt;span class="c1"&gt;# process_code_errors function to get the final status.
&lt;/span&gt;        &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;process_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                           &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                           &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Update the status of the QualityGateCheck instance
&lt;/span&gt;        &lt;span class="c1"&gt;# with the determined final status.
&lt;/span&gt;        &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;final_status&lt;/span&gt;

        &lt;span class="c1"&gt;# Report the final status using the report_quality_gate_status function, 
&lt;/span&gt;        &lt;span class="c1"&gt;# passing the gate's status and check type.
&lt;/span&gt;        &lt;span class="nf"&gt;report_quality_gate_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
                                   &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                   &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe_check&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="c1"&gt;# Finally, return the determined final status of the quality check.
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;Even&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;glance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;probably&lt;/span&gt; &lt;span class="n"&gt;gives&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;headache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="n"&gt;aren&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;adding&lt;/span&gt; &lt;span class="n"&gt;anything&lt;/span&gt; &lt;span class="n"&gt;helpful&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;Improved version with more useful comments:&lt;/strong&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main_quality_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Orchestrates the quality check process.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

        &lt;span class="n"&gt;error_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="n"&gt;warning_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
       &lt;span class="c1"&gt;# Create a QualityGateCheck with the above values. 
&lt;/span&gt;       &lt;span class="n"&gt;gate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;QualityGateCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error_threshold_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warning_threshold_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;validation_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;validation_result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;validation_result&lt;/span&gt;

        &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;process_code_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                           &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                           &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning_threshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;final_status&lt;/span&gt; &lt;span class="c1"&gt;# Update the gate’s status.
&lt;/span&gt;
        &lt;span class="c1"&gt;# Post the status of the gate check and the type. 
&lt;/span&gt;        &lt;span class="nf"&gt;report_quality_gate_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
                                   &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                   &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe_check&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;gate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version makes use of docstrings and comments to help understand the code without detracting from the code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 IDEs and other tools can utilize these docstrings for improved navigation, debugging, and API documentation generation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use comments judiciously to explain the &lt;em&gt;why&lt;/em&gt; behind the code, not just the what. Meaningful names for variables and functions often reduce the need for extensive commenting. Additionally, leverage docstrings to document the purpose, arguments, and return values of functions and classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maintain consistency and build a lasting code legacy
&lt;/h3&gt;

&lt;p&gt;Regardless of the specific best practices you choose to implement, &lt;strong&gt;consistency is paramount&lt;/strong&gt;. The easiest way to keep code consistent is to enforce a unified style throughout. There are different styles for different programming languages, and even within those coding guidelines there are variations. For instance, indenting with tabs versus spaces. Choose whatever style best suits your use case.&lt;/p&gt;

&lt;p&gt;Maintaining a consistent code style streamlines the adoption of best practices across a project. This stylistic uniformity extends its benefits to several crucial areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistent unit test coverage:&lt;/strong&gt; When code style is consistent, it becomes easier to establish and maintain uniform standards for unit testing. This includes consistent naming conventions for test cases, a consistent structure for test setup and teardown, and a predictable approach to mocking dependencies. This uniformity makes it simpler to identify areas lacking adequate test coverage and ensures that tests are written and executed in a standardized way, leading to more reliable and maintainable tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistent refactoring:&lt;/strong&gt; A consistent code style facilitates safer and more efficient refactoring. When the codebase adheres to a predictable structure and set of conventions, developers can more easily understand the impact of changes and apply refactoring patterns consistently. For example, consistently named variables and methods, along with a uniform code layout, make it easier to identify and extract common logic, rename elements, or restructure code without introducing unintended side effects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistent documentation:&lt;/strong&gt; Consistency in code style naturally promotes consistency in documentation. When code follows a predictable structure and naming scheme, it becomes easier to establish conventions for inline comments, docstrings, and higher-level documentation. This includes a consistent tone, structure, and level of detail. Uniformity in documentation makes it easier for developers to find the information they need, understand the codebase, and contribute effectively.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Furthermore, a consistently styled codebase, coupled with the consistent application of best practices in testing, refactoring, and documentation, significantly eases the onboarding process for new team members. The predictable patterns and structures within the code and its related artifacts enable faster comprehension and integration into the development workflow.”&lt;/p&gt;

&lt;p&gt;In essence, consistent code reflects a &lt;strong&gt;commitment to care, attention to detail, and future maintainability&lt;/strong&gt;. Most software development is a collaborative endeavor, and someone else will likely need to read or modify your code in the future. By prioritizing consistency and writing high quality code, you contribute to a positive and sustainable code legacy. The choices you make in naming variables, refactoring functions, adding comments, and addressing technical debt shape this legacy.&lt;/p&gt;

&lt;p&gt;What kind of legacy you leave is up to you.&lt;/p&gt;

&lt;p&gt;&lt;a id="765d"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From theory to practice: Best practices for implementing coding standards effectively
&lt;/h2&gt;

&lt;p&gt;Now that you understand best practices for writing good code, how do you actually implement them?&lt;/p&gt;

&lt;p&gt;Remember that best practices are guidelines that &lt;strong&gt;enable developers to do their best work collaboratively and efficiently&lt;/strong&gt;; they are not meant to control anyone. They should inspire and empower you, not limit you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learn from others:&lt;/strong&gt; Actively read code from reputable open-source projects to help you learn from other styles, perspectives, and use cases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage automation and shift left:&lt;/strong&gt; Integrate tools for static code analysis, linters, and code reviews into your development workflow as early as possible (shifting left). These tools can automatically identify potential issues and enforce coding standards, minimizing the impact of bad code and freeing you up to think about more complex problems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cultivate curiosity and embrace exceptions:&lt;/strong&gt; Understand the rationale behind coding style enforcements. Occasionally, there might be valid reasons to deviate from a standard, but these exceptions should be conscious and well-documented. The justification for a certain coding style enforcement may seem obvious to you, but can you explain &lt;em&gt;why&lt;/em&gt; it’s so obvious? Occasionally refactoring our brains is just as essential as refactoring our code. Every now and then you might have to break your own rules.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="2a5b"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways: Building a foundation for sustainable software development
&lt;/h2&gt;

&lt;p&gt;Avoiding technical debt is not just a matter of writing “good” code — it is &lt;strong&gt;a continuous commitment to clarity, maintainability, and collaboration&lt;/strong&gt;. By embracing the best practices outlined in this article you can build more robust, scalable, and sustainable software systems.&lt;/p&gt;

&lt;p&gt;Remember that the effort invested in writing good code &lt;em&gt;today&lt;/em&gt; pays significant dividends in reduced maintenance costs, fewer outages, and a more productive and satisfied development team &lt;em&gt;tomorrow&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="8dfe"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself: Tools and resources for enforcing code quality
&lt;/h2&gt;

&lt;p&gt;Understanding these core best practices for writing good code helps lay the foundation for software development with minimal tech debt. As software applications and the technology that supports them become more and more powerful — and therefore more complex — enforcing best practices becomes even more critical.&lt;/p&gt;

&lt;p&gt;When it comes to enforcing code quality at scale, tools like SonarQube integrate seamlessly with existing development pipelines.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Shifting code quality all the way left, &lt;a href="https://www.youtube.com/watch?v=m8sAdYCIWhY?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;SonarQube’s IDE plugin&lt;/a&gt; annotates your code as you write it with reasoning and remediation suggestions that ensure issues don’t even get committed. It’s free and you can &lt;a href="https://docs.sonarsource.com/sonarqube-for-ide/vs-code/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;get started with it right away&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.sonarsource.com/sonarqube-cloud/improving/pull-request-analysis/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;Pull request analysis&lt;/a&gt; helps automate the more basic aspects of code reviews so you can focus on larger issues and catch security vulnerabilities before they’re merged to prod. Check out &lt;a href="https://github.com/Sonar-Demos/python-flask-demo?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;this repo&lt;/a&gt; for hands-on experience using SonarQube.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.sonarsource.com/sonarqube-cloud/standards/overview/?s_category=Organic&amp;amp;s_source=External-Referral&amp;amp;s_origin=medium" rel="noopener noreferrer"&gt;Quality gates and profiles&lt;/a&gt; mean that you set the standards you want to hold yourself to, adjusting and adapting as necessary.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 The code for the examples in this post can be found &lt;a href="https://utm.guru/uinyl" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>codequality</category>
      <category>bestpractices</category>
      <category>sonarqube</category>
    </item>
    <item>
      <title>The DevRel Digest February 2025: DevRel You Should Know for 2025</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Fri, 28 Feb 2025 19:45:06 +0000</pubDate>
      <link>https://dev.to/lizzzzz/the-devrel-digest-february-2025-devrel-you-should-know-for-2025-4753</link>
      <guid>https://dev.to/lizzzzz/the-devrel-digest-february-2025-devrel-you-should-know-for-2025-4753</guid>
      <description>&lt;p&gt;In November and December of 2023, I didn’t know what to write about for the DevRel Digest. When I find myself uncertain about what to do next, I turn to community for inspiration. Community is not only one of the core responsibilities of Developer Relations, it is also a value we practice amongst each other. In turning to my peers for inspiration, it became obvious what to write about: The community itself.&lt;/p&gt;

&lt;p&gt;So I wrote about nine &lt;a href="https://utm.guru/uh7Jy" rel="noopener noreferrer"&gt;“DevRel you should know” for 2024&lt;/a&gt; in &lt;a href="https://utm.guru/uh7Jz" rel="noopener noreferrer"&gt;two parts&lt;/a&gt;. With 2025 already underway and lots of major developments in the field and in tech at large, this next iteration of “DevRel you should know” feels more important than ever. In order to understand why I’ve decided on this particular evolution, I’ve provided the following context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67hhob311camlhuzbfyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67hhob311camlhuzbfyw.png" alt="Senior pug Gary Photoshopped poorly in the Three of Cups" width="800" height="1374"&gt;&lt;/a&gt;&lt;/p&gt;
The &lt;a href="https://utm.guru/uh7LN" rel="noopener noreferrer"&gt;Three of Cups&lt;/a&gt; is about a sense of community and can indicate that it's time to get more involved by helping.



&lt;h2&gt;
  
  
  A quick look back before a look at the future
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AI anxiety&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While AI and its most notable players have been in development for a while, it wasn’t until the December 2022 launch of ChatGPT that the technology became meaningfully accessible. By January 2023, the OpenAI-based chatbot had become one of the &lt;a href="https://utm.guru/uh7JA" rel="noopener noreferrer"&gt;fastest-growing consumer software applications in history&lt;/a&gt;. At the same time, Developer Relations &lt;a href="https://utm.guru/uh7JB" rel="noopener noreferrer"&gt;experienced significant downsizing and layoffs&lt;/a&gt;, prompting many to wonder if the practice of DevRel was in its final long-anticipated and much-written-about death throes.&lt;/p&gt;

&lt;p&gt;While the breadth and ambiguity of DevRel elude explicit success metrics and definitive business value, DevRel’s lack of uniformity and standardization might also be the key to its adaptability and resilience. Combined with a diversity of practitioners from typically unconventional paths to tech and its roots in community, Developer Relations might be the best equipped to survive tech’s ever-shifting landscape. &lt;/p&gt;

&lt;p&gt;With the anxiety of AI, our role as advocates for developers might become more significant than ever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content, clarity, and company-specific needs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://utm.guru/uh7JC" rel="noopener noreferrer"&gt;Content&lt;/a&gt; remains one of the most effective methods of reaching developers. With mainstream search engines becoming more and more polluted with ads and low-quality, inaccurate AI-generated content, the art of technical writing is critical to a product’s success. The point of content for developers should be to make the developer’s life easier – whether that’s presenting a novel solution to the problem they are working on or providing&lt;a href="https://utm.guru/uh7JD" rel="noopener noreferrer"&gt; clear documentation&lt;/a&gt; and quickstart tutorials to get them up and running with as little friction as possible. Bad content dilutes brands; &lt;a href="https://utm.guru/uh7JE" rel="noopener noreferrer"&gt;good content builds trust&lt;/a&gt;. Being able to discern quality in content will always be part of a successful DevRel program.&lt;/p&gt;

&lt;p&gt;High quality content requires clarity not only in its presentation, but in its objectives as well. These goals are where DevRel can collaborate with company organizations that may seem at odds with the empathy and authenticity of a developer audience. With the layoffs of 2023 and the lethargy of the job market in 2024, DevRel had to reconcile with &lt;a href="https://utm.guru/uh7JF" rel="noopener noreferrer"&gt;“aligning with the enemy”&lt;/a&gt; (AKA marketing and sales) in order to evolve. But this doesn’t have to be a compromise. Understanding a specific persona’s marketing funnel can help Developer Advocates gain insight into what kind of efforts to focus on for brand awareness; insight into what actually closes a deal can help a Developer Relations program refine its messaging. In both cases, there is an opportunity to see what success looks like and how to measure it.&lt;/p&gt;

&lt;p&gt;Perhaps one of the silver linings of the “death of DevRel” is &lt;a href="https://utm.guru/uh7JG" rel="noopener noreferrer"&gt;its renaissance&lt;/a&gt;, in which both companies and DevRel practitioners are asking themselves, “Does Developer Relations make sense for this company at this time? What does the company need and how can a DevRel program meet that need?” DevRel has been making a comeback, this time with a lot more discernment. The State of DevRel Report for 2024 &lt;a href="https://utm.guru/uh7JH" rel="noopener noreferrer"&gt;reflects this&lt;/a&gt;, with respondents reporting more hiring than in the previous year; however, there was also an increase in expectations for head counts to decrease as company reorganizations became more prevalent than in previous surveys. &lt;/p&gt;

&lt;p&gt;In other words, DevRel was still volatile in 2024 as companies figured out how best to reach developers.&lt;/p&gt;

&lt;p&gt;With this in mind, how should we think about Developer Relations in 2025? To answer this question, I sought out the wisdom and experience of two prominent members of the community who have gone above and beyond to help raise us all up. They kindly donated their time to me for this post, offering perspectives and insights that will resonate with many working in tech right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  DevRel you should know: Wesley Faulkner and Erin Mikail Staples
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgl9tx60spjv4nocm1inx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgl9tx60spjv4nocm1inx.png" alt="A headshot of Wesley Faulkner" width="800" height="962"&gt;&lt;/a&gt;&lt;/p&gt;
Wesley Faulkner worked in hardware and marketing before finding his “home” in DevRel.



&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Wesley Faulkner&lt;/strong&gt; is one of the most visible and active members of the DevRel community. A self-described “technology nerd,” Wesley started his career as a hardware technician for Dell, receiving annual promotions and steadily leveling up from tech support for desktops to tech support for government and education, finally moving over to servers and storage before leaving the company. It was easy to be successful because he was good at what he did, but there was a yearning for something else. “A lot of the movements that I made were usually out of frustration,” he tells me. Those movements would end up including social media management, marketing, community, and moderation at a variety of companies.&lt;/p&gt;

&lt;p&gt;From the outside, such a career path may seem a little unconventional, but for Wesley, it led him right to where he needed to be. “It was a friend of mine who knew of my background – knew my technical background, my marketing background – that said, ‘Hey, we're doing this thing called DevRel, would you be interested?’ I interviewed and I got the job,” Wesley says, summarizing it as “an evolution of everything I had done beforehand.” But it wasn’t until his attendance to DevRelCon in 2019 that Wesley realized he had found his calling. “I was like, ‘I'm home,’” he says.&lt;/p&gt;

&lt;p&gt;“Luckily that was generally the start of the Golden Age of DevRel where salaries went up, demand went up, and I felt like I had more agency, more control.”&lt;/p&gt;

&lt;p&gt;Echoing a sentiment many of us are feeling right now, he adds, “Things don’t feel that way now.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnacarkpyua88lrvb6iqm.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnacarkpyua88lrvb6iqm.jpeg" alt="A headshot of Erin Mikail Staples" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
Erin Mikail Staples initially wanted to work in politics and journalism, but after gaining some technical experience, found herself in DevRel.

 

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Erin Mikail Staples&lt;/strong&gt; shares a similar career journey. Erin started with aspirations to work in politics. “My first internships were all in nonprofit and policy and court reporting and not techy things,” she tells me, and like Wesley, she experienced restlessness. “I naturally got really annoyed with the news industry.”&lt;/p&gt;

&lt;p&gt;She decided to pursue something more creative. She started working at an agency with an interest in advertising. While she was there, she found herself working in the Shopify ecosystem and her natural curiosity led her to peel back the layers to the code underneath. “I was the person in my friend group that did the MySpace, LiveJournal, and Tumblr templates,” she says – an experience I think a lot of us of a certain generation can relate to.&lt;/p&gt;

&lt;p&gt;Voicing another sentiment that resonates deeply with me, she adds, “Bring sparkle gifs back to the internet please.”&lt;/p&gt;

&lt;p&gt;Erin next worked a little in progressive tech and then went to grad school to study the impacts of machine learning and algorithms. It was there that she attended a talk from a speaker about the Cambridge Analytica scandal and experienced a deep sense of disillusionment. Her adviser convinced her to not drop out of the program, offering her this reframe: “If you can't take down the system then how do you empower the individual?”&lt;/p&gt;

&lt;p&gt;From there, Erin’s curiosity and sense of community led her to a COVID-inspired “weird viral moment” in which she was named a “&lt;a href="https://utm.guru/uh7JI" rel="noopener noreferrer"&gt;virtual mall CEO&lt;/a&gt;.” That’s how she ended up as a head of an open source community and upleveling her technical chops. It was only natural for her next step to be developer relations at Orbit.&lt;/p&gt;

&lt;p&gt;“Chaos,” is how she initially describes her career to me. Chaos is exactly the kind of qualification you need to succeed in a role that varies so greatly from company to company.&lt;/p&gt;

&lt;h2&gt;
  
  
  Neurodivergence and the best and worst of DevRel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://utm.guru/uh7JJ" rel="noopener noreferrer"&gt;22% of DevRel practitioners identify as neurodivergent&lt;/a&gt;, mirroring the &lt;a href="https://utm.guru/uh7JK" rel="noopener noreferrer"&gt;10-20% of the global population considered to be of the same identity&lt;/a&gt;. This suggests that DevRel is particularly suited for neurodivergent people and that DevRel is one of the more inclusive roles in tech. Which makes sense given the breadth of what DevRel includes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvyu6la2cn86vu987447.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvyu6la2cn86vu987447.png" alt="An excerpt from the State of DevRel Report for 2024 breaking down the percentages of underrepresented people in Developer Relations" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;
From the &lt;a href="https://utm.guru/uh7JJ" rel="noopener noreferrer"&gt;State of DevRel Report for 2024&lt;/a&gt;



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Both Wesley and Erin identify as neurodivergent; Wesley has dyslexia and ADHD and Erin has OCD and ADHD. As someone diagnosed with ADHD myself, I recognized the many experiences we have in common.&lt;/p&gt;

&lt;p&gt;“I was very impulsive,” says Erin of her experience in school, “I would get up and walk around in the class. But I had learned that teachers wouldn't get me in trouble if I got good grades … so I would speedrun through my work and then wander the halls.”&lt;/p&gt;

&lt;p&gt;There can be many professional challenges to being neurodivergent. Wesley says, “They don't fully understand how I knew the dominoes were going to fall that way because I set them up that way,” adding that his unconventional approach is often questioned. “They say, ‘Well, why are you putting that there or why are you doing that or why are you doing this?’ They may not understand, but to me it's clear.”&lt;/p&gt;

&lt;p&gt;“And that's the restrictions that I fall under time and time again.”&lt;/p&gt;

&lt;p&gt;When I ask Wesley if he feels that his neurodivergence has helped or hindered his work in Developer Relations, he simply says, “Yes.”&lt;/p&gt;

&lt;p&gt;Similarly, Wesley names “the ambiguity” as both the best and worst parts of DevRel. “This is a two-sided coin,” he explains. When it comes to DevRel, “there is a lot that's not known and there's a lot that's not settled. I love being able to explore those areas where there is a potential to make change, to set a course and to make impact without being criticized about &lt;em&gt;how&lt;/em&gt; I did something but rather that I &lt;em&gt;did it&lt;/em&gt; … I like making order out of chaos.”&lt;/p&gt;

&lt;p&gt;Erin says the worst parts of DevRel are when “people perceive you as less of an engineer … I hate feeling like I have to prove my worth to have a seat at the table.” She adds, “There's this whole notion of going deep versus going wide in education. And I think in DevRel, you have to go very wide.”&lt;/p&gt;

&lt;p&gt;“And that doesn't mean we can't keep pace with someone going deep, but I think in DevRel you have to be a number one super user of your product” because ultimately, as a VP of engineering told Erin, “I don't think any of these developers at this company could give a live product demo tomorrow.” But Erin could.&lt;/p&gt;

&lt;p&gt;Which is where she finds context switching and pattern matching – two typical traits of neurodivergence – useful.&lt;/p&gt;

&lt;p&gt;Wesley credits his way of thinking for his success in DevRel, saying, “In order for me to make things make sense in my mind, I really fall back on first principles thinking – understanding the basics and then building up from there … I just follow where the data takes me and what makes sense from the feedback that I'm getting. Which means I constantly rejigger or re-tweak based on what I'm hearing, what I'm getting.”&lt;/p&gt;

&lt;p&gt;This sort of “systems thinking” is what helps Wesley ask the right questions. “If someone's telling me, ‘Hey, let's do a blog series,’ I say, ‘Well, are there going to be ads? How are we promoting it?’ All the pieces,” he says, adding, “I'm able to try to get everything in – sequenced and in pieces so that it makes sense. So that I know that when I step out with my left foot, there is something for me to step &lt;em&gt;on&lt;/em&gt;. And when I step out there with my right foot, there's also something for me to step on.”&lt;/p&gt;

&lt;p&gt;This kind of thinking is useful in other ways. Erin says she feels like she is in “a much more problem solving role [in DevRel] where I'm influencing products and I'm influencing how we communicate and I'm influencing how we talk to people – which is a very cool thing … It's very creative. I get to learn a ton of new things – which is so much fun.”&lt;/p&gt;

&lt;h2&gt;
  
  
  What to focus on for DevRel in 2025
&lt;/h2&gt;

&lt;p&gt;I asked Wesley and Erin for their advice on what DevRel should focus on in 2025.&lt;/p&gt;

&lt;p&gt;This is what Wesley has to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I think to each their own. We have different focuses. This is the thing that I would say that I lean into. I focus on problems and then try to focus on solutions.”&lt;/p&gt;

&lt;p&gt;“DevRel has not talked about the definition [of DevRel] and how it's being owned by other people who aren't necessarily DevRel practitioners. So that's why I joined the steering committee of the &lt;a href="https://utm.guru/uh7JL" rel="noopener noreferrer"&gt;DevRelFoundation&lt;/a&gt; to help with fixing that problem.”&lt;/p&gt;

&lt;p&gt;“I hate the way companies operate and how they treat people. I'm writing a book, creating a brand new work structure that doesn't even exist yet to fix this. I'd say that if people want to follow what I'm focusing on, it’s to think bold and go ahead and take on the big things that you think will withstand the test of time. Then leave your artifacts to show that there's another way.”&lt;/p&gt;

&lt;p&gt;“Even if you don't think that it's going to be successful, let people draw upon your work, draw upon your energy. If it's not [the right solution] now, then make sure that the people in the future have what they need to to have the evidence that other people believe that this is the right thing to do.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, trust your intuition, test your hypothesis, and document everything just in case it happens to be the foundation that someone else can use to complete a vision for a better way.&lt;/p&gt;

&lt;p&gt;Erin offers some practical advice for “a better way”:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Get good at products and understand business models … Having done my research on independent business models I can tell you that every time I've been laid off, I have predicted it. This is because you learn about what starts happening. And so you understand how your company makes business and what percentage of your company gets from different inputs and outputs.”&lt;/p&gt;

&lt;p&gt;“Understand the business model and then also understand the product. One of my professors did educational design for Apple. He taught a class on UX for education and accessibility and he made us use screen readers. We would go into class with our laptops and he’d be like, ‘Great, turn your screen off and use a screen reader with headphones.’” We used different adaptive technologies to understand that 1) Not everything can be universal so 2) How do you maximize that and what are the product decisions that go into fueling these types of things?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;She adds, “And don't be scared of talking to sales!”&lt;/p&gt;

&lt;p&gt;In summary, understanding business models and user experience can go a long way in not only serving developers, but in serving yourself and your value at a company – and sales can be your greatest ally in understanding the business model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combatting uncertainty with community
&lt;/h2&gt;

&lt;p&gt;I found myself searching for a job (again!) at the end of 2024 and into the beginning of 2025. The world of tech is much different now and it feels like there is a lot more at stake. When I wrote about “&lt;a href="https://utm.guru/uh7J6" rel="noopener noreferrer"&gt;the return of DevRel&lt;/a&gt;,” I felt hopeful and optimistic about this work. Now I am not as confident and I need community more than ever. I know I am not the only one.&lt;/p&gt;

&lt;p&gt;To this point, Wesley shares a little inspiration. He says his love of technology was inspired by the vision he saw in &lt;em&gt;Star Trek&lt;/em&gt;, saying, “Like a lot of sci-fi addicts, what I loved about &lt;em&gt;Star Trek&lt;/em&gt; is not just the future [it presents], but this kind of evolution of humanity into this &lt;em&gt;good&lt;/em&gt; space … And I wanted to be part of that evolution, both with technology and this kind of racial and gender dynamic in which there was harmony.”&lt;/p&gt;

&lt;p&gt;He sees that evolution and harmony in DevRel – a group of people from diverse backgrounds and experiences who love technology and want to share that love with others. Reflecting on his experience at &lt;a href="https://utm.guru/uh7JM" rel="noopener noreferrer"&gt;DevRelCon in London in 2023&lt;/a&gt;, Wesley says, “It felt like it didn’t matter what team we're representing, it didn't matter what company we worked for, it still felt as if we were all connected. People change jobs all the time, but we all felt like we still could come &lt;em&gt;home&lt;/em&gt; to each other.”&lt;/p&gt;

&lt;p&gt;Wesley also admits to feeling disillusioned. He says, “If you do the right thing, if you're a very moral, ethical person, that doesn't mean that you'll win in the end. That's not something I would like to be true, but it doesn't bear out to be true.” What has prevented him from losing all faith is community.&lt;/p&gt;

&lt;p&gt;Not surprisingly, both Wesley and Erin are members of many different communities. Erin finds her sense of belonging from places like the &lt;a href="https://utm.guru/uh7JN" rel="noopener noreferrer"&gt;NY comedy scene&lt;/a&gt;, online fandoms, the subreddit for her neighborhood that she moderates, the dog park she regularly visits with her pup, Q, and even former teammates from her time as a swimmer in college that she’s maintained contact with. For Wesley, his communities include his friends and family, his neighbors, the various DevRel-related online networks on Discord, LinkedIn, and Slack, the listeners of his podcasts &lt;a href="https://utm.guru/uh7JO" rel="noopener noreferrer"&gt;Radical Respect&lt;/a&gt; and &lt;a href="https://utm.guru/uh7JQ" rel="noopener noreferrer"&gt;Community Pulse&lt;/a&gt;, and even the temporary communities he experiences when &lt;a href="https://utm.guru/uh7JQ" rel="noopener noreferrer"&gt;giving talks and connecting with audiences&lt;/a&gt;.    &lt;/p&gt;

&lt;p&gt;Wesley talks about the “reinforcement” of community he feels whenever he learns someone has found his work or his voice valuable. “That's kind of my love language. And as long as I feel I can still [provide value], it makes me feel good.” In other words, knowing that he’s made some positive contribution to the world – big or small – is what continues to motivate Wesley.&lt;/p&gt;

&lt;p&gt;At DevRelCon, he gave out challenge coins that on one side read, “&lt;strong&gt;Integrity, resilience, accountability, perseverance, community&lt;/strong&gt;” and “&lt;strong&gt;Community of leaders and community of learners&lt;/strong&gt;” on the other.&lt;/p&gt;

&lt;p&gt;Wesley says, “These are the principles that I try to live by when I talk about DevRel.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn62ao42pflp1saks3unw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn62ao42pflp1saks3unw.jpg" alt="A photo of the DevRel challenge coin" width="419" height="419"&gt;&lt;/a&gt;&lt;/p&gt;
One of the challenge coins Wesley gave out at DevRelCon.






&lt;p&gt;You can learn more about &lt;strong&gt;Wesley Faulkner&lt;/strong&gt; on &lt;a href="https://utm.guru/uh7Ka" rel="noopener noreferrer"&gt;his website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can learn more about &lt;strong&gt;Erin Mikail Staples&lt;/strong&gt; on &lt;a href="https://utm.guru/uh7JR" rel="noopener noreferrer"&gt;her website&lt;/a&gt;, as well as on &lt;a href="https://utm.guru/uh7JS" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://utm.guru/uh7JT" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, and &lt;a href="https://utm.guru/uh7JV" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;. She currently works as a Senior Developer Experience Engineer for &lt;a href="https://utm.guru/uh7Kd" rel="noopener noreferrer"&gt;Galileo&lt;/a&gt;, a platform for enterprise gen-AI evaluation and observability.  &lt;/p&gt;

&lt;p&gt;Many thanks to Wesley and Erin for their generous contributions to this post. If you enjoyed this post, please consider &lt;a href="https://utm.guru/uh7JW" rel="noopener noreferrer"&gt;buying me a coffee&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Events and resources and other notable things
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Check out the new DeveloperRelations.com website and leave your feedback &lt;a href="https://utm.guru/uh7JX" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Love this&lt;a href="https://utm.guru/uh7JY" rel="noopener noreferrer"&gt; LinkedIn post on testing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Check out this upcoming &lt;a href="https://utm.guru/uh7JZ" rel="noopener noreferrer"&gt;Write the Docs meetup in San Francisco on March 19th&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Need help understanding agentic RAG, one of the newest developments in the AI space? Look no further than &lt;a href="https://utm.guru/uh7Jx" rel="noopener noreferrer"&gt;this blog post I wrote for Vellum AI&lt;/a&gt;, where I try to break it down into accessible language.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devrel</category>
    </item>
    <item>
      <title>Harder, Better, Faster, Stronger Tests With Fixtures</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Tue, 31 Dec 2024 23:39:47 +0000</pubDate>
      <link>https://dev.to/lizzzzz/harder-better-faster-stronger-tests-with-fixtures-105c</link>
      <guid>https://dev.to/lizzzzz/harder-better-faster-stronger-tests-with-fixtures-105c</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/gAjR4_CbPpQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Okay so I really just wanted to reference Daft Punk. With their &lt;a href="https://utm.guru/uhSY1" rel="noopener noreferrer"&gt;December 2024 limited re-release of &lt;em&gt;Discovery&lt;/em&gt; and screening of &lt;em&gt;Interstella 5555&lt;/em&gt;&lt;/a&gt;, the French electronic duo have been on my mind. Of course it has made me nostalgic and a little bit sad, yearning for days that seemed simpler.&lt;/p&gt;

&lt;p&gt;It is not easy to be unemployed &lt;em&gt;and&lt;/em&gt; depressed during the holidays, so guess what? Here’s a tutorial for you to round out what was probably the worst year of my life! (Including the year I was diagnosed with cancer!)&lt;/p&gt;

&lt;p&gt;But real talk: Test fixtures &lt;em&gt;can&lt;/em&gt; improve your tests by reducing redundancy, isolating scenarios, and increasing performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use this tutorial
&lt;/h3&gt;

&lt;p&gt;This tutorial is designed to accommodate many different learning styles so you can choose your own adventure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://utm.guru/uhSYU" rel="noopener noreferrer"&gt;&lt;strong&gt;Jump straight to the code&lt;/strong&gt;&lt;/a&gt;: The code for this tutorial can be found &lt;a href="https://utm.guru/uhSYZ" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Use the README to get it up and running locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;But first, who cares about testing?&lt;/strong&gt;: I’ll try to convince you why writing tests can be fun and list some best practices to keep in mind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What are test fixtures?&lt;/strong&gt;: An introduction to test fixtures in unittest and how they can help you adhere to unit testing best practices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A little bit of context&lt;/strong&gt;: If you’re the kind of person who likes to ask a lot of questions or who finds comfort in expectation-setting, I got you, my sweet little anxious overachiever! This section aims to help set you up for success. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test fixtures in action&lt;/strong&gt;: A walk-through of the tutorial code in which we’ll explore the effects of using different kinds of test fixtures so you can experience that “&lt;em&gt;Aha!&lt;/em&gt;” moment first-hand.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="testing-matters"&gt;But first, who cares about testing? Can’t AI do it?&lt;/h2&gt;

&lt;p&gt;First of all, &lt;em&gt;I&lt;/em&gt; care about testing.&lt;/p&gt;

&lt;p&gt;Second of all, yes … but unless you are a sadist who enjoys incident pages at three in the morning trying to debug code a robot wrote, you should probably at least check the robot’s work. (And – you know – no shame if that &lt;em&gt;is&lt;/em&gt; your thing.)&lt;/p&gt;

&lt;p&gt;You can read my previous posts in this series to learn more about why testing is important, but in summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Besides the qualitative benefits of software testing such as bug prevention, reduction in development costs, and improved performance, the most compelling benefit of writing tests is it makes us better engineers. &lt;strong&gt;Writing tests forces us to ask ourselves, “What exactly is the expected behavior of this method or application?”&lt;/strong&gt; When software becomes “difficult to test,” it is usually a good indicator of code smells and an opportunity to refactor a method or reconsider the entire design of a system.&lt;/li&gt;
&lt;li&gt;A less obvious but still important benefit to writing tests – unit tests especially – is their double duty as quick documentation. Best practices for unit tests call for long, descriptive function names. &lt;strong&gt;These function names not only make verbose test output more readable and quick to assess,&lt;/strong&gt; &lt;strong&gt;they also provide documentation for the function under test&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Writing tests can be fun. Yeah, you read that correctly. A well-constructed test suite can be just as satisfying a problem to solve as the application code itself. And that feeling when all your tests pass? Or when your tests assist in a smooth refactor or feature implementation? It feels &lt;em&gt;good&lt;/em&gt;. &lt;strong&gt;Tests can be a quick dopamine win in a profession that can be fraught with midnight debugging sessions and bouts of imposter syndrome&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unit testing best practices
&lt;/h3&gt;

&lt;p&gt;Now that I’ve successfully convinced you of the benefits of writing tests and you are now sufficiently stoked, here are some unit test best practices to keep in mind.&lt;/p&gt;

&lt;p&gt;A good unit test should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One specific assertion at a time&lt;/li&gt;
&lt;li&gt;Independent, isolated, and controlled&lt;/li&gt;
&lt;li&gt;Relevant and meaningful&lt;/li&gt;
&lt;li&gt;Repeatable and deterministic&lt;/li&gt;
&lt;li&gt;​​Automatic&lt;/li&gt;
&lt;li&gt;Descriptive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mpzmmfxyhx6zbt5ee03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mpzmmfxyhx6zbt5ee03.png" alt="Senior pug Gary Photoshopped poorly as the Judgement tarot card" width="800" height="1381"&gt;&lt;/a&gt;&lt;/p&gt;
The tarot card for &lt;a href="https://utm.guru/uhSY9" rel="noopener noreferrer"&gt;Judgement&lt;/a&gt; seems appropriate for software testing, so here is senior pug Gary Photoshopped poorly as an angel -- naturally.



&lt;h2 id="what-are-test-fixtures"&gt;What are test fixtures?&lt;/h2&gt;

&lt;p&gt;“&lt;a href="https://utm.guru/uhSYV" rel="noopener noreferrer"&gt;In the context of software, a test fixture (also called "test context") is used to set up the system state and input data needed for test execution&lt;/a&gt;.” The purpose of a test fixture is to establish the environment in which the test(s) will be run. Test fixtures can help tests adhere to our unit testing best practices by controlling for variables like databases and data sets, system state, operating system, specific files, and mocks.&lt;/p&gt;

&lt;p&gt;Specifically, in Python’s unittest framework, test fixtures are functions or methods that are executed before or after a test or group of tests to establish a testing environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Class and method-level test fixtures
&lt;/h3&gt;

&lt;p&gt;Class and method-level fixtures are provided by a TestCase instance and are part of the group of methods concerned with running tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class-level test fixtures&lt;/strong&gt; are methods that are executed either before and/or after &lt;em&gt;all of the test methods&lt;/em&gt; in a TestCase instance. They look 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;
@classmethod
def setUpClass(cls):
    print("Class-level setup test fixture has been executed!")

@classmethod
def tearDownClass(cls):
    print("Class-level tear down test fixture has been executed!")

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

&lt;/div&gt;



&lt;p&gt;An example implementation might look 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;
import unittest

class ExampleTestCaseClassTestFixtures(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("Class-level setup test fixture has been executed!")

    @classmethod
    def tearDownClass(cls):
        print("Class-level teardown test fixture has been executed!")

    def test_example_equal(self):
        self.assertEqual(1 + 1, 2)

    def test_example_not_equal(self):
        self.assertNotEqual(1 + 1, 3)

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

&lt;/div&gt;



&lt;p&gt;If you were to run the above tests, the output you would get might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class-level setup test fixture has been executed!

test_example_equal (tests.test_example.ExampleTestCaseClassTestFixtures.test_example_equal) ... ok

test_example_not_equal (tests.test_example.ExampleTestCaseClassTestFixtures.test_example_not_equal) ... ok

Class-level teardown test fixture has been executed!

----------------------------------------------------------------------

Ran 2 tests in 0.000s

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

&lt;/div&gt;



&lt;p&gt;💡 This example is provided &lt;a href="https://utm.guru/uhTm7" rel="noopener noreferrer"&gt;in the repo&lt;/a&gt; and as long as you are on the &lt;code&gt;fixtures-tutorial&lt;/code&gt; branch you can run it from the root directory with: &lt;code&gt;python -m unittest tests.examples.test_test_case_fixtures_example.ExampleTestCaseClassTestFixtures -v&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method-level test fixtures&lt;/strong&gt; are methods that are executed either before and/or after &lt;em&gt;each test method&lt;/em&gt; in a TestCase instance. They look 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;    def setUp(self):
        print("Method-level setup test fixture has been executed!")

    def tearDown(self):
        print("Method-level teardown test fixture has been executed!")

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

&lt;/div&gt;



&lt;p&gt;An example implementation might look 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;class ExampleTestCaseMethodTestFixtures(unittest.TestCase):
    def setUp(self):
        print("Method-level setup test fixture has been executed!")

    def tearDown(self):
        print("Method-level teardown test fixture has been executed!")

    def test_example_equal(self):
        self.assertEqual(1 + 1, 2)

    def test_example_not_equal(self):
        self.assertNotEqual(1 + 1, 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you were to run the above tests, the output you would get might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_example_equal (tests.test_example.ExampleTestCaseMethodTestFixtures.test_example_equal) ... Method-level setup test fixture has been executed!

Method-level teardown test fixture has been executed!

ok

test_example_not_equal (tests.test_example.ExampleTestCaseMethodTestFixtures.test_example_not_equal) ... Method-level setup test fixture has been executed!

Method-level teardown test fixture has been executed!

ok

----------------------------------------------------------------------

Ran 2 tests in 0.000s

OK

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

&lt;/div&gt;



&lt;p&gt;Notice how in this output the setup and teardown messages are repeated twice – once for each of the two test methods in the test case. &lt;/p&gt;

&lt;p&gt;💡 This example is provided &lt;a href="https://utm.guru/uhTm7" rel="noopener noreferrer"&gt;in the repo&lt;/a&gt; and as long as you are on the &lt;code&gt;fixtures-tutorial&lt;/code&gt; branch you can run it from the root directory with: &lt;code&gt;python -m unittest tests.examples.test_test_case_fixtures_example.ExampleTestCaseMethodTestFixtures -v&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Module-level test fixtures
&lt;/h3&gt;

&lt;p&gt;Module-level test fixtures are functions that are executed before and/or after &lt;em&gt;all the tests in a module&lt;/em&gt; are run. These fixtures are typically used for setting up and tearing down resources that are shared across multiple tests within a module. They look 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;def setUpModule():
    print("Module-level setup test fixture has been executed!")

def tearDownModule():
    print("Module-level teardown test fixture has been executed!")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An example implementation might look 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;import unittest

def setUpModule():
    print("Module-level setup test fixture has been executed!")

def tearDownModule():
    print("Module-level teardown test fixture has been executed!")

class ExampleTestCaseSecond(unittest.TestCase):

    def test_example_equal(self):
        self.assertEqual(1 + 1, 2)

    def test_example_not_equal(self):
        self.assertNotEqual(1 + 1, 3)

class ExampleTestCaseFirst(unittest.TestCase):

    def test_example_equal(self):
        self.assertEqual(1 + 1, 2)

    def test_example_not_equal(self):
        self.assertNotEqual(1 + 1, 3)

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

&lt;/div&gt;



&lt;p&gt;If you were to run the above tests, the output you would get might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Module-level setup test fixture has been executed!

test_example_equal (tests.examples.test_module_fixtures_example.ExampleTestCaseFirst.test_example_equal) ... ok

test_example_not_equal (tests.examples.test_module_fixtures_example.ExampleTestCaseFirst.test_example_not_equal) ... ok

test_example_equal (tests.examples.test_module_fixtures_example.ExampleTestCaseSecond.test_example_equal) ... ok

test_example_not_equal (tests.examples.test_module_fixtures_example.ExampleTestCaseSecond.test_example_not_equal) ... ok

Module-level teardown test fixture has been executed!

----------------------------------------------------------------------

Ran 4 tests in 0.000s

OK

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

&lt;/div&gt;



&lt;p&gt;I’m sure you’ve already noticed how this output differs from the prior two examples because you’re smart like that!&lt;/p&gt;

&lt;p&gt;💡 This example is provided &lt;a href="https://utm.guru/uhTm7" rel="noopener noreferrer"&gt;in the repo&lt;/a&gt; and as long as you are on the &lt;code&gt;fixtures-tutorial&lt;/code&gt; branch you can run it from the root directory with: &lt;code&gt;python -m unittest tests.examples.test_module_fixtures_example  -v&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now that you’ve got a basic understanding of test fixtures, read on to see them in action.&lt;/p&gt;

&lt;p&gt;To read more about test fixtures in unittest, refer to the documentation &lt;a href="https://utm.guru/uhSYW" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="context"&gt;A little bit of context&lt;/h2&gt;

&lt;p&gt;You can find the code for this tutorial in this repo on the &lt;code&gt;fixtures-tutorial&lt;/code&gt; &lt;a href="https://utm.guru/uhSYZ" rel="noopener noreferrer"&gt;branch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Build-a-Pug is a Flask app that makes a call to an &lt;a href="https://utm.guru/uhSYT" rel="noopener noreferrer"&gt;OpenAI endpoint&lt;/a&gt; to generate an image of a pug based on a prompt constructed from user provided input. For this iteration of Build-a-Pug, I’ve added a &lt;a href="https://utm.guru/uhSYP" rel="noopener noreferrer"&gt;SQLite&lt;/a&gt; database to store the pugs that are built which can be retrieved via the &lt;code&gt;See Your Grumble&lt;/code&gt; page (Because that is what a group of pugs is called – a “grumble”!).&lt;/p&gt;

&lt;p&gt;SQLite is an embedded SQL database engine. Unlike most other SQL databases, SQLite does not have a separate server process, and it reads and writes directly to ordinary disk files. For the sake of this tutorial, you do not need to concern yourself too much with the inner workings of SQLite. All you need to know is that initializing the database requires an extra explicit step and that the database manifests as a single &lt;code&gt;.sqlite&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caveats and troubleshooting
&lt;/h3&gt;

&lt;p&gt;Because OpenAI provides access to generated images for a limited duration, depending on when you view your grumble, some images may return an invalid signature authentication error which will appear as broken images. This is because the images are not being saved anywhere and implementing that functionality felt out of scope for this particular tutorial.&lt;/p&gt;

&lt;p&gt;To fix this, you can delete the database and initialize a new one. This will, however, mean all your pugs will be permanently lost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Please don’t deploy this app to production anywhere
&lt;/h3&gt;

&lt;p&gt;This app was initially created as a toy demo app. As I’ve iterated on it, it’s become clear that it needs to be refactored. Obviously it isn’t intended for production, nor is it a good example of how to properly implement a database, however it &lt;em&gt;does&lt;/em&gt; successfully demonstrate how and why test fixtures are useful – especially when you start adding complexity (like databases). Use this app as a learning resource and who knows? Maybe I’ll do a tutorial on refactoring!&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use the test code
&lt;/h3&gt;

&lt;p&gt;The most successful learning happens when you achieve that “&lt;em&gt;Aha!&lt;/em&gt;” experience. I liken it to a magic trick: It’s the moment of wonder and delight when your brain is not only pleasantly surprised, but intrigued. It invites playful curiosity. To try to recreate that experience, I have commented out sections of the test code for you to later uncomment and run so you can see for yourself how different test fixtures impact the test results.&lt;/p&gt;

&lt;h2 id="in-action"&gt;Test fixtures in action&lt;/h2&gt;

&lt;p&gt;Build-a-Pug is a Flask app that makes a call to an &lt;a href="https://utm.guru/uhSYT" rel="noopener noreferrer"&gt;OpenAI endpoint&lt;/a&gt; to generate an image of a pug based on a prompt constructed from user provided input. For this iteration of Build-a-Pug, I’ve added a &lt;a href="https://utm.guru/uhSYP" rel="noopener noreferrer"&gt;SQLite&lt;/a&gt; database to store the pugs that are built which can be retrieved via the See Your Grumble page (Because that is what a group of pugs is called – a “grumble”!).&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://utm.guru/uhSYS" rel="noopener noreferrer"&gt;pipenv&lt;/a&gt;: &lt;code&gt;pip install pipenv --user&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://utm.guru/uhSYT" rel="noopener noreferrer"&gt;OpenAI API key and organization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python 3+&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repo: &lt;code&gt;git clone https://github.com/liz-acosta/testing-strategies-for-python.git&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Change directory to the project&lt;/li&gt;
&lt;li&gt;Check out the &lt;code&gt;fixtures-tutorial&lt;/code&gt; branch: &lt;code&gt;git checkout fixtures-tutorial&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Install dependencies from Pipfile.lock: &lt;code&gt;pipenv install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;a href="https://utm.guru/uhSYQ" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt; by renaming &lt;code&gt;.env_template&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt; ...&lt;/li&gt;
&lt;li&gt;... and replacing placeholder secrets with real secrets&lt;/li&gt;
&lt;li&gt;Initialize the SQLite database: &lt;code&gt;pipenv run init-db&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Optional: Delete the database: &lt;code&gt;pipenv run delete-db&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Run the app locally
&lt;/h3&gt;

&lt;p&gt;While you don’t need to run the app for the tutorial, it could be helpful to understand the tests.&lt;/p&gt;

&lt;p&gt;There are some caveats to this app, see the Caveats and troubleshooting section to learn more. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To run the app locally: &lt;code&gt;pipenv run start-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;http://localhost:5000/&lt;/code&gt; in your browser&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should get something that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8p85uwr59rizyp36q8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8p85uwr59rizyp36q8q.png" alt="A screenshot of the Build-a-Pug app" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, you can build your own pug:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc0m9far8llyrghzlzb9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc0m9far8llyrghzlzb9.png" alt="Screenshot of a created pug named Fiona" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn more about pugs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsaw2o3894dmnb12ut4iz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsaw2o3894dmnb12ut4iz.png" alt="A screenshot of the Pug Facts page" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or check out your grumble:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6sisurhmug29qs79ulcf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6sisurhmug29qs79ulcf.png" alt="A screenshot of a grumble of two pugs" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the tests
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Run the tests with method-level test fixtures
&lt;/h4&gt;

&lt;p&gt;The tests we are interested in are located in &lt;a href="https://utm.guru/uhSZ5" rel="noopener noreferrer"&gt;&lt;code&gt;tests/unit/test_pug.py&lt;/code&gt;&lt;/a&gt; – and in particular, we want to take a look at the tests that pertain to the database operations.&lt;/p&gt;

&lt;p&gt;Locate the test case called &lt;code&gt;TestPugDBWithMethodLevelFixtures&lt;/code&gt; and take a look at what the code is doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# A method-level test fixture that
# creates and inserts data into a sqlite database before each test in this class
def setUp(self):
    """Create a test database before each test method in this class"""

    self.connection = sqlite3.connect(TEST_DATABASE_FILEPATH)
    self.connection.row_factory = sqlite3.Row

    test_pug_lily = Pug("Lily", "6", "San Francisco", "4:00 PM")
    test_pug_lily.description = "Lily is the best pug"
    test_pug_lily.image = "lily_pug.jpg"

    test_pug_fiona = Pug("Fiona", "2", "San Francisco", "4:00 PM")
    test_pug_fiona.description = "Fiona is the best pug"    
    test_pug_fiona.image = "sweet_fiona.jpg"
    test_pugs = [test_pug_lily, test_pug_fiona]

    with open("build_a_pug/schema.sql", "r") as f:
        self.connection.executescript(f.read())

    query = "INSERT INTO pug (name, age, home, puppy_dinner, description, image) VALUES (?, ?, ?, ?, ?, ?)"

    for pug in test_pugs:
        self.connection.cursor().execute(query, (pug.name, pug.age, 
        pug.home, pug.puppy_dinner, pug.description, pug.image,),)

        self.connection.commit()

    print(Fore.GREEN + f"Test database: {TEST_DATABASE_FILEPATH} connection created and test data inserted")

# A method-level test fixture that
# closes and deletes the previously created sqlite database after each test in this class
def tearDown(self):
    """Close and delete the test database after  each test method in this class"""

    self.connection.close()
    os.remove(TEST_DATABASE_FILEPATH)

    print(Fore.RED + f"Test database: {TEST_DATABASE_FILEPATH} connection closed and deleted")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code uses method-level test fixtures to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a SQLite database connection, create a table in the database, create a couple of instances of the class Pug, and insert them into the database&lt;/li&gt;
&lt;li&gt;Close the database connection and delete the database&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;💡 There is a lot of SQLite/database boilerplate here you do not need to worry about – just focus on the &lt;code&gt;setUp&lt;/code&gt; and &lt;code&gt;tearDown&lt;/code&gt; methods and how they impact the tests.&lt;/p&gt;

&lt;p&gt;For your convenience, I have color-coded the printed output of the test fixture methods.&lt;/p&gt;

&lt;p&gt;Run the tests with: &lt;code&gt;pipenv run pug-unit-tests&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If everything went as planned, all the tests should pass and you should see the setup and teardown printed output for each test method run.&lt;/p&gt;

&lt;h4&gt;
  
  
  Run the tests with class-level test fixtures
&lt;/h4&gt;

&lt;p&gt;Now let’s see what happens when we use class level fixtures.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Comment out the whole &lt;code&gt;TestPugDBWithMethodLevelFixtures&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;Uncomment the class &lt;code&gt;TestPugDBWithClassLevelFixtures&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Run the tests: &lt;code&gt;pipenv run pug-unit-tests&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Did you get this test failure?: &lt;code&gt;AssertionError: 3 != 2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since the database was created and torn down before and after &lt;em&gt;all the test methods were executed&lt;/em&gt;, the pug we added in the &lt;code&gt;test_create_pug&lt;/code&gt; test is still in the database and therefore affects the results of the &lt;code&gt;test_get_grumble&lt;/code&gt; test.&lt;/p&gt;

&lt;p&gt;You probably also noticed that the green and red printed output appeared only once.&lt;/p&gt;

&lt;h4&gt;
  
  
  Run the tests with module-level test fixtures
&lt;/h4&gt;

&lt;p&gt;Bear with me because this one gets a little tricky.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Comment out the whole &lt;code&gt;TestPugDBWithClassLevelFixtures&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;Uncomment the class &lt;code&gt;TestPugDBWithModuleLevelFixtures&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Toward the top of the file, uncomment the functions &lt;code&gt;setUpModule&lt;/code&gt; and &lt;code&gt;tearDownModule&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run the tests: &lt;code&gt;pipenv run pug-unit-tests&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This time we get the same assertion error.&lt;/p&gt;

&lt;p&gt;Similarly, the green and red printed output appear only once, but instead of wrapping a particular test case class, they bookend the entire module.&lt;/p&gt;

&lt;h2&gt;
  
  
  In conclusion
&lt;/h2&gt;

&lt;p&gt;I hope that this tutorial demonstrated how test fixtures can help further refine your tests by enforcing best practices like isolating test cases, controlling variables, and improving performance.&lt;/p&gt;

&lt;p&gt;In this particular example, a test database allows us to leave our real database unharmed. While we &lt;em&gt;could&lt;/em&gt; set up this test database at the top of our test module, such a potentially draining resource may not be necessary for all of our tests &lt;em&gt;and&lt;/em&gt; it could affect test results in unintended ways. We also have the option of setting up our test database at the class level, but as we witnessed, this means the test methods within that test case are reliant on each other and no longer independent.&lt;/p&gt;

&lt;p&gt;As our tests are currently written, method-level fixtures seem to serve us best. However, this can change as our test needs evolve, forcing us to truly internalize what the code under test is really intended to do.&lt;/p&gt;

&lt;p&gt;Testing, like life, is full of challenges, but it’s also filled with opportunities for growth, clarity, and even a little fun. By embracing tools like test fixtures, we can reduce chaos and gain confidence in the code we write – something that feels especially meaningful during times when we might just need a little dopamine hit to stave off the imposter syndrome. Whether you’re debugging at midnight or just trying to make it through the day, remember that every small step forward counts.&lt;/p&gt;

&lt;p&gt;Have you been using test fixtures? How have they helped or hindered your tests?&lt;/p&gt;

&lt;p&gt;If you enjoyed this tutorial, please consider &lt;a href="https://utm.guru/uhSYX" rel="noopener noreferrer"&gt;buying me a coffee&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  More resources on test fixtures
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://utm.guru/uhSYO" rel="noopener noreferrer"&gt;Easy Python Unit Tests: Setup, Teardown, Fixtures, And More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://utm.guru/uhSYN" rel="noopener noreferrer"&gt;Python Test Fixtures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://utm.guru/uhSYM" rel="noopener noreferrer"&gt;Python unittest Fixtures&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>unittest</category>
      <category>python</category>
    </item>
    <item>
      <title>The DevRel Digest November 2024: If You Content It, They Will Come</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Mon, 02 Dec 2024 21:34:39 +0000</pubDate>
      <link>https://dev.to/lizzzzz/the-devrel-digest-november-2024-if-you-content-it-they-will-come-209j</link>
      <guid>https://dev.to/lizzzzz/the-devrel-digest-november-2024-if-you-content-it-they-will-come-209j</guid>
      <description>&lt;h2&gt;
  
  
  Content for a dream DevRel content program
&lt;/h2&gt;

&lt;p&gt;According to the 2024 State of DevRel report, “&lt;a href="https://utm.guru/uhLYx" rel="noopener noreferrer"&gt;content marketing remains the most effective tactic for outreach to new developers&lt;/a&gt;.” From marketing to education, content development remains&lt;a href="https://utm.guru/uhLYw" rel="noopener noreferrer"&gt; the top responsibility of DevRel practitioners&lt;/a&gt; from IC to C-level. In other words, you can’t have DevRel without content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uc48r081b3viw898n48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uc48r081b3viw898n48.png" alt="A chart illustrating top DevRel activities by role level that shows content development as a recurring activity from IC to manager to C-level" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;
Image via &lt;a href="https://utm.guru/uhLYw" rel="noopener noreferrer"&gt;The State of DevRel Report 2024&lt;/a&gt;

 

&lt;p&gt;&lt;strong&gt;So what would my dream DevRel content program look like?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At a high level, when it comes to Developer Relations, content is any information provided by a company or brand that is intended for developers. So this includes blog posts, tutorials, documentation, webinars, and conference talks. Content accompanies a developer at every step of their journey from product awareness and evaluation, education and implementation, and product champion.&lt;/p&gt;

&lt;p&gt;Whether it’s a blog post, a white paper, or a talk for a meetup, content for developers should follow the Triple A’s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://utm.guru/uhLYv" rel="noopener noreferrer"&gt;Authenticity&lt;/a&gt;:&lt;/strong&gt; Developer audiences are famously &lt;a href="https://utm.guru/uhLYu" rel="noopener noreferrer"&gt;allergic to bullshit&lt;/a&gt; – which is why they are my favorite group of people to create content for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accuracy:&lt;/strong&gt; Outdated docs and broken tutorials are not only huge turn-offs for developers, they make the work of developers harder than it needs to be.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“Aha!”:&lt;/strong&gt; There is no experience more powerful than the moment a developer fully internalizes the value of something. The more hands-on the &lt;a href="https://utm.guru/uhLYt" rel="noopener noreferrer"&gt;“Aha!” moment&lt;/a&gt; is, the better it is, generating a powerful neural connection between a solution or a concept and the triumphant dopamine hit of success – a connection that is especially important for developers. Content should always offer an opportunity for this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zkxj8m3ev5pl79pgmds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zkxj8m3ev5pl79pgmds.png" alt="An illustration of the Triple A's of content for developers" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the best ways to help determine if a piece of content is following the Triple A’s is to actually take the time to do your Ideal Customer Profile (ICP) homework. The more thorough and researched your ICPs are, the more precise your content can be, ensuring that content is not being produced in vain. Check out this &lt;a href="https://utm.guru/uhLYs" rel="noopener noreferrer"&gt;developer ICP framework&lt;/a&gt; from Tessa Kriesel to help you get the most out of the exercise.&lt;/p&gt;

&lt;p&gt;My dream DevRel content program can be divided into three primary categories that sync up with the developer on their hero’s journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Awareness and evaluation&lt;/li&gt;
&lt;li&gt;Education, onboarding, implementation&lt;/li&gt;
&lt;li&gt;Champion content
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The call to adventure: Content for awareness and evaluation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvlimvjgsafpak4qesv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvlimvjgsafpak4qesv9.png" alt="The hero’s journey of the developer and the role of developer relations." width="800" height="589"&gt;&lt;/a&gt;&lt;/p&gt;
The hero’s journey of the developer and the role of developer relations.



&lt;p&gt;This is the content that invites the developer to learn more by piquing their curiosity. This category of content should focus less on the product (henceforth referred to as the “solution”) and more on addressing specific use cases, problem spaces, and technologies – especially emergent ones. The point is to reach developers by empathizing with the particular obstacles they are facing and offering them assistance overcoming them.&lt;/p&gt;

&lt;p&gt;Content in this category should be more general and SEO-forward, answering the questions commonly asked by developers. Content in this category could include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Written content&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;SEO-forward blog posts that answer general questions related to your solution. For instance, if your solution is an open source vector database, a blog post topic could be the differences between nearest neighbor search (NNS), approximate nearest neighbor search (ANNS), and semantic search.&lt;/li&gt;
&lt;li&gt;Tutorials and recipes that focus on learning about a particular tech rather than the solution itself. For instance, “&lt;a href="https://utm.guru/uhLYr" rel="noopener noreferrer"&gt;How to build a movie recommendation app without the complexities of vector databases&lt;/a&gt;.”&lt;/li&gt;
&lt;li&gt;Introductory documentation that is not gated and emphasizes how easy it is to get started with the solution.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Video content&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;SEO-forward “&lt;a href="https://utm.guru/uhLYq" rel="noopener noreferrer"&gt;edutainment&lt;/a&gt;” that provides useful information in bite-sized videos that are fun to watch. The smart DevRel content producer will repurpose the aforementioned example blog post about search methods for vector databases into an edutainment script, breaking down each method into a 1-minute video.&lt;/li&gt;
&lt;li&gt;SEO-forward webinars that focus on solution related but still more general topics. For instance, a webinar that walks through the creation of a vector database – and just so happens to use the solution to do so!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Conference/in-person content&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Talks – especially shorter talk slots like lightning talks. And while your solution should not be in the title of the talk, you should definitely offer swag to those who attend!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Important metrics and outcomes of awareness and evaluation content&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Views and engagement with an emphasis on engagement (especially saving and/or sharing) and follow through on calls to action. (What did they click next and is it where you wanted them to go?)&lt;/li&gt;
&lt;li&gt;Lead generation segmented into first time users and established users (but don’t gate on work emails).&lt;/li&gt;
&lt;li&gt;Trial sign-ups … and for the love of all that’s good in the world, please provide some sort of self-service free or limited trial and if that’s not possible, please really consider if your solution is ready for a DevRel content program.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Descent into the unknown: Content for education, onboarding, and implementation
&lt;/h2&gt;

&lt;p&gt;Whether a solution is adopted by a company via developer recommendation or handed down by a CTO, education, onboarding, and implementation is where the hero’s journey into the unknown can become fraught. The content provided for developers at this stage should be supportive, accessible, applicable, and as frictionless as possible. While the decision to use the solution has already been made, the marketing isn’t over yet. This is an opportunity to win over lifelong solution champions.   &lt;/p&gt;

&lt;p&gt;Content in this category should be very specific, organized, and easily searchable, allowing developers to skip, review, or bookmark as needed. It should be streamlined, lightweight, and copy-pasteable. Content in this category could include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Written content&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;White papers that emphasize customer success stories, quantifiable outcomes, and implementation solutions that are as detailed as company intellectual property policies will allow.&lt;/li&gt;
&lt;li&gt;Documentation for advanced use cases, concepts, features, and troubleshooting.&lt;/li&gt;
&lt;li&gt;Onboarding guides and tutorials focusing on typical integration cases and specific tech components that provide architecture diagrams and sample code wherever possible – anything that makes it easier for developers to add the solution to their existing tech.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Video content&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Webinars that explain and demonstrate more advanced concepts and features and provide examples of where these concepts and features can be implemented.&lt;/li&gt;
&lt;li&gt;Longer, more detailed education on-demand videos that cover solution topics in-depth. The developers who gravitate toward this kind of content are good candidates for future champions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Conference/in-person content&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Workshops are an especially good format for more advanced use cases and features because attendees can troubleshoot directly with solution advocates.&lt;/li&gt;
&lt;li&gt;Co-located and/or unofficial meetups and office hours provide a venue for users of your solution to get facetime with you and each other. This can also be an extremely valuable opportunity to get honest solution feedback as well as identify potential champions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Important metrics and outcomes of awareness and evaluation content&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Views and engagement with even more emphasis on what is being shared and by whom.&lt;/li&gt;
&lt;li&gt;Documentary discovery flow to evaluate whether or not you have your information organized in a way that is intuitive and frictionless.&lt;/li&gt;
&lt;li&gt;Changes in frequently asked questions and issues can help determine if you need to address something better in your content or if your content is alleviating points of friction or if you need to reconsider something in the solution all together.&lt;/li&gt;
&lt;li&gt;Leads – with an emphasis on identifying who has shown up more than once.&lt;/li&gt;
&lt;li&gt;Community activity that indicates investment in the solution such as users responding to other users.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Returning with the boon: Content created by champions
&lt;/h2&gt;

&lt;p&gt;The best possible outcome of a DevRel content program is content created by users. By this time, the developer has heeded the call to adventure, descended into the unknown, and returned as a champion with a boon to share with other developers. A good DevRel content program inspires, enables, and empowers users to create their own content and share it – which is the most powerful testament to the power of your solution that you could ever ask for.&lt;/p&gt;

&lt;p&gt;A champions content program is a topic more suitable for another blog post, but I mention it here to close the loop for now. The point is that a successful DevRel content program becomes self-perpetuating and all content up to this point should help generate momentum in that direction. &lt;/p&gt;

&lt;h2&gt;
  
  
  From awareness to advocacy: The power of a dream DevRel content program
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodutwzfbtb7o2oehuntv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodutwzfbtb7o2oehuntv.png" alt="Gary poorly Photoshopped into The Moon tarot card" width="800" height="1408"&gt;&lt;/a&gt;&lt;/p&gt;
In &lt;a href="https://utm.guru/uhMjh" rel="noopener noreferrer"&gt;The Moon&lt;/a&gt; tarot card featuring senior pug Gary as the dog, the wolf, and the crayfish. This card is less about content and more about my own fears in my current job search 😅, but I guess it could also illustrate the part of the developer's hero's journey when they feel most uncertain (and how this is a great place for content to come to the rescue).

 

&lt;p&gt;A dream DevRel content program doesn’t just inform – it transforms. By aligning content with the developer’s journey, adhering to the Triple A’s of authenticity, accuracy, and "Aha!" moments, and maintaining an unwavering focus on the needs of your ICPs, you create more than just engagement. You foster trust, empowerment, and advocacy. Every piece of content should serve as a bridge connecting developers to solutions, demystifying complexity, and igniting inspiration. When done right, a DevRel content program transcends marketing – it becomes a catalyst for community, collaboration, and shared success, turning developers into champions who carry your story forward.&lt;/p&gt;

&lt;p&gt;(And you should &lt;a href="https://utm.guru/uhMhU" rel="noopener noreferrer"&gt;hire me&lt;/a&gt; to run it! )&lt;/p&gt;

&lt;h2&gt;
  
  
  Events and resources and other notable things
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Developer Marketing Alliance Developer Relations Summit is tomorrow, December 3rd, 2024. With speakers such as Wesley Faulkner and Katie Wasilenko Miller among its speakers, I’m looking forward to the insights, inspiration, and community. Register &lt;a href="https://utm.guru/uhMhI" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I’m excited because the &lt;a href="https://utm.guru/uhLYp" rel="noopener noreferrer"&gt;DevRel Foundations working groups&lt;/a&gt; are getting underway and yes, I am volunteering to manage the group on metrics, reporting, and best practices cause you know I love some process and data!&lt;/li&gt;
&lt;li&gt;From the CNCF to career transitions, here’s an &lt;a href="https://utm.guru/uhLYo" rel="noopener noreferrer"&gt;open source list of tech professionals&lt;/a&gt; who are down to grab a virtual coffee with you and chat – I should add my name to that list!&lt;/li&gt;
&lt;li&gt;What they never tell you about leaving an abusive relationship is that it’s &lt;em&gt;expensive&lt;/em&gt;. If you can, please consider donating to &lt;a href="https://utm.guru/uhLYn" rel="noopener noreferrer"&gt;help my friend Emily start a brand new life&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devrel</category>
    </item>
    <item>
      <title>If this resonates with you, then you might like working with me</title>
      <dc:creator>Liz Acosta</dc:creator>
      <pubDate>Sun, 01 Dec 2024 03:17:44 +0000</pubDate>
      <link>https://dev.to/lizzzzz/if-this-resonates-with-you-then-you-might-like-working-with-me-25bj</link>
      <guid>https://dev.to/lizzzzz/if-this-resonates-with-you-then-you-might-like-working-with-me-25bj</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftotdr8co3c6zko2lnn18.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftotdr8co3c6zko2lnn18.jpg" alt="Photo of Liz dressed up as Wednesday Addams with senior pug Gary in her lap dressed up as Puglsey -- of course." width="800" height="921"&gt;&lt;/a&gt;&lt;/p&gt;
 Me and Gary dressed up as Wednesday and Pugsley Addams for Halloween -- get it?!



&lt;h2&gt;
  
  
  Let's try this again
&lt;/h2&gt;

&lt;p&gt;Back in October 2023, I wrote &lt;a href="https://utm.guru/uhLYg" rel="noopener noreferrer"&gt;this article on LinkedIn&lt;/a&gt; hoping that an unconventional approach to job searching would get me an offer.&lt;/p&gt;

&lt;p&gt;And it worked! Sort of!&lt;/p&gt;

&lt;p&gt;While my gig with Snowflake didn't end up working out, it &lt;em&gt;did&lt;/em&gt; help me identify my professional strengths and needs. So here's an iteration on the original post:&lt;/p&gt;

&lt;h2&gt;
  
  
  I can't keep pretending
&lt;/h2&gt;

&lt;p&gt;It is a bummer spending &lt;em&gt;yet another&lt;/em&gt; holiday season unemployed. I wish it wasn’t so hard. I’ve tried medication, coaching, and years of therapy, but my most recent role at Snowflake finally forced me to confront some truths about myself: The parts of myself who struggle to conform are also the parts who make me good at what I do well.&lt;/p&gt;

&lt;p&gt;My ability to see the bigger picture, adapt to new plans, efficiently internalize new information, empathize with developers, and produce high quality content is a side effect of a confluence of neurodivergence and lifelong mental health conditions. My innovative approach to problem-solving is a result of seeing things differently, and because I see things differently, I have different needs.&lt;/p&gt;

&lt;p&gt;I have spent my life trying to be the person I think other people want me to be; as a result, I’ve ended up in some very misaligned situations. What happens if I stop trying to pretend?&lt;/p&gt;

&lt;p&gt;So here I am, no longer trying to pretend because real talk: your girl needs a job! &lt;/p&gt;

&lt;h2&gt;
  
  
  You receive: One unconventional but cool teammate, I receive: One job
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiv3z70tqpfozc2lut0dn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiv3z70tqpfozc2lut0dn.jpg" alt="The Trade Offer meme where I receive one job and you receive one cool, unconventional teammate" width="500" height="654"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might like working with me if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You need someone who can quickly synthesize the big picture and figure out how to achieve it by breaking it down into explicit, atomic tasks.&lt;/strong&gt; As an autistic person, I excel at identifying patterns and novel connections – especially patterns and connections in data and especially-&lt;em&gt;especially&lt;/em&gt; data related to human behavior. This has been very useful in identifying and creating content that resonates deeply with readers. Once I have the big picture in mind, my natural inclination for &lt;a href="https://utm.guru/uhLX7" rel="noopener noreferrer"&gt;“bottom-up thinking”&lt;/a&gt; means I am meticulously detail-oriented – a skill critical to breaking down objectives into actionable items.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You are process and data driven – or need someone to develop and establish process and data.&lt;/strong&gt; I have ADHD and when it’s &lt;em&gt;this easy&lt;/em&gt; to get distracted, a documented process helps keep me on track. While my co-workers may not need process for the same reasons I do, process can help establish a reproducible structure or framework that actually makes pivoting easier as priorities shift or data reveals new findings. Process and data enable more accurate goal-setting and facilitate better work evaluations. And just like code, process is best when given room to iterate upon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need someone who can write code and write words.&lt;/strong&gt; I like doing both and I relish any opportunity I get to context switch rapidly between the two. &lt;a href="https://utm.guru/uhLX6" rel="noopener noreferrer"&gt;Tutorials&lt;/a&gt; and &lt;a href="https://utm.guru/uhLX5" rel="noopener noreferrer"&gt;recipes&lt;/a&gt; are my favorite types of content to produce. My technical writing skills are extensible and especially useful when &lt;a href="https://utm.guru/uhLX4" rel="noopener noreferrer"&gt;working alongside engineers&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need someone who is not only comfortable in front of an audience, but enjoys it.&lt;/strong&gt; The precise and focused mindfulness that interacting with a live audience requires forces me to be completely present mentally, physically, and even spiritually. It breaks me out of the state of survival dissociation I live in. It’s a dopamine high I would compare to snowboarding, and I can’t get &lt;em&gt;enough&lt;/em&gt; dopamine – literally! &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need someone who can inspire a team and be honest with you.&lt;/strong&gt; As a creative person with a film and arts degree who was very active in the Los Angeles arts and LGBTQ+ communities, one of my values is creating a space around me that feels safe and nonjudgmental. I’ve participated in and facilitated support groups and know how to meet people where they are at. If you want me to tell you the truth about something, you can trust me to be completely honest. To that point, I believe that honesty without kindness is just bullying, and there’s already enough crap people have to deal with out in the world – I don’t want to be another source of it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like doing a good job. I value high quality, considered, and intentional output and I do that by focusing on the details. I want to do a good job and would love to find a situation where I can truly thrive.&lt;/p&gt;

&lt;h2&gt;
  
  
  From surviving to thriving
&lt;/h2&gt;

&lt;p&gt;I work best when I have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fully remote or hybrid up to two days a week with a strong preference for offices in San Francisco.&lt;/strong&gt; Offices are a minefield for my focus and executive function. Because I hold myself to such high standards, I feel distressed when I feel my productivity slow down. &lt;a href="https://utm.guru/uhLX3" rel="noopener noreferrer"&gt;When I am allowed to be a weird little goblin in private&lt;/a&gt;, I get &lt;em&gt;so much work done!&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An emphasis on work-life balance and boundaries.&lt;/strong&gt; I often do my best work when I am not at work. My brain never turns off, so even when I am relaxing I am passively problem-solving. &lt;a href="https://utm.guru/uhLX2" rel="noopener noreferrer"&gt;I need to take breaks so I can nurture my hobbies and replenish my internal resources&lt;/a&gt;. Moreover, as a deeply creative endeavor, successful DevRel requires rest! I think Marisa Smith, PhD says it best &lt;a href="https://utm.guru/uhLX1" rel="noopener noreferrer"&gt;here&lt;/a&gt;:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff01rfaeykjsnp1ood506.png" alt="A LinkedIn post from Marisa Smith, PhD that identifies the following 3 strategies for maintaining a creative spark when doing DevRel: Collaboration, experimentation, and taking breaks" width="800" height="900"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opportunities to write code, to write words, and to lead projects.&lt;/strong&gt; These are my favorite things to do!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust, autonomy, and appreciation.&lt;/strong&gt; I take a lot of pride in my work and though my methods may occasionally seem unconventional, anything that I put my name on has to meet my high standards. When I am fully resourced and I feel confident and focused, I am extremely self-motivated -- I mean, just look at how I've managed to keep up a consistent flow of my own content all on my own! I trust my teammates to be the best at what they were hired for and I always acknowledge the efforts of others. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  All I want for XMas is a job 🎵
&lt;/h2&gt;

&lt;p&gt;Here's hoping for a holiday season miracle! If you've got any leads or if this post resonated with you in anyway, you can contact me by leaving a comment here, via &lt;a href="https://utm.guru/uhLYc" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or using the &lt;a href="https://utm.guru/uhLYb" rel="noopener noreferrer"&gt;contact form&lt;/a&gt; on my website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ylyu9x972munh4ccfw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ylyu9x972munh4ccfw.gif" alt="A gif of Mariah Carey from the " width="480" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>devrel</category>
    </item>
  </channel>
</rss>
